Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/api/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -4073,6 +4073,14 @@ Epoch ms the ledger recorded this settlement — the resolution a progress-based

> `readonly` **perWorker**: [`Budget`](index.md#budget-4)

##### deliverable?

> `readonly` `optional` **deliverable?**: [`DeliverableSpec`](runtime.md#deliverablespec)\<`unknown`\>

The same independent completion check used for workers. When present, the driver receives a
`submit_result` tool and may finish work itself instead of being forced to delegate it. The
first passing submission is retained; a false or throwing check fails closed.

##### analysts?

> `readonly` `optional` **analysts?**: [`AnalystRegistry`](index.md#analystregistry)
Expand Down Expand Up @@ -4215,6 +4223,16 @@ choice, steerable counterpart to the one-shot own-sandbox delegation MCP.

`string` \| `undefined`

##### submittedResult()

> **submittedResult**(): \{ `result`: `unknown`; \} \| `undefined`

The first result whose injected independent check passed, if the driver submitted one.

###### Returns

\{ `result`: `unknown`; \} \| `undefined`

##### settled()

> **settled**(): readonly [`SettledWorker`](#settledworker)[]
Expand Down
37 changes: 34 additions & 3 deletions docs/api/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -9205,6 +9205,13 @@ Resolve a spawned `profile` to a worker LEAF or a driver child (the recursion se

Per-child budget reserved from the conserved pool on each spawn.

##### deliverable?

> `readonly` `optional` **deliverable?**: [`DeliverableSpec`](#deliverablespec)\<`unknown`\>

Independent completion check for work the driver performs itself. When present, the driver
receives `submit_result`; the first passing submission ends the loop and becomes the output.

##### maxLiveWorkers?

> `readonly` `optional` **maxLiveWorkers?**: `number`
Expand Down Expand Up @@ -9461,6 +9468,18 @@ The URL an in-box harness mounts as `mcp.mcpServers.coordination.url`.

> `readonly` **port**: `number`

##### submittedResult

> **submittedResult**: () => \{ `result`: `unknown`; \} \| `undefined`

The first driver-authored result whose injected independent check passed.

The first result whose injected independent check passed, if the driver submitted one.

###### Returns

\{ `result`: `unknown`; \} \| `undefined`

##### drainResolved

> **drainResolved**: () => `Promise`\<`number`\>
Expand Down Expand Up @@ -11707,9 +11726,9 @@ WHERE workers run — derives the worker seam. Provide this OR an explicit `make

> `readonly` `optional` **deliverable?**: [`DeliverableSpec`](#deliverablespec)\<`unknown`\>

The completion oracle for backend-derived workers (settled ⟺ delivered). Strongly recommended:
without it the supervisor trusts a worker's self-report — exactly the "ran but didn't deliver"
failure mode of a static orchestrator.
The independent completion check for backend-derived workers and direct supervisor
submissions. Strongly recommended: without it the supervisor cannot submit its own work and
backend-derived workers fall back to their own validity signal.

##### makeWorkerAgent?

Expand Down Expand Up @@ -11985,6 +12004,12 @@ Resolve a spawned worker `profile` to a leaf agent — the recursion seam (same

Per-child budget reserved from the conserved pool on each spawn.

##### deliverable?

> `readonly` `optional` **deliverable?**: [`DeliverableSpec`](#deliverablespec)\<`unknown`\>

Independent completion check for direct driver work (`submit_result`).

##### maxLiveWorkers?

> `readonly` `optional` **maxLiveWorkers?**: `number`
Expand Down Expand Up @@ -19969,6 +19994,12 @@ Stand up the coordination MCP over a live scope. The HOST address is `127.0.0.1`

[`Budget`](index.md#budget-4)

###### deliverable?

[`DeliverableSpec`](#deliverablespec)\<`unknown`\>

Independent completion check exposed to the driver as `submit_result`.

###### maxLiveWorkers?

`number`
Expand Down
73 changes: 73 additions & 0 deletions src/mcp/tools/coordination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
Settled,
Agent as SuperviseAgent,
} from '../../runtime'
import type { DeliverableSpec } from '../../runtime/supervise/completion-gate'
import { type WatchTraceOptions, watchTrace } from '../../runtime/supervise/detector-monitor'
import { freeSlots } from '../../runtime/supervise/dispatch'
import { type BusRecord, type BusStats, createEventBus } from '../../runtime/supervise/event-bus'
Expand Down Expand Up @@ -104,6 +105,12 @@ export interface CoordinationToolsOptions {
readonly blobs: ResultBlobStore
readonly makeWorkerAgent: MakeWorkerAgent
readonly perWorker: Budget
/**
* The same independent completion check used for workers. When present, the driver receives a
* `submit_result` tool and may finish work itself instead of being forced to delegate it. The
* first passing submission is retained; a false or throwing check fails closed.
*/
readonly deliverable?: DeliverableSpec<unknown>
readonly analysts?: AnalystRegistry
readonly onEvent?: (event: CoordinationEvent) => void | Promise<void>
readonly questionPolicy?: QuestionPolicy
Expand Down Expand Up @@ -181,6 +188,8 @@ export interface CoordinationTools {
readonly tools: McpToolDescriptor[]
isStopped(): boolean
stopReason(): string | undefined
/** The first result whose injected independent check passed, if the driver submitted one. */
submittedResult(): { readonly result: unknown } | undefined
settled(): ReadonlyArray<SettledWorker>
questions(): ReadonlyArray<QuestionRecord>
/** The full ordered log of every bus event — UP (settled / question / finding) and DOWN
Expand Down Expand Up @@ -216,6 +225,7 @@ export const coordinationVerbNames = [
'list_questions',
'answer_question',
'ask_parent',
'submit_result',
'stop',
'list_analysts',
'run_analyst',
Expand All @@ -225,8 +235,10 @@ const idArg = { type: 'string', description: 'The workerId returned by spawn_age

/** Build the driver's MCP tools over a live scope. */
export function createCoordinationTools(opts: CoordinationToolsOptions): CoordinationTools {
const deliverable = opts.deliverable
let stopped = false
let reason: string | undefined
let submitted: { readonly result: unknown } | undefined
let questionSeq = 0
const ledger: SettledWorker[] = []
const questions: QuestionRecord[] = [...(opts.priorQuestions ?? [])]
Expand Down Expand Up @@ -984,6 +996,66 @@ export function createCoordinationTools(opts: CoordinationToolsOptions): Coordin
return { question: q }
},
},
...(deliverable
? [
{
name: 'submit_result',
description: [
'Submit the complete result to the injected independent check.',
'The first passing result is retained; stop work when accepted.',
...(deliverable.describe ? [`Expected result: ${deliverable.describe}`] : []),
].join(' '),
inputSchema: {
type: 'object',
properties: {
result: {
description: 'The complete result in the form requested by the task.',
},
},
required: ['result'],
additionalProperties: false,
},
handler: async (raw: unknown) => {
if (submitted) {
return {
accepted: true,
retained: 'earlier-passing-result',
stop: true,
}
}
const a = obj(raw)
if (!Object.hasOwn(a, 'result')) {
throw new Error('submit_result: "result" is required')
}

// Copy once at intake so the value checked below is the exact value retained after
// acceptance, even when this handler is called directly rather than through JSON-RPC.
const result = structuredClone(a.result)
let accepted = false
try {
accepted = (await deliverable.check(result)) === true
} catch {
accepted = false
}
if (!accepted) return { accepted: false, stop: false }
// Two remote callers may submit concurrently. Whichever passing check completes
// first owns the retained result; a later completion must never overwrite it.
if (submitted) {
return {
accepted: true,
retained: 'earlier-passing-result',
stop: true,
}
}

submitted = Object.freeze({ result })
stopped = true
reason = 'result-accepted'
return { accepted: true, retained: 'this-result', stop: true }
},
} satisfies McpToolDescriptor,
]
: []),
{
name: 'stop',
description: 'Declare the run complete.',
Expand Down Expand Up @@ -1046,6 +1118,7 @@ export function createCoordinationTools(opts: CoordinationToolsOptions): Coordin
stats: () => bus.stats(),
isStopped: () => stopped,
stopReason: () => reason,
submittedResult: () => submitted,
settled: () => ledger,
questions: () => questions,
drainResolved,
Expand Down
13 changes: 10 additions & 3 deletions src/runtime/supervise/coordination-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
type ToolLoopCompaction,
type ToolLoopCompactionOptions,
} from '../tool-loop'
import type { DeliverableSpec } from './completion-gate'
import type { PriorCoordination } from './coordination-log'
import {
bestDelivered,
Expand Down Expand Up @@ -81,6 +82,9 @@ export interface DriverAgentOptions {
readonly makeWorkerAgent: MakeWorkerAgent
/** Per-child budget reserved from the conserved pool on each spawn. */
readonly perWorker: Budget
/** Independent completion check for work the driver performs itself. When present, the driver
* receives `submit_result`; the first passing submission ends the loop and becomes the output. */
readonly deliverable?: DeliverableSpec<unknown>
/** Hard cap on simultaneously-LIVE workers — `spawn_agent` fails closed once this many are in
* flight (a concurrency fence on top of the conserved-pool fence). Omit/`<= 0` = no cap. */
readonly maxLiveWorkers?: number
Expand Down Expand Up @@ -307,6 +311,7 @@ export function driverAgent(opts: DriverAgentOptions): Agent<unknown, unknown> {
blobs: opts.blobs,
makeWorkerAgent: opts.makeWorkerAgent,
perWorker: opts.perWorker,
...(opts.deliverable ? { deliverable: opts.deliverable } : {}),
...(opts.maxLiveWorkers !== undefined ? { maxLiveWorkers: opts.maxLiveWorkers } : {}),
...(opts.analysts ? { analysts: opts.analysts } : {}),
...(opts.analyzeOnSettle ? { analyzeOnSettle: opts.analyzeOnSettle } : {}),
Expand Down Expand Up @@ -484,9 +489,11 @@ export function driverAgent(opts: DriverAgentOptions): Agent<unknown, unknown> {
// never be lost to the driver's pull discipline (e.g. a brain that spawned and stopped
// without awaiting). Non-blocking: live children are the supervisor's to tear down.
await coord.drainResolved()
// The driver's deliverable comes from the finalizer seam over DELIVERED children only
// (the completion-oracle), never its own prose — a driver cannot self-declare done
// (Foreman 0/18). Default keep-best; nothing delivered → undefined.
// Direct work is eligible only through `submit_result`, after the same injected independent
// check workers face. Raw driver prose remains ineligible. The first passing submission wins;
// otherwise finalize over delivered children as before.
const submitted = coord.submittedResult()
if (submitted) return submitted.result
return runFinalizer(opts.finalizer ?? bestDelivered, {
settled: coord.settled(),
blobs: opts.blobs,
Expand Down
7 changes: 7 additions & 0 deletions src/runtime/supervise/coordination-mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
type SettledWorker,
type WorkerWatchOptions,
} from '../../mcp/tools/coordination'
import type { DeliverableSpec } from './completion-gate'
import type { Budget, ResultBlobStore, Scope } from './types'

export interface CoordinationMcpHandle {
Expand All @@ -41,6 +42,8 @@ export interface CoordinationMcpHandle {
readonly port: number
/** The coordination tools' settled-worker ledger (for the driver's finalize). */
settled(): ReadonlyArray<SettledWorker>
/** The first driver-authored result whose injected independent check passed. */
submittedResult: CoordinationTools['submittedResult']
/** Post-loop drain of already-settled, unpulled children into the ledger — call before reading
* `settled()` for a finalize, so a delivered child the harness never awaited is not lost. */
drainResolved: CoordinationTools['drainResolved']
Expand All @@ -61,6 +64,8 @@ export async function serveCoordinationMcp(opts: {
blobs: ResultBlobStore
makeWorkerAgent: MakeWorkerAgent
perWorker: Budget
/** Independent completion check exposed to the driver as `submit_result`. */
deliverable?: DeliverableSpec<unknown>
/** Hard cap on simultaneously-LIVE workers — `spawn_agent` fails closed once this many are in
* flight (a concurrency fence on top of the conserved-pool fence). Omit/`<= 0` = no cap. */
maxLiveWorkers?: number
Expand Down Expand Up @@ -89,6 +94,7 @@ export async function serveCoordinationMcp(opts: {
blobs: opts.blobs,
makeWorkerAgent: opts.makeWorkerAgent,
perWorker: opts.perWorker,
...(opts.deliverable ? { deliverable: opts.deliverable } : {}),
...(opts.maxLiveWorkers !== undefined ? { maxLiveWorkers: opts.maxLiveWorkers } : {}),
awaitTimeoutMs: opts.awaitTimeoutMs ?? DEFAULT_AWAIT_EVENT_TIMEOUT_MS,
...(opts.analysts ? { analysts: opts.analysts } : {}),
Expand Down Expand Up @@ -150,6 +156,7 @@ export async function serveCoordinationMcp(opts: {
url: `http://${host}:${port}/mcp`,
port,
settled: () => coord.settled(),
submittedResult: () => coord.submittedResult(),
drainResolved: () => coord.drainResolved(),
isStopped: () => coord.isStopped(),
history: () => coord.history(),
Expand Down
7 changes: 4 additions & 3 deletions src/runtime/supervise/supervise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ export interface SuperviseOptions {
readonly budget: Budget
/** WHERE workers run — derives the worker seam. Provide this OR an explicit `makeWorkerAgent`. */
readonly backend?: ExecutorConfig
/** The completion oracle for backend-derived workers (settled ⟺ delivered). Strongly recommended:
* without it the supervisor trusts a worker's self-report — exactly the "ran but didn't deliver"
* failure mode of a static orchestrator. */
/** The independent completion check for backend-derived workers and direct supervisor
* submissions. Strongly recommended: without it the supervisor cannot submit its own work and
* backend-derived workers fall back to their own validity signal. */
readonly deliverable?: DeliverableSpec<unknown>
/** Override the worker seam directly (tests / advanced) instead of deriving it from `backend`. */
readonly makeWorkerAgent?: MakeWorkerAgent
Expand Down Expand Up @@ -235,6 +235,7 @@ export function supervise(profile: SupervisorProfile, task: unknown, opts: Super
blobs,
makeWorkerAgent: workerFactory,
perWorker,
...(opts.deliverable ? { deliverable: opts.deliverable } : {}),
...(log ? { onEvent: (ev) => log.append(runId, ev, new Date(now()).toISOString()) } : {}),
...(priorCoordination &&
(priorCoordination.questions.length > 0 || priorCoordination.findings.length > 0)
Expand Down
Loading