@alvin0/ai-agent-sdk-core 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +6 -0
  2. package/dist/agent/define/session/config.js +1 -1
  3. package/dist/agent/define/session/config.js.map +1 -1
  4. package/dist/agent/define/session/types.d.ts +14 -0
  5. package/dist/agent/define/session/types.d.ts.map +1 -1
  6. package/dist/agent/define/session.d.ts.map +1 -1
  7. package/dist/agent/define/session.js +1 -1
  8. package/dist/agent/define/session.js.map +1 -1
  9. package/dist/agent/loop/events.d.ts +24 -1
  10. package/dist/agent/loop/events.d.ts.map +1 -1
  11. package/dist/agent/loop/run-turn.d.ts.map +1 -1
  12. package/dist/agent/loop/run-turn.js +1 -1
  13. package/dist/agent/loop/run-turn.js.map +1 -1
  14. package/dist/agent/loop/schedule.d.ts +5 -0
  15. package/dist/agent/loop/schedule.d.ts.map +1 -1
  16. package/dist/agent/loop/schedule.js +1 -1
  17. package/dist/agent/loop/schedule.js.map +1 -1
  18. package/dist/agent/loop/turn/config.js +1 -1
  19. package/dist/agent/loop/turn/config.js.map +1 -1
  20. package/dist/agent/loop/turn/model-request-boundary.js +1 -1
  21. package/dist/agent/loop/turn/model-request-boundary.js.map +1 -1
  22. package/dist/agent/loop/turn/model-round.js +1 -1
  23. package/dist/agent/loop/turn/model-round.js.map +1 -1
  24. package/dist/agent/loop/turn/types.d.ts +35 -2
  25. package/dist/agent/loop/turn/types.d.ts.map +1 -1
  26. package/dist/agent/loop/types.d.ts +16 -0
  27. package/dist/agent/loop/types.d.ts.map +1 -1
  28. package/dist/agent/mode/run-agent.d.ts +9 -0
  29. package/dist/agent/mode/run-agent.d.ts.map +1 -1
  30. package/dist/agent/mode/run-agent.js +2 -2
  31. package/dist/agent/mode/run-agent.js.map +1 -1
  32. package/dist/agent/mode/user-input.d.ts.map +1 -1
  33. package/dist/agent/mode/user-input.js +1 -1
  34. package/dist/agent/mode/user-input.js.map +1 -1
  35. package/dist/agent/team/managed.d.ts.map +1 -1
  36. package/dist/agent/team/managed.js +1 -1
  37. package/dist/agent/team/managed.js.map +1 -1
  38. package/dist/agent/tool/capture.js +1 -1
  39. package/dist/agent/tool/capture.js.map +1 -1
  40. package/dist/agent/tool/definition.d.ts +7 -0
  41. package/dist/agent/tool/definition.d.ts.map +1 -1
  42. package/dist/agent/tool/definition.js.map +1 -1
  43. package/dist/agent.js +1 -1
  44. package/dist/composition/agent/public-event.d.ts +19 -0
  45. package/dist/composition/agent/public-event.d.ts.map +1 -0
  46. package/dist/composition/agent/public-event.js +2 -0
  47. package/dist/composition/agent/public-event.js.map +1 -0
  48. package/dist/composition/agent/session.js +1 -1
  49. package/dist/composition/agent/session.js.map +1 -1
  50. package/dist/composition/agent/types.d.ts +11 -1
  51. package/dist/composition/agent/types.d.ts.map +1 -1
  52. package/dist/composition/embedding/retry.js +1 -1
  53. package/dist/composition/embedding/retry.js.map +1 -1
  54. package/dist/embedding/errors.d.ts +5 -1
  55. package/dist/embedding/errors.d.ts.map +1 -1
  56. package/dist/embedding/errors.js +1 -1
  57. package/dist/embedding/errors.js.map +1 -1
  58. package/dist/index.d.ts +2 -1
  59. package/dist/index.js +1 -1
  60. package/dist/observability/bus.js +1 -1
  61. package/dist/primitives/version.d.ts +1 -1
  62. package/dist/primitives/version.js +1 -1
  63. package/dist/primitives/version.js.map +1 -1
  64. package/dist/runtime/registry.js +1 -1
  65. package/dist/runtime/with-retry.d.ts +15 -0
  66. package/dist/runtime/with-retry.d.ts.map +1 -1
  67. package/dist/runtime/with-retry.js +1 -1
  68. package/dist/runtime/with-retry.js.map +1 -1
  69. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"schedule.js","names":[],"sources":["../../../src/agent/loop/schedule.ts"],"sourcesContent":["import { createToolResultMessage, createUserMessage } from '../../message/index.ts'\nimport type { ContentBlock } from '../../message/index.ts'\nimport { detachedFrozen } from '../../primitives/index.ts'\nimport type { JsonObject } from '../../primitives/index.ts'\nimport { waitForSettlement } from '../../async/index.ts'\nimport type { History } from '../history/history.ts'\nimport type { ApprovalBroker } from '../tool/approval.ts'\nimport type { ToolCallPosition, ToolExecutionResult } from '../tool/definition.ts'\nimport {\n estimateTextBlockTokens, previewForSpill, truncateMiddleToTokens,\n type SpillStore, type ToolOutputOverflowPolicy,\n} from '../tool/output-budget.ts'\nimport { TOOL_ERROR_CODES, ToolError, toolErrorDisposition } from '../tool/errors.ts'\nimport {\n authorizeToolCall, dispatchAuthorizedToolCall, finalizeToolCall, prepareToolCall, toolFailure,\n type AuthorizedToolCall, type ToolCallRequest, type ToolInterceptor,\n} from '../tool/pipeline.ts'\nimport type { ToolCatalog } from '../tool/registry.ts'\nimport { createSpanId, type TraceRef } from '../trace/trace.ts'\nimport type { AgentEvent, ToolDeclineReason, TurnHooks } from './events.ts'\nimport type { RunAccountingPort } from '../accounting/contracts.ts'\nimport type { SdkLogger } from '../../logging/types.ts'\nimport { createToolAdmission, type AdmissionTicket } from './admission.ts'\nimport { ProgramRun, validPrograms, type StepRuntime } from './program.ts'\nimport {\n emitEvent, errorCode, immutableResult, messageOf, now, raceWithSignal, teardownFailure, withApprovalEvents,\n} from './tool-call-support.ts'\nimport { attachNestedToolPort, NESTED_TOOL_ERROR_CODES, type ProgramGrant } from '../tool/nested.ts'\nimport type { ProgramResultStore } from '../tool/program-results.ts'\n\nexport interface RunToolCallsOptions {\n readonly calls: readonly ToolCallRequest[]\n readonly catalog: ToolCatalog\n readonly history: History\n readonly position: ToolCallPosition\n readonly signal: AbortSignal\n readonly logger?: SdkLogger\n readonly parentTrace: TraceRef\n readonly maxParallel?: number\n /**\n * How many BUDGETED calls may still be dispatched this step.\n *\n * Calls to a `budgetExempt` tool are not counted against it, so a spent\n * budget never blocks the model from submitting, asking, or delegating.\n */\n readonly dispatchLimit?: number\n /** Why calls past `dispatchLimit` are not run; shown to the model verbatim. */\n readonly declineReason?: ToolDeclineReason\n /** Estimated tokens of text one result may put in front of the model. */\n readonly maxResultTokens?: number\n /** What happens to output over that budget. Defaults to `auto`. */\n readonly resultOverflow?: ToolOutputOverflowPolicy\n /** Where spilled output goes; `auto` spills only when this is mounted. */\n readonly spillStore?: SpillStore\n /** Maximum serialized bytes retained for one finalized result. Defaults to 4 MiB. */\n readonly maxResultBytes?: number\n /** End-to-end wall-clock allowance for one call. Defaults to 10 minutes. */\n readonly maxDurationMs?: number\n /** Maximum cancellation settlement wait. Defaults to 30 seconds. */\n readonly teardownTimeoutMs?: number\n readonly interceptors?: readonly ToolInterceptor[]\n readonly approvals?: ApprovalBroker\n readonly emit?: (event: AgentEvent) => Promise<void>\n readonly checkpoint?: TurnHooks['checkpoint']\n readonly accounting?: RunAccountingPort\n}\nexport interface ToolCallsOutcome {\n readonly results: readonly ToolExecutionResult[]\n readonly concluded: boolean\n readonly concludedBy?: string\n readonly dispatched: number\n /** Dispatched calls that spent budget; exempt tools are excluded. */\n readonly budgeted: number\n /** Calls the loop refused to run, for the reason in `declineReason`. */\n readonly declined: number\n}\n\ninterface Slot {\n readonly call: ToolCallRequest\n readonly trace: TraceRef\n readonly authorized?: AuthorizedToolCall\n readonly pending: Promise<ToolExecutionResult>\n readonly dispatched: boolean\n /** The program this call hosts, when the host granted it one. */\n readonly program?: ProgramRun\n /** Set when the loop refused to run the call. */\n readonly declined?: boolean\n readonly signal: AbortSignal\n readonly deadline: AbortSignal\n readonly teardownTimeoutMs: number\n}\n\n/** Scheduler options the loop owns and does not publish. */\nexport interface InternalScheduleOptions {\n /** Overrides `dispatchLimit`; `unbounded` when the budget is a notice rather than a wall. */\n readonly admissionLimit?: number | 'unbounded'\n /**\n * Program tools and what each may call. Research seam for SP-01; the public\n * way to enable programs is not decided.\n */\n readonly programs?: ReadonlyMap<string, ProgramGrant>\n /** Where programs retain values; owned and closed by the caller. */\n readonly programResults?: ProgramResultStore\n}\n\n/** Execute safe siblings concurrently while committing every result in model order. */\nexport async function runToolCalls(options: RunToolCallsOptions): Promise<ToolCallsOutcome> {\n return await scheduleToolCalls(options)\n}\n\n/** {@link runToolCalls} with the loop's internal options. */\nexport async function scheduleToolCalls(\n options: RunToolCallsOptions,\n internal: InternalScheduleOptions = {},\n): Promise<ToolCallsOutcome> {\n const maxParallel = positiveInteger(options.maxParallel ?? 8, 'maxParallel')\n const maxResultBytes = positiveInteger(options.maxResultBytes ?? 4 * 1024 * 1024, 'maxResultBytes')\n const maxDurationMs = positiveInteger(options.maxDurationMs ?? 10 * 60_000, 'maxDurationMs')\n const teardownTimeoutMs = positiveInteger(options.teardownTimeoutMs ?? 30_000, 'teardownTimeoutMs')\n const calls = Object.freeze(options.calls.map(immutableCall))\n const admission = createToolAdmission(internal.admissionLimit ?? Math.max(0, options.dispatchLimit ?? calls.length))\n const step: StepRuntime = {\n admission, maxDurationMs, teardownTimeoutMs, maxResultBytes,\n programs: validPrograms(internal.programs),\n programResults: internal.programResults,\n }\n const results: ToolExecutionResult[] = []\n let dispatched = 0\n let declined = 0\n let concludedBy: string | undefined\n let index = 0\n let carried: ReturnType<typeof prepareToolCall> | undefined\n\n while (index < calls.length) {\n const first = calls[index]\n if (first === undefined) break\n const prepared = carried ?? prepare(options, first)\n carried = undefined\n if (prepared.mode === 'exclusive') {\n const slot = await start(options, prepared, step)\n dispatched += slot.dispatched ? 1 : 0\n declined += slot.declined === true ? 1 : 0\n const result = await commit(options, slot, maxResultBytes)\n results.push(result)\n if (concludedBy === undefined && !result.isError && result.concludesTurn) concludedBy = first.toolName\n index++\n continue\n }\n\n // A rolling parallel segment. Re-prepare each call immediately before start;\n // an updated registry can therefore turn the next call into a barrier.\n const segment: Slot[] = []\n const segmentAbort = new AbortController()\n const segmentOptions: RunToolCallsOptions = {\n ...options,\n signal: AbortSignal.any([options.signal, segmentAbort.signal]),\n }\n let nextPrepared = prepared\n try {\n while (index < calls.length && segment.length < maxParallel) {\n const call = calls[index]\n if (call === undefined) break\n const candidate = nextPrepared\n if (candidate.mode !== 'parallel') break\n const slot = await start(segmentOptions, candidate, step)\n dispatched += slot.dispatched ? 1 : 0\n declined += slot.declined === true ? 1 : 0\n segment.push(slot)\n index++\n const nextCall = calls[index]\n if (nextCall !== undefined && segment.length < maxParallel) {\n nextPrepared = prepare(segmentOptions, nextCall)\n if (nextPrepared.mode !== 'parallel') carried = nextPrepared\n }\n }\n } catch (error: unknown) {\n // Admission of a later sibling failed after earlier bodies started.\n // Cancel and drain everything already owned before propagating that error.\n segmentAbort.abort(error)\n await drainSegment(options, segment, maxResultBytes)\n throw error\n }\n if (segment.length === 0) continue\n // Bodies have already started. Finalization and history publication remain ordered.\n let fatal: unknown\n let hasFatal = false\n for (const slot of segment) {\n try {\n const result = await commit(options, slot, maxResultBytes)\n results.push(result)\n if (concludedBy === undefined && !result.isError && result.concludesTurn) concludedBy = slot.call.toolName\n } catch (error: unknown) {\n if (!hasFatal) fatal = error\n hasFatal = true\n segmentAbort.abort(error)\n }\n }\n // Every parallel body was already dispatched. Commit/drain all siblings so\n // none is orphaned, then propagate the first fatal contract violation.\n if (hasFatal) throw fatal\n }\n return {\n results: Object.freeze(results),\n concluded: concludedBy !== undefined,\n ...concludedBy === undefined ? {} : { concludedBy },\n dispatched,\n budgeted: admission.budgeted,\n declined,\n }\n}\n\nfunction prepare(options: RunToolCallsOptions, call: ToolCallRequest) {\n return prepareToolCall({\n catalog: options.catalog, call, position: options.position, signal: options.signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n ...options.interceptors === undefined ? {} : { interceptors: options.interceptors },\n ...options.approvals === undefined ? {} : { approvals: options.approvals },\n })\n}\n\nasync function start(\n options: RunToolCallsOptions,\n prepared: ReturnType<typeof prepareToolCall>,\n step: StepRuntime,\n): Promise<Slot> {\n const { maxDurationMs, teardownTimeoutMs } = step\n const deadline = AbortSignal.timeout(maxDurationMs)\n const signal = AbortSignal.any([options.signal, deadline])\n const boundedPrepared = {\n ...prepared,\n options: { ...prepared.options, signal, teardownTimeoutMs },\n context: { ...prepared.context, signal },\n }\n const call = boundedPrepared.options.call\n const trace: TraceRef = {\n traceId: options.parentTrace.traceId,\n spanId: createSpanId(),\n parentSpanId: options.parentTrace.spanId,\n }\n options.history.append({ kind: 'tool-call', callId: call.callId, name: call.toolName, rawArguments: call.rawArguments })\n await emitEvent(options, { type: 'span-start', trace, at: now(), name: `execute_tool ${call.toolName}`, kind: 'execute_tool', attributes: {\n 'gen_ai.operation.name': 'execute_tool', 'gen_ai.tool.name': call.toolName, 'gen_ai.tool.call.id': call.callId,\n }, input: call.rawArguments })\n await emitEvent(options, { type: 'tool-call', call, trace })\n // A tool the model may always reach: submitting, asking, delegating. Letting\n // a budget block these is what turns a spent budget into a dead run.\n const exempt = options.catalog.get(call.toolName)?.budgetExempt === true\n const grant = step.programs.get(call.toolName)\n // A program spends the budget it is charged for; it can never be the free\n // call, and a parallel program would race its own children for siblings.\n if (grant !== undefined && (exempt || prepared.mode !== 'exclusive')) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure(\n 'this program tool must be exclusive and must not be budget-exempt',\n NESTED_TOOL_ERROR_CODES.CONFIGURATION,\n )),\n }\n const ticket = step.admission.reserve(exempt)\n if (ticket === undefined) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false, declined: true,\n pending: Promise.resolve(declinedResult(options.declineReason ?? 'tool-calls')),\n }\n // Every path below that does not start the body returns the reservation.\n try {\n return await authorizeAndDispatch(options, boundedPrepared, call, trace, signal, deadline, ticket, step, grant)\n } finally {\n ticket.release()\n }\n}\n\nasync function authorizeAndDispatch(\n options: RunToolCallsOptions,\n boundedPrepared: ReturnType<typeof prepareToolCall>,\n call: ToolCallRequest,\n trace: TraceRef,\n signal: AbortSignal,\n deadline: AbortSignal,\n ticket: AdmissionTicket,\n step: StepRuntime,\n grant: ProgramGrant | undefined,\n): Promise<Slot> {\n const { maxDurationMs, teardownTimeoutMs } = step\n if (options.signal.aborted) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure('the call was cancelled before it started', TOOL_ERROR_CODES.ABORTED_BEFORE_DISPATCH)),\n }\n const authorizationPending = authorizeToolCall(withApprovalEvents(options, boundedPrepared, trace))\n let authorization: Awaited<ReturnType<typeof authorizeToolCall>>\n try {\n authorization = await raceWithSignal(authorizationPending, signal)\n } catch (error: unknown) {\n if (!signal.aborted) throw error\n const settled = await waitForSettlement(authorizationPending, teardownTimeoutMs)\n if (!settled) throw teardownFailure(call.toolName, 'authorization', teardownTimeoutMs, error)\n return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(cancelledResult(deadline, maxDurationMs)),\n }\n }\n if (authorization.kind === 'final') return {\n call, trace, signal, deadline, teardownTimeoutMs,\n dispatched: false, pending: Promise.resolve(authorization.result),\n }\n try {\n const checkpointing = Promise.resolve(options.checkpoint?.({\n kind: 'before-tool-dispatch', call, snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n }))\n try { await raceWithSignal(checkpointing, signal) }\n catch (error: unknown) {\n if (!signal.aborted) throw error\n if (!await waitForSettlement(checkpointing, teardownTimeoutMs)) {\n throw teardownFailure(call.toolName, 'checkpoint', teardownTimeoutMs, error)\n }\n }\n } catch (error: unknown) {\n if (error instanceof ToolError && error.code === TOOL_ERROR_CODES.TEARDOWN_TIMEOUT) throw error\n return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure(`history checkpoint failed: ${messageOf(error)}`, TOOL_ERROR_CODES.CHECKPOINT_FAILED)),\n }\n }\n if (signal.aborted) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(cancelledResult(deadline, maxDurationMs)),\n }\n const program = grant === undefined\n ? undefined\n : new ProgramRun(options, step, grant, { call, trace, signal })\n if (program !== undefined) attachNestedToolPort(authorization.call, program.port, signal => program.bindExecutionSignal(signal))\n ticket.confirm()\n const pending = dispatchAuthorizedToolCall(authorization.call)\n // Observe rejection in the same turn in which dispatch creates the promise.\n // commit() still receives and propagates the original rejection in order.\n void pending.catch(() => undefined)\n return {\n call, trace, signal, deadline, teardownTimeoutMs,\n authorized: authorization.call, dispatched: true,\n ...program === undefined ? {} : { program },\n pending,\n }\n}\n\n/**\n * What the model reads when the loop refuses to run a call.\n *\n * Not a failure. A failure is something that went wrong, and a model answers\n * one by retrying — with a smaller argument, a different path — which spends\n * exactly what is no longer there. This says what happened and what to do\n * instead, in the plainest terms available, and nothing about it reads as\n * broken tooling.\n * @param reason - Which limit refused the call.\n * @returns A successful result carrying the instruction.\n */\nfunction declinedResult(reason: ToolDeclineReason): ToolExecutionResult {\n return {\n isError: false,\n value: undefined,\n content: [{ type: 'text', text: declineText(reason) }],\n // Never shown to the model; lets a UI render this as declined rather than\n // as work that succeeded.\n meta: { declined: true, reason },\n }\n}\n\n/**\n * The instruction for one decline reason.\n *\n * Each limit gets its own wording: a model told \"no remaining tool-call\n * budget\" when it actually tripped the repeat guard learns the wrong lesson\n * and repeats the call in the next turn.\n * @param reason - Which limit refused the call.\n * @returns The sentence the model reads.\n */\nfunction declineText(reason: ToolDeclineReason): string {\n const finish = ' Answer now from what you already have, and say plainly what is'\n + ' unverified or unfinished.'\n switch (reason) {\n case 'tool-calls':\n return 'This call was not run: the turn has spent its tool-call budget.'\n + ' Further work calls will not run either, so do not retry it.' + finish\n case 'repeated-tool-call':\n return 'This call was not run: it repeats a call already made with the same'\n + ' arguments, and repeating it cannot produce a new result. Change'\n + ' approach or finish.' + finish\n case 'tool-call-cycle':\n return 'This call was not run: the same sequence of calls has repeated'\n + ' several times without progress. Break the cycle rather than'\n + ' re-entering it.' + finish\n case 'tokens':\n return 'This call was not run: the turn has spent its token budget.' + finish\n case 'consecutive-tool-errors':\n return 'This call was not run: too many calls in a row have failed.'\n + ' Something in the approach is wrong, not the individual call.' + finish\n case 'steps':\n return 'This call was not run: the turn has no model steps left.' + finish\n case 'usage-required':\n return 'This call was not run: the run policy requires provider usage'\n + ' reporting that the last model call did not supply.' + finish\n }\n}\n\nasync function drainSegment(\n options: RunToolCallsOptions,\n segment: readonly Slot[],\n maxResultBytes: number,\n): Promise<void> {\n for (const slot of segment) {\n try { await commit(options, slot, maxResultBytes) }\n catch { /* The admission failure remains primary after every owned slot settles. */ }\n }\n}\n\nasync function commit(\n options: RunToolCallsOptions,\n slot: Slot,\n maxResultBytes: number,\n): Promise<ToolExecutionResult> {\n let result: ToolExecutionResult\n let fatal: unknown\n let hasFatal = false\n try {\n let pending: ToolExecutionResult\n try {\n pending = await raceWithSignal(slot.pending, slot.signal)\n } catch (error: unknown) {\n if (!slot.signal.aborted) throw error\n const settled = await waitForSettlement(slot.pending, slot.teardownTimeoutMs)\n if (!settled) throw teardownFailure(slot.call.toolName, 'execution', slot.teardownTimeoutMs, error)\n pending = cancelledResult(slot.deadline)\n }\n if (slot.program !== undefined) {\n // The body has returned; no child may start or publish after this point.\n const closed = await slot.program.close(slot.teardownTimeoutMs)\n if (!closed) throw teardownFailure(slot.call.toolName, 'nested call', slot.teardownTimeoutMs, undefined)\n const latched: unknown = slot.program.fatal\n if (latched !== undefined) {\n throw toolErrorDisposition(latched) === 'fatal'\n ? latched\n : ToolError.fatal(messageOf(latched), errorCode(latched), { cause: latched })\n }\n }\n let finalized = pending\n if (slot.authorized !== undefined) {\n const finalizing = finalizeToolCall(slot.authorized, pending)\n try {\n finalized = await raceWithSignal(finalizing, slot.signal)\n } catch (error: unknown) {\n if (!slot.signal.aborted) throw error\n const settled = await waitForSettlement(finalizing, slot.teardownTimeoutMs)\n if (!settled) throw teardownFailure(slot.call.toolName, 'finalization', slot.teardownTimeoutMs, error)\n finalized = cancelledResult(slot.deadline)\n }\n }\n result = immutableResult(await boundOutput(options, slot, finalized), maxResultBytes)\n } catch (error: unknown) {\n slot.program?.abort(error)\n hasFatal = toolErrorDisposition(error) === 'fatal'\n fatal = error\n result = immutableResult(toolFailure(messageOf(error), errorCode(error)), maxResultBytes)\n }\n const message = createToolResultMessage({ callId: slot.call.callId, content: [...result.content], isError: result.isError })\n options.history.append({ kind: 'tool-result', callId: slot.call.callId, message, result })\n await emitEvent(options, { type: 'tool-result', call: slot.call, result, trace: slot.trace })\n await emitEvent(options, {\n type: 'span-end', trace: slot.trace, at: now(), status: result.isError ? 'error' : 'success',\n output: result.isError ? { error: result.error } : result.value,\n ...result.isError ? { error: { type: 'ToolError', message: result.error.message, code: result.error.code } } : {},\n })\n if (result.additionalContext !== undefined) {\n // Additional context is deliberately a separate application message.\n options.history.append({ kind: 'user', message: createUserMessage({\n content: [...result.additionalContext], source: { kind: 'app', producer: `tool:${slot.call.toolName}` },\n }) })\n }\n if (hasFatal) throw fatal\n return result\n}\n\n/**\n * Keep one tool result inside its share of the context window.\n *\n * This runs at the RESULT boundary rather than when the request is assembled,\n * which is the whole point: by the time an oversized result reaches the model\n * the window is already gone, and the turn's only remaining move is to lose\n * everything it has paid for. Both reference harnesses cut here.\n *\n * The budget is the stricter of the turn's and the tool's own declaration, so\n * a tool that knows it returns a lot can ask for room without any tool being\n * able to exceed what the host allows.\n * @param options - The scheduling options carrying budget and store.\n * @param slot - The call this result belongs to.\n * @param result - The finalized result.\n * @returns The result the model will read.\n */\nasync function boundOutput(\n options: RunToolCallsOptions,\n slot: Slot,\n result: ToolExecutionResult,\n): Promise<ToolExecutionResult> {\n const turnBudget = options.maxResultTokens\n const toolBudget = options.catalog.get(slot.call.toolName)?.maxOutputTokens\n const budget = turnBudget === undefined\n ? toolBudget\n : toolBudget === undefined ? turnBudget : Math.min(turnBudget, toolBudget)\n if (budget === undefined) return result\n const tokens = estimateTextBlockTokens(result.content)\n if (tokens <= budget) return result\n\n // Only text is shortened. Cutting an image block produces a corrupt image\n // rather than a smaller one, so those pass through and are counted by the\n // byte cap instead.\n const texts = result.content.filter(block => block.type === 'text')\n if (texts.length === 0) return result\n const full = texts.map(block => block.text).join('\\n')\n const policy = options.resultOverflow ?? 'auto'\n const store = options.spillStore\n\n if (policy !== 'truncate' && store !== undefined) {\n try {\n // Reserve room for the notice inside the budget, so the replacement is\n // never bigger than what it replaced.\n const record = await store.save(full, {\n toolName: slot.call.toolName, callId: String(slot.call.callId),\n })\n const preview = previewForSpill(full, Math.max(1, Math.floor(budget * 0.75)))\n return replaceText(result, `${preview}\\n\\n[Output exceeded this call's budget of `\n + `${String(budget)} estimated tokens. The full ${String(record.bytes)} bytes are saved. `\n + `${record.retrieval}]`, {\n outputSpilled: { locator: record.locator, bytes: record.bytes, estimatedTokens: tokens },\n })\n } catch {\n // Best-effort, deliberately: a store that is full or unreachable must not\n // cost the model a result it can still read most of.\n }\n }\n\n const truncated = truncateMiddleToTokens(full, budget)\n return replaceText(result, truncated.text\n + `\\n\\n[Output was ${String(truncated.originalTokens)} estimated tokens, over this call's `\n + `budget of ${String(budget)}. Omitted output does not mean the operation failed. `\n + 'Check an existing receipt or current state; repeat the operation only when the host confirms it is safe.]', {\n outputTruncated: { estimatedTokens: truncated.originalTokens, budget },\n })\n}\n\n/**\n * Swap a result's text for one shortened block, keeping everything else.\n * @param result - The original result.\n * @param text - The replacement text.\n * @param meta - What a UI should know about the replacement.\n * @returns The rewritten result.\n */\nfunction replaceText(\n result: ToolExecutionResult,\n text: string,\n meta: JsonObject,\n): ToolExecutionResult {\n // The replacement takes the FIRST text block's position and the other text\n // blocks drop out. Appending it after the non-text blocks instead would\n // reorder a result whose image came after its caption.\n let placed = false\n const content: ContentBlock[] = []\n for (const block of result.content) {\n if (block.type !== 'text') { content.push(block); continue }\n if (placed) continue\n placed = true\n content.push({ type: 'text', text })\n }\n // `value` is deliberately untouched: it is what a host logs and replays, and\n // shortening it would make the record disagree with what the tool returned.\n return { ...result, content, meta: { ...result.meta, ...meta } }\n}\n\nfunction immutableCall(call: ToolCallRequest): ToolCallRequest {\n if (typeof call.callId !== 'string' || call.callId.length === 0\n || typeof call.toolName !== 'string' || call.toolName.length === 0\n || typeof call.rawArguments !== 'string') {\n throw new TypeError('tool call identity, name, and raw arguments must be strings')\n }\n return detachedFrozen(call)\n}\n\nfunction cancelledResult(deadline: AbortSignal, maxDurationMs?: number): ToolExecutionResult {\n const limit = maxDurationMs === undefined ? 'configured time limit' : `${maxDurationMs}ms time limit`\n return deadline.aborted\n ? toolFailure(\n `the tool exceeded its ${limit}`,\n TOOL_ERROR_CODES.TIMEOUT,\n )\n : toolFailure('the call was cancelled', TOOL_ERROR_CODES.ABORTED)\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 1) throw new RangeError(`${name} must be a positive integer`)\n return value\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"schedule.js","names":[],"sources":["../../../src/agent/loop/schedule.ts"],"sourcesContent":["import { createToolResultMessage, createUserMessage } from '../../message/index.ts'\nimport type { ContentBlock } from '../../message/index.ts'\nimport { detachedFrozen } from '../../primitives/index.ts'\nimport type { JsonObject } from '../../primitives/index.ts'\nimport { waitForSettlement } from '../../async/index.ts'\nimport type { History } from '../history/history.ts'\nimport type { ApprovalBroker } from '../tool/approval.ts'\nimport type { ToolCallPosition, ToolExecutionResult } from '../tool/definition.ts'\nimport {\n estimateTextBlockTokens, previewForSpill, truncateMiddleToTokens,\n type SpillStore, type ToolOutputOverflowPolicy,\n} from '../tool/output-budget.ts'\nimport { TOOL_ERROR_CODES, ToolError, toolErrorDisposition } from '../tool/errors.ts'\nimport {\n authorizeToolCall, dispatchAuthorizedToolCall, finalizeToolCall, prepareToolCall, toolFailure,\n type AuthorizedToolCall, type ToolCallRequest, type ToolInterceptor,\n} from '../tool/pipeline.ts'\nimport type { ToolCatalog } from '../tool/registry.ts'\nimport { createSpanId, type TraceRef } from '../trace/trace.ts'\nimport type { AgentEvent, ToolDeclineReason, TurnHooks } from './events.ts'\nimport type { RunAccountingPort } from '../accounting/contracts.ts'\nimport type { SdkLogger } from '../../logging/types.ts'\nimport { createToolAdmission, type AdmissionTicket } from './admission.ts'\nimport { ProgramRun, validPrograms, type StepRuntime } from './program.ts'\nimport {\n emitEvent, errorCode, immutableResult, messageOf, now, raceWithSignal, teardownFailure, withApprovalEvents,\n} from './tool-call-support.ts'\nimport { attachNestedToolPort, NESTED_TOOL_ERROR_CODES, type ProgramGrant } from '../tool/nested.ts'\nimport type { ProgramResultStore } from '../tool/program-results.ts'\n\nexport interface RunToolCallsOptions {\n readonly calls: readonly ToolCallRequest[]\n readonly catalog: ToolCatalog\n readonly history: History\n readonly position: ToolCallPosition\n readonly signal: AbortSignal\n readonly logger?: SdkLogger\n readonly parentTrace: TraceRef\n readonly maxParallel?: number\n /**\n * How many BUDGETED calls may still be dispatched this step.\n *\n * Calls to a `budgetExempt` tool are not counted against it, so a spent\n * budget never blocks the model from submitting, asking, or delegating.\n */\n readonly dispatchLimit?: number\n /** Why calls past `dispatchLimit` are not run; shown to the model verbatim. */\n readonly declineReason?: ToolDeclineReason\n /** Estimated tokens of text one result may put in front of the model. */\n readonly maxResultTokens?: number\n /** What happens to output over that budget. Defaults to `auto`. */\n readonly resultOverflow?: ToolOutputOverflowPolicy\n /** Where spilled output goes; `auto` spills only when this is mounted. */\n readonly spillStore?: SpillStore\n /** Maximum serialized bytes retained for one finalized result. Defaults to 4 MiB. */\n readonly maxResultBytes?: number\n /** End-to-end wall-clock allowance for one call. Defaults to 10 minutes. */\n readonly maxDurationMs?: number\n /** Maximum cancellation settlement wait. Defaults to 30 seconds. */\n readonly teardownTimeoutMs?: number\n readonly interceptors?: readonly ToolInterceptor[]\n readonly approvals?: ApprovalBroker\n readonly emit?: (event: AgentEvent) => Promise<void>\n readonly checkpoint?: TurnHooks['checkpoint']\n readonly accounting?: RunAccountingPort\n /**\n * A same-turn result the loop may expose instead of dispatching this exact\n * call again. The scheduler still records a distinct call/result pair.\n */\n readonly recover?: (call: ToolCallRequest) => ToolExecutionResult | undefined\n}\nexport interface ToolCallsOutcome {\n readonly results: readonly ToolExecutionResult[]\n readonly concluded: boolean\n readonly concludedBy?: string\n readonly dispatched: number\n /** Dispatched calls that spent budget; exempt tools are excluded. */\n readonly budgeted: number\n /** Calls the loop refused to run, for the reason in `declineReason`. */\n readonly declined: number\n}\n\ninterface Slot {\n readonly call: ToolCallRequest\n readonly trace: TraceRef\n readonly authorized?: AuthorizedToolCall\n readonly pending: Promise<ToolExecutionResult>\n readonly dispatched: boolean\n /** The program this call hosts, when the host granted it one. */\n readonly program?: ProgramRun\n /** Set when the loop refused to run the call. */\n readonly declined?: boolean\n /** Set when the call was answered from this turn's earlier identical result. */\n readonly recovered?: boolean\n readonly signal: AbortSignal\n readonly deadline: AbortSignal\n readonly teardownTimeoutMs: number\n}\n\n/** Scheduler options the loop owns and does not publish. */\nexport interface InternalScheduleOptions {\n /** Track actual dispatched bodies, excluding policy, declined calls and result publication. */\n readonly onToolActivity?: (awaitsPerson: boolean, active: boolean) => void\n /** Overrides `dispatchLimit`; `unbounded` when the budget is a notice rather than a wall. */\n readonly admissionLimit?: number | 'unbounded'\n /** A call-specific guard; refusing one sibling does not reserve another's quota. */\n readonly decline?: (call: ToolCallRequest) => ToolDeclineReason | undefined\n /**\n * Refuses a call even when it is budget-exempt. The finalize window uses it\n * so that only the tools a mode names (its submission) can run once the\n * budget is spent, not every tool that is normally always reachable.\n */\n readonly restrict?: (call: ToolCallRequest) => ToolDeclineReason | undefined\n /**\n * Program tools and what each may call. Research seam for SP-01; the public\n * way to enable programs is not decided.\n */\n readonly programs?: ReadonlyMap<string, ProgramGrant>\n /** Where programs retain values; owned and closed by the caller. */\n readonly programResults?: ProgramResultStore\n}\n\n/** Execute safe siblings concurrently while committing every result in model order. */\nexport async function runToolCalls(options: RunToolCallsOptions): Promise<ToolCallsOutcome> {\n return await scheduleToolCalls(options)\n}\n\n/** {@link runToolCalls} with the loop's internal options. */\nexport async function scheduleToolCalls(\n options: RunToolCallsOptions,\n internal: InternalScheduleOptions = {},\n): Promise<ToolCallsOutcome> {\n const maxParallel = positiveInteger(options.maxParallel ?? 8, 'maxParallel')\n const maxResultBytes = positiveInteger(options.maxResultBytes ?? 4 * 1024 * 1024, 'maxResultBytes')\n const maxDurationMs = positiveInteger(options.maxDurationMs ?? 10 * 60_000, 'maxDurationMs')\n const teardownTimeoutMs = positiveInteger(options.teardownTimeoutMs ?? 30_000, 'teardownTimeoutMs')\n const calls = Object.freeze(options.calls.map(immutableCall))\n const admission = createToolAdmission(internal.admissionLimit ?? Math.max(0, options.dispatchLimit ?? calls.length))\n const step: StepRuntime = {\n admission, maxDurationMs, teardownTimeoutMs, maxResultBytes,\n programs: validPrograms(internal.programs),\n programResults: internal.programResults,\n }\n const results: ToolExecutionResult[] = []\n let dispatched = 0\n let declined = 0\n let concludedBy: string | undefined\n let index = 0\n let carried: ReturnType<typeof prepareToolCall> | undefined\n\n while (index < calls.length) {\n const first = calls[index]\n if (first === undefined) break\n const prepared = carried ?? prepare(options, first)\n carried = undefined\n if (prepared.mode === 'exclusive') {\n const slot = await start(options, prepared, step, internal)\n dispatched += slot.dispatched ? 1 : 0\n declined += slot.declined === true ? 1 : 0\n const result = await commit(options, slot, maxResultBytes)\n results.push(result)\n if (concludedBy === undefined && !result.isError && result.concludesTurn) concludedBy = first.toolName\n index++\n continue\n }\n\n // A rolling parallel segment. Re-prepare each call immediately before start;\n // an updated registry can therefore turn the next call into a barrier.\n const segment: Slot[] = []\n const segmentAbort = new AbortController()\n const segmentOptions: RunToolCallsOptions = {\n ...options,\n signal: AbortSignal.any([options.signal, segmentAbort.signal]),\n }\n let nextPrepared = prepared\n try {\n while (index < calls.length && segment.length < maxParallel) {\n const call = calls[index]\n if (call === undefined) break\n const candidate = nextPrepared\n if (candidate.mode !== 'parallel') break\n const slot = await start(segmentOptions, candidate, step, internal)\n dispatched += slot.dispatched ? 1 : 0\n declined += slot.declined === true ? 1 : 0\n segment.push(slot)\n index++\n const nextCall = calls[index]\n if (nextCall !== undefined && segment.length < maxParallel) {\n nextPrepared = prepare(segmentOptions, nextCall)\n if (nextPrepared.mode !== 'parallel') carried = nextPrepared\n }\n }\n } catch (error: unknown) {\n // Admission of a later sibling failed after earlier bodies started.\n // Cancel and drain everything already owned before propagating that error.\n segmentAbort.abort(error)\n await drainSegment(options, segment, maxResultBytes)\n throw error\n }\n if (segment.length === 0) continue\n // Bodies have already started. Finalization and history publication remain ordered.\n let fatal: unknown\n let hasFatal = false\n for (const slot of segment) {\n try {\n const result = await commit(options, slot, maxResultBytes)\n results.push(result)\n if (concludedBy === undefined && !result.isError && result.concludesTurn) concludedBy = slot.call.toolName\n } catch (error: unknown) {\n if (!hasFatal) fatal = error\n hasFatal = true\n segmentAbort.abort(error)\n }\n }\n // Every parallel body was already dispatched. Commit/drain all siblings so\n // none is orphaned, then propagate the first fatal contract violation.\n if (hasFatal) throw fatal\n }\n return {\n results: Object.freeze(results),\n concluded: concludedBy !== undefined,\n ...concludedBy === undefined ? {} : { concludedBy },\n dispatched,\n budgeted: admission.budgeted,\n declined,\n }\n}\n\nfunction prepare(options: RunToolCallsOptions, call: ToolCallRequest) {\n return prepareToolCall({\n catalog: options.catalog, call, position: options.position, signal: options.signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n ...options.interceptors === undefined ? {} : { interceptors: options.interceptors },\n ...options.approvals === undefined ? {} : { approvals: options.approvals },\n })\n}\n\nasync function start(\n options: RunToolCallsOptions,\n prepared: ReturnType<typeof prepareToolCall>,\n step: StepRuntime,\n internal: InternalScheduleOptions,\n): Promise<Slot> {\n const { teardownTimeoutMs } = step\n const definition = options.catalog.get(prepared.options.call.toolName)\n // A call that waits for a person is bounded by its own limit, not the turn's\n // tool limit; without one it ends only with the run.\n const maxDurationMs = definition?.awaitsPerson === true ? definition.timeoutMs : step.maxDurationMs\n const deadline = maxDurationMs === undefined ? new AbortController().signal : AbortSignal.timeout(maxDurationMs)\n const signal = AbortSignal.any([options.signal, deadline])\n const boundedPrepared = {\n ...prepared,\n options: { ...prepared.options, signal, teardownTimeoutMs },\n context: { ...prepared.context, signal },\n }\n const call = boundedPrepared.options.call\n const trace: TraceRef = {\n traceId: options.parentTrace.traceId,\n spanId: createSpanId(),\n parentSpanId: options.parentTrace.spanId,\n }\n const recovered = !options.signal.aborted && !signal.aborted\n ? options.recover?.(call)\n : undefined\n options.history.append({ kind: 'tool-call', callId: call.callId, name: call.toolName, rawArguments: call.rawArguments })\n await emitEvent(options, { type: 'span-start', trace, at: now(), name: `execute_tool ${call.toolName}`, kind: 'execute_tool', attributes: {\n 'gen_ai.operation.name': 'execute_tool', 'gen_ai.tool.name': call.toolName, 'gen_ai.tool.call.id': call.callId,\n ...recovered === undefined ? {} : { 'sdk.tool.recovered': true },\n }, input: call.rawArguments })\n await emitEvent(options, { type: 'tool-call', call, trace })\n // Recovery is not a completion signal. Preserve the existing cancellation\n // outcomes before consulting retained same-turn state.\n if (options.signal.aborted) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure(\n 'the call was cancelled before it started',\n TOOL_ERROR_CODES.ABORTED_BEFORE_DISPATCH,\n )),\n }\n if (signal.aborted) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(cancelledResult(deadline, maxDurationMs)),\n }\n const restricted = internal.restrict?.(call)\n if (restricted !== undefined) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false, declined: true,\n pending: Promise.resolve(declinedResult(restricted)),\n }\n if (recovered !== undefined) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false, recovered: true,\n pending: Promise.resolve(recovered),\n }\n // A tool the model may always reach: submitting, asking, delegating. Letting\n // a budget block these is what turns a spent budget into a dead run.\n const exempt = options.catalog.get(call.toolName)?.budgetExempt === true\n const decline = exempt ? undefined : internal.decline?.(call)\n if (decline !== undefined) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false, declined: true,\n pending: Promise.resolve(declinedResult(decline)),\n }\n const grant = step.programs.get(call.toolName)\n // A program spends the budget it is charged for; it can never be the free\n // call, and a parallel program would race its own children for siblings.\n if (grant !== undefined && (exempt || prepared.mode !== 'exclusive')) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure(\n 'this program tool must be exclusive and must not be budget-exempt',\n NESTED_TOOL_ERROR_CODES.CONFIGURATION,\n )),\n }\n const ticket = step.admission.reserve(exempt)\n if (ticket === undefined) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false, declined: true,\n pending: Promise.resolve(declinedResult(options.declineReason ?? 'tool-calls')),\n }\n // Every path below that does not start the body returns the reservation.\n try {\n return await authorizeAndDispatch(options, boundedPrepared, call, trace, signal, deadline, ticket, step, grant, internal)\n } finally {\n ticket.release()\n }\n}\n\nasync function authorizeAndDispatch(\n options: RunToolCallsOptions,\n boundedPrepared: ReturnType<typeof prepareToolCall>,\n call: ToolCallRequest,\n trace: TraceRef,\n signal: AbortSignal,\n deadline: AbortSignal,\n ticket: AdmissionTicket,\n step: StepRuntime,\n grant: ProgramGrant | undefined,\n internal: InternalScheduleOptions,\n): Promise<Slot> {\n const { maxDurationMs, teardownTimeoutMs } = step\n if (options.signal.aborted) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure('the call was cancelled before it started', TOOL_ERROR_CODES.ABORTED_BEFORE_DISPATCH)),\n }\n const authorizationPending = authorizeToolCall(withApprovalEvents(options, boundedPrepared, trace))\n let authorization: Awaited<ReturnType<typeof authorizeToolCall>>\n try {\n authorization = await raceWithSignal(authorizationPending, signal)\n } catch (error: unknown) {\n if (!signal.aborted) throw error\n const settled = await waitForSettlement(authorizationPending, teardownTimeoutMs)\n if (!settled) throw teardownFailure(call.toolName, 'authorization', teardownTimeoutMs, error)\n return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(cancelledResult(deadline, maxDurationMs)),\n }\n }\n if (authorization.kind === 'final') return {\n call, trace, signal, deadline, teardownTimeoutMs,\n dispatched: false, pending: Promise.resolve(authorization.result),\n }\n try {\n const checkpointing = Promise.resolve(options.checkpoint?.({\n kind: 'before-tool-dispatch', call, snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n }))\n try { await raceWithSignal(checkpointing, signal) }\n catch (error: unknown) {\n if (!signal.aborted) throw error\n if (!await waitForSettlement(checkpointing, teardownTimeoutMs)) {\n throw teardownFailure(call.toolName, 'checkpoint', teardownTimeoutMs, error)\n }\n }\n } catch (error: unknown) {\n if (error instanceof ToolError && error.code === TOOL_ERROR_CODES.TEARDOWN_TIMEOUT) throw error\n return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure(`history checkpoint failed: ${messageOf(error)}`, TOOL_ERROR_CODES.CHECKPOINT_FAILED)),\n }\n }\n if (signal.aborted) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(cancelledResult(deadline, maxDurationMs)),\n }\n const program = grant === undefined\n ? undefined\n : new ProgramRun(options, step, grant, { call, trace, signal })\n if (program !== undefined) attachNestedToolPort(authorization.call, program.port, signal => program.bindExecutionSignal(signal))\n ticket.confirm()\n const awaitsPerson = authorization.call.tool.awaitsPerson === true\n internal.onToolActivity?.(awaitsPerson, true)\n const pending = dispatchAuthorizedToolCall(authorization.call)\n // Observe rejection in the same turn in which dispatch creates the promise.\n // commit() still receives and propagates the original rejection in order.\n const settled = (): void => { internal.onToolActivity?.(awaitsPerson, false) }\n void pending.then(settled, settled)\n return {\n call, trace, signal, deadline, teardownTimeoutMs,\n authorized: authorization.call, dispatched: true,\n ...program === undefined ? {} : { program },\n pending,\n }\n}\n\n/**\n * What the model reads when the loop refuses to run a call.\n *\n * Not a failure. A failure is something that went wrong, and a model answers\n * one by retrying — with a smaller argument, a different path — which spends\n * exactly what is no longer there. This says what happened and what to do\n * instead, in the plainest terms available, and nothing about it reads as\n * broken tooling.\n * @param reason - Which limit refused the call.\n * @returns A successful result carrying the instruction.\n */\nfunction declinedResult(reason: ToolDeclineReason): ToolExecutionResult {\n return {\n isError: false,\n value: undefined,\n content: [{ type: 'text', text: declineText(reason) }],\n // Never shown to the model; lets a UI render this as declined rather than\n // as work that succeeded.\n meta: { declined: true, reason },\n }\n}\n\n/**\n * The instruction for one decline reason.\n *\n * Each limit gets its own wording: a model told \"no remaining tool-call\n * budget\" when it actually tripped the repeat guard learns the wrong lesson\n * and repeats the call in the next turn.\n * @param reason - Which limit refused the call.\n * @returns The sentence the model reads.\n */\nfunction declineText(reason: ToolDeclineReason): string {\n const finish = ' Answer now from what you already have, and say plainly what is'\n + ' unverified or unfinished.'\n switch (reason) {\n case 'tool-calls':\n return 'This call was not run: the turn has spent its tool-call budget.'\n + ' Further work calls will not run either, so do not retry it.' + finish\n case 'repeated-tool-call':\n return 'This call was not run: it repeats a call already made with the same'\n + ' arguments, and repeating it cannot produce a new result. Change'\n + ' approach or finish.' + finish\n case 'tool-call-cycle':\n return 'This call was not run: the same sequence of calls has repeated'\n + ' several times without progress. Break the cycle rather than'\n + ' re-entering it.' + finish\n case 'tokens':\n return 'This call was not run: the turn has spent its token budget.' + finish\n case 'time':\n return 'This call was not run: the turn has spent its time budget.' + finish\n case 'consecutive-tool-errors':\n return 'This call was not run: too many calls in a row have failed.'\n + ' Something in the approach is wrong, not the individual call.' + finish\n case 'steps':\n return 'This call was not run: the turn has no model steps left.' + finish\n case 'usage-required':\n return 'This call was not run: the run policy requires provider usage'\n + ' reporting that the last model call did not supply.' + finish\n }\n}\n\nasync function drainSegment(\n options: RunToolCallsOptions,\n segment: readonly Slot[],\n maxResultBytes: number,\n): Promise<void> {\n for (const slot of segment) {\n try { await commit(options, slot, maxResultBytes) }\n catch { /* The admission failure remains primary after every owned slot settles. */ }\n }\n}\n\nasync function commit(\n options: RunToolCallsOptions,\n slot: Slot,\n maxResultBytes: number,\n): Promise<ToolExecutionResult> {\n let result: ToolExecutionResult\n let fatal: unknown\n let hasFatal = false\n try {\n let pending: ToolExecutionResult\n try {\n pending = await raceWithSignal(slot.pending, slot.signal)\n } catch (error: unknown) {\n if (!slot.signal.aborted) throw error\n const settled = await waitForSettlement(slot.pending, slot.teardownTimeoutMs)\n if (!settled) throw teardownFailure(slot.call.toolName, 'execution', slot.teardownTimeoutMs, error)\n pending = cancelledResult(slot.deadline)\n }\n if (slot.program !== undefined) {\n // The body has returned; no child may start or publish after this point.\n const closed = await slot.program.close(slot.teardownTimeoutMs)\n if (!closed) throw teardownFailure(slot.call.toolName, 'nested call', slot.teardownTimeoutMs, undefined)\n const latched: unknown = slot.program.fatal\n if (latched !== undefined) {\n throw toolErrorDisposition(latched) === 'fatal'\n ? latched\n : ToolError.fatal(messageOf(latched), errorCode(latched), { cause: latched })\n }\n }\n let finalized = pending\n if (slot.authorized !== undefined) {\n const finalizing = finalizeToolCall(slot.authorized, pending)\n try {\n finalized = await raceWithSignal(finalizing, slot.signal)\n } catch (error: unknown) {\n if (!slot.signal.aborted) throw error\n const settled = await waitForSettlement(finalizing, slot.teardownTimeoutMs)\n if (!settled) throw teardownFailure(slot.call.toolName, 'finalization', slot.teardownTimeoutMs, error)\n finalized = cancelledResult(slot.deadline)\n }\n }\n result = immutableResult(await boundOutput(options, slot, finalized), maxResultBytes)\n } catch (error: unknown) {\n slot.program?.abort(error)\n hasFatal = toolErrorDisposition(error) === 'fatal'\n fatal = error\n result = immutableResult(toolFailure(messageOf(error), errorCode(error)), maxResultBytes)\n }\n const message = createToolResultMessage({ callId: slot.call.callId, content: [...result.content], isError: result.isError })\n options.history.append({ kind: 'tool-result', callId: slot.call.callId, message, result })\n await emitEvent(options, { type: 'tool-result', call: slot.call, result, trace: slot.trace,\n ...slot.declined === true ? { declined: true as const } : {},\n ...slot.recovered === true ? { recovered: true as const } : {},\n ...slot.dispatched ? {} : { dispatched: false as const } })\n await emitEvent(options, {\n type: 'span-end', trace: slot.trace, at: now(), status: result.isError ? 'error' : 'success',\n output: result.isError ? { error: result.error } : result.value,\n ...result.isError ? { error: { type: 'ToolError', message: result.error.message, code: result.error.code } } : {},\n })\n if (result.additionalContext !== undefined) {\n // Additional context is deliberately a separate application message.\n options.history.append({ kind: 'user', message: createUserMessage({\n content: [...result.additionalContext], source: { kind: 'app', producer: `tool:${slot.call.toolName}` },\n }) })\n }\n if (hasFatal) throw fatal\n return result\n}\n\n/**\n * Keep one tool result inside its share of the context window.\n *\n * This runs at the RESULT boundary rather than when the request is assembled,\n * which is the whole point: by the time an oversized result reaches the model\n * the window is already gone, and the turn's only remaining move is to lose\n * everything it has paid for. Both reference harnesses cut here.\n *\n * The budget is the stricter of the turn's and the tool's own declaration, so\n * a tool that knows it returns a lot can ask for room without any tool being\n * able to exceed what the host allows.\n * @param options - The scheduling options carrying budget and store.\n * @param slot - The call this result belongs to.\n * @param result - The finalized result.\n * @returns The result the model will read.\n */\nasync function boundOutput(\n options: RunToolCallsOptions,\n slot: Slot,\n result: ToolExecutionResult,\n): Promise<ToolExecutionResult> {\n const turnBudget = options.maxResultTokens\n const toolBudget = options.catalog.get(slot.call.toolName)?.maxOutputTokens\n const budget = turnBudget === undefined\n ? toolBudget\n : toolBudget === undefined ? turnBudget : Math.min(turnBudget, toolBudget)\n if (budget === undefined) return result\n const tokens = estimateTextBlockTokens(result.content)\n if (tokens <= budget) return result\n\n // Only text is shortened. Cutting an image block produces a corrupt image\n // rather than a smaller one, so those pass through and are counted by the\n // byte cap instead.\n const texts = result.content.filter(block => block.type === 'text')\n if (texts.length === 0) return result\n const full = texts.map(block => block.text).join('\\n')\n const policy = options.resultOverflow ?? 'auto'\n const store = options.spillStore\n\n if (policy !== 'truncate' && store !== undefined) {\n try {\n // Reserve room for the notice inside the budget, so the replacement is\n // never bigger than what it replaced.\n const record = await store.save(full, {\n toolName: slot.call.toolName, callId: String(slot.call.callId),\n })\n const preview = previewForSpill(full, Math.max(1, Math.floor(budget * 0.75)))\n return replaceText(result, `${preview}\\n\\n[Output exceeded this call's budget of `\n + `${String(budget)} estimated tokens. The full ${String(record.bytes)} bytes are saved. `\n + `${record.retrieval}]`, {\n outputSpilled: { locator: record.locator, bytes: record.bytes, estimatedTokens: tokens },\n })\n } catch {\n // Best-effort, deliberately: a store that is full or unreachable must not\n // cost the model a result it can still read most of.\n }\n }\n\n const truncated = truncateMiddleToTokens(full, budget)\n return replaceText(result, truncated.text\n + `\\n\\n[Output was ${String(truncated.originalTokens)} estimated tokens, over this call's `\n + `budget of ${String(budget)}. Omitted output does not mean the operation failed. `\n + 'Check an existing receipt or current state; repeat the operation only when the host confirms it is safe.]', {\n outputTruncated: { estimatedTokens: truncated.originalTokens, budget },\n })\n}\n\n/**\n * Swap a result's text for one shortened block, keeping everything else.\n * @param result - The original result.\n * @param text - The replacement text.\n * @param meta - What a UI should know about the replacement.\n * @returns The rewritten result.\n */\nfunction replaceText(\n result: ToolExecutionResult,\n text: string,\n meta: JsonObject,\n): ToolExecutionResult {\n // The replacement takes the FIRST text block's position and the other text\n // blocks drop out. Appending it after the non-text blocks instead would\n // reorder a result whose image came after its caption.\n let placed = false\n const content: ContentBlock[] = []\n for (const block of result.content) {\n if (block.type !== 'text') { content.push(block); continue }\n if (placed) continue\n placed = true\n content.push({ type: 'text', text })\n }\n // `value` is deliberately untouched: it is what a host logs and replays, and\n // shortening it would make the record disagree with what the tool returned.\n return { ...result, content, meta: { ...result.meta, ...meta } }\n}\n\nfunction immutableCall(call: ToolCallRequest): ToolCallRequest {\n if (typeof call.callId !== 'string' || call.callId.length === 0\n || typeof call.toolName !== 'string' || call.toolName.length === 0\n || typeof call.rawArguments !== 'string') {\n throw new TypeError('tool call identity, name, and raw arguments must be strings')\n }\n return detachedFrozen(call)\n}\n\nfunction cancelledResult(deadline: AbortSignal, maxDurationMs?: number): ToolExecutionResult {\n const limit = maxDurationMs === undefined ? 'configured time limit' : `${maxDurationMs}ms time limit`\n return deadline.aborted\n ? toolFailure(\n `the tool exceeded its ${limit}`,\n TOOL_ERROR_CODES.TIMEOUT,\n )\n : toolFailure('the call was cancelled', TOOL_ERROR_CODES.ABORTED)\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 1) throw new RangeError(`${name} must be a positive integer`)\n return value\n}\n"],"mappings":"g/BA2HA,eAAsB,EAAa,EAAyD,CAC1F,OAAO,MAAM,EAAkB,CAAO,CACxC,CAGA,eAAsB,EACpB,EACA,EAAoC,CAAC,EACV,CAC3B,IAAM,EAAc,EAAgB,EAAQ,aAAe,EAAG,aAAa,EACrE,EAAiB,EAAgB,EAAQ,gBAAkB,QAAiB,gBAAgB,EAC5F,EAAgB,EAAgB,EAAQ,eAAiB,IAAa,eAAe,EACrF,EAAoB,EAAgB,EAAQ,mBAAqB,IAAQ,mBAAmB,EAC5F,EAAQ,OAAO,OAAO,EAAQ,MAAM,IAAI,CAAa,CAAC,EACtD,EAAY,EAAoB,EAAS,gBAAkB,KAAK,IAAI,EAAG,EAAQ,eAAiB,EAAM,MAAM,CAAC,EAC7G,EAAoB,CACxB,YAAW,gBAAe,oBAAmB,iBAC7C,SAAU,EAAc,EAAS,QAAQ,EACzC,eAAgB,EAAS,cAC3B,EACM,EAAiC,CAAC,EACpC,EAAa,EACb,EAAW,EACX,EACA,EAAQ,EACR,EAEJ,KAAO,EAAQ,EAAM,QAAQ,CAC3B,IAAM,EAAQ,EAAM,GACpB,GAAI,IAAU,IAAA,GAAW,MACzB,IAAM,EAAW,GAAW,EAAQ,EAAS,CAAK,EAElD,GADA,EAAU,IAAA,GACN,EAAS,OAAS,YAAa,CACjC,IAAM,EAAO,MAAM,EAAM,EAAS,EAAU,EAAM,CAAQ,EAC1D,GAAc,KAAK,WACnB,GAAY,IAAK,WAAa,IAC9B,IAAM,EAAS,MAAM,EAAO,EAAS,EAAM,CAAc,EACzD,EAAQ,KAAK,CAAM,EACf,IAAgB,IAAA,IAAa,CAAC,EAAO,SAAW,EAAO,gBAAe,EAAc,EAAM,UAC9F,IACA,QACF,CAIA,IAAM,EAAkB,CAAC,EACnB,EAAe,IAAI,gBACnB,EAAsC,CAC1C,GAAG,EACH,OAAQ,YAAY,IAAI,CAAC,EAAQ,OAAQ,EAAa,MAAM,CAAC,CAC/D,EACI,EAAe,EACnB,GAAI,CACF,KAAO,EAAQ,EAAM,QAAU,EAAQ,OAAS,GACjC,EAAM,KACN,IAAA,IAF8C,CAG3D,IAAM,EAAY,EAClB,GAAI,EAAU,OAAS,WAAY,MACnC,IAAM,EAAO,MAAM,EAAM,EAAgB,EAAW,EAAM,CAAQ,EAClE,GAAc,KAAK,WACnB,GAAY,IAAK,WAAa,IAC9B,EAAQ,KAAK,CAAI,EACjB,IACA,IAAM,EAAW,EAAM,GACnB,IAAa,IAAA,IAAa,EAAQ,OAAS,IAC7C,EAAe,EAAQ,EAAgB,CAAQ,EAC3C,EAAa,OAAS,aAAY,EAAU,GAEpD,CACF,OAAS,EAAgB,CAKvB,MAFA,EAAa,MAAM,CAAK,EACxB,MAAM,EAAa,EAAS,EAAS,CAAc,EAC7C,CACR,CACA,GAAI,EAAQ,SAAW,EAAG,SAE1B,IAAI,EACA,EAAW,GACf,IAAK,IAAM,KAAQ,EACjB,GAAI,CACF,IAAM,EAAS,MAAM,EAAO,EAAS,EAAM,CAAc,EACzD,EAAQ,KAAK,CAAM,EACf,IAAgB,IAAA,IAAa,CAAC,EAAO,SAAW,EAAO,gBAAe,EAAc,EAAK,KAAK,SACpG,OAAS,EAAgB,CAClB,IAAU,EAAQ,GACvB,EAAW,GACX,EAAa,MAAM,CAAK,CAC1B,CAIF,GAAI,EAAU,MAAM,CACtB,CACA,MAAO,CACL,QAAS,OAAO,OAAO,CAAO,EAC9B,UAAW,IAAgB,IAAA,GAC3B,GAAG,IAAgB,IAAA,GAAY,CAAC,EAAI,CAAE,aAAY,EAClD,aACA,SAAU,EAAU,SACpB,UACF,CACF,CAEA,SAAS,EAAQ,EAA8B,EAAuB,CACpE,OAAO,EAAgB,CACrB,QAAS,EAAQ,QAAS,OAAM,SAAU,EAAQ,SAAU,OAAQ,EAAQ,OAC5E,GAAI,EAAQ,SAAW,IAAA,GAAY,CAAC,EAAI,CAAE,OAAQ,EAAQ,MAAO,EACjE,GAAG,EAAQ,eAAiB,IAAA,GAAY,CAAC,EAAI,CAAE,aAAc,EAAQ,YAAa,EAClF,GAAG,EAAQ,YAAc,IAAA,GAAY,CAAC,EAAI,CAAE,UAAW,EAAQ,SAAU,CAC3E,CAAC,CACH,CAEA,eAAe,EACb,EACA,EACA,EACA,EACe,CACf,GAAM,CAAE,qBAAsB,EACxB,EAAa,EAAQ,QAAQ,IAAI,EAAS,QAAQ,KAAK,QAAQ,EAG/D,EAAgB,GAAY,eAAiB,GAAO,EAAW,UAAY,EAAK,cAChF,EAAW,IAAkB,IAAA,GAAY,IAAI,gBAAgB,CAAC,CAAC,OAAS,YAAY,QAAQ,CAAa,EACzG,EAAS,YAAY,IAAI,CAAC,EAAQ,OAAQ,CAAQ,CAAC,EACnD,EAAkB,CACtB,GAAG,EACH,QAAS,CAAE,GAAG,EAAS,QAAS,SAAQ,mBAAkB,EAC1D,QAAS,CAAE,GAAG,EAAS,QAAS,QAAO,CACzC,EACM,EAAO,EAAgB,QAAQ,KAC/B,EAAkB,CACtB,QAAS,EAAQ,YAAY,QAC7B,OAAQ,EAAa,EACrB,aAAc,EAAQ,YAAY,MACpC,EACM,EAAY,CAAC,EAAQ,OAAO,SAAW,CAAC,EAAO,QACjD,EAAQ,UAAU,CAAI,EACtB,IAAA,GASJ,GARA,EAAQ,QAAQ,OAAO,CAAE,KAAM,YAAa,OAAQ,EAAK,OAAQ,KAAM,EAAK,SAAU,aAAc,EAAK,YAAa,CAAC,EACvH,MAAM,EAAU,EAAS,CAAE,KAAM,aAAc,QAAO,GAAI,EAAI,EAAG,KAAM,gBAAgB,EAAK,WAAY,KAAM,eAAgB,WAAY,CACxI,wBAAyB,eAAgB,mBAAoB,EAAK,SAAU,sBAAuB,EAAK,OACxG,GAAG,IAAc,IAAA,GAAY,CAAC,EAAI,CAAE,qBAAsB,EAAK,CACjE,EAAG,MAAO,EAAK,YAAa,CAAC,EAC7B,MAAM,EAAU,EAAS,CAAE,KAAM,YAAa,OAAM,OAAM,CAAC,EAGvD,EAAQ,OAAO,QAAS,MAAO,CACjC,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EACvB,2CACA,EAAiB,uBACnB,CAAC,CACH,EACA,GAAI,EAAO,QAAS,MAAO,CACzB,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EAAgB,EAAU,CAAa,CAAC,CACnE,EACA,IAAM,EAAa,EAAS,WAAW,CAAI,EAC3C,GAAI,IAAe,IAAA,GAAW,MAAO,CACnC,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAAO,SAAU,GAC/E,QAAS,QAAQ,QAAQ,EAAe,CAAU,CAAC,CACrD,EACA,GAAI,IAAc,IAAA,GAAW,MAAO,CAClC,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAAO,UAAW,GAChF,QAAS,QAAQ,QAAQ,CAAS,CACpC,EAGA,IAAM,EAAS,EAAQ,QAAQ,IAAI,EAAK,QAAQ,CAAC,EAAE,eAAiB,GAC9D,EAAU,EAAS,IAAA,GAAY,EAAS,UAAU,CAAI,EAC5D,GAAI,IAAY,IAAA,GAAW,MAAO,CAChC,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAAO,SAAU,GAC/E,QAAS,QAAQ,QAAQ,EAAe,CAAO,CAAC,CAClD,EACA,IAAM,EAAQ,EAAK,SAAS,IAAI,EAAK,QAAQ,EAG7C,GAAI,IAAU,IAAA,KAAc,GAAU,EAAS,OAAS,aAAc,MAAO,CAC3E,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EACvB,oEACA,EAAwB,aAC1B,CAAC,CACH,EACA,IAAM,EAAS,EAAK,UAAU,QAAQ,CAAM,EAC5C,GAAI,IAAW,IAAA,GAAW,MAAO,CAC/B,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAAO,SAAU,GAC/E,QAAS,QAAQ,QAAQ,EAAe,EAAQ,eAAiB,YAAY,CAAC,CAChF,EAEA,GAAI,CACF,OAAO,MAAM,EAAqB,EAAS,EAAiB,EAAM,EAAO,EAAQ,EAAU,EAAQ,EAAM,EAAO,CAAQ,CAC1H,QAAU,CACR,EAAO,QAAQ,CACjB,CACF,CAEA,eAAe,EACb,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACe,CACf,GAAM,CAAE,gBAAe,qBAAsB,EAC7C,GAAI,EAAQ,OAAO,QAAS,MAAO,CACjC,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EAAY,2CAA4C,EAAiB,uBAAuB,CAAC,CAC5H,EACA,IAAM,EAAuB,EAAkB,EAAmB,EAAS,EAAiB,CAAK,CAAC,EAC9F,EACJ,GAAI,CACF,EAAgB,MAAM,EAAe,EAAsB,CAAM,CACnE,OAAS,EAAgB,CACvB,GAAI,CAAC,EAAO,QAAS,MAAM,EAE3B,GAAI,CAAC,MADiB,EAAkB,EAAsB,CAAiB,EACjE,MAAM,EAAgB,EAAK,SAAU,gBAAiB,EAAmB,CAAK,EAC5F,MAAO,CACL,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EAAgB,EAAU,CAAa,CAAC,CACnE,CACF,CACA,GAAI,EAAc,OAAS,QAAS,MAAO,CACzC,OAAM,QAAO,SAAQ,WAAU,oBAC/B,WAAY,GAAO,QAAS,QAAQ,QAAQ,EAAc,MAAM,CAClE,EACA,GAAI,CACF,IAAM,EAAgB,QAAQ,QAAQ,EAAQ,aAAa,CACzD,KAAM,uBAAwB,OAAM,SAAU,EAAQ,QAAQ,SAAS,EAAG,SAC1E,GAAI,EAAQ,SAAW,IAAA,GAAY,CAAC,EAAI,CAAE,OAAQ,EAAQ,MAAO,CACnE,CAAC,CAAC,EACF,GAAI,CAAE,MAAM,EAAe,EAAe,CAAM,CAAE,OAC3C,EAAgB,CACrB,GAAI,CAAC,EAAO,QAAS,MAAM,EAC3B,GAAI,CAAC,MAAM,EAAkB,EAAe,CAAiB,EAC3D,MAAM,EAAgB,EAAK,SAAU,aAAc,EAAmB,CAAK,CAE/E,CACF,OAAS,EAAgB,CACvB,GAAI,aAAiB,GAAa,EAAM,OAAS,EAAiB,iBAAkB,MAAM,EAC1F,MAAO,CACL,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EAAY,8BAA8B,EAAU,CAAK,IAAK,EAAiB,iBAAiB,CAAC,CAC5H,CACF,CACA,GAAI,EAAO,QAAS,MAAO,CACzB,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EAAgB,EAAU,CAAa,CAAC,CACnE,EACA,IAAM,EAAU,IAAU,IAAA,GACtB,IAAA,GACA,IAAI,EAAW,EAAS,EAAM,EAAO,CAAE,OAAM,QAAO,QAAO,CAAC,EAC5D,IAAY,IAAA,IAAW,EAAqB,EAAc,KAAM,EAAQ,KAAM,GAAU,EAAQ,oBAAoB,CAAM,CAAC,EAC/H,EAAO,QAAQ,EACf,IAAM,EAAe,EAAc,KAAK,KAAK,eAAiB,GAC9D,EAAS,iBAAiB,EAAc,EAAI,EAC5C,IAAM,EAAU,EAA2B,EAAc,IAAI,EAGvD,MAAsB,CAAE,EAAS,iBAAiB,EAAc,EAAK,CAAE,EAE7E,OADA,EAAa,KAAK,EAAS,CAAO,EAC3B,CACL,OAAM,QAAO,SAAQ,WAAU,oBAC/B,WAAY,EAAc,KAAM,WAAY,GAC5C,GAAG,IAAY,IAAA,GAAY,CAAC,EAAI,CAAE,SAAQ,EAC1C,SACF,CACF,CAaA,SAAS,EAAe,EAAgD,CACtE,MAAO,CACL,QAAS,GACT,MAAO,IAAA,GACP,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,EAAY,CAAM,CAAE,CAAC,EAGrD,KAAM,CAAE,SAAU,GAAM,QAAO,CACjC,CACF,CAWA,SAAS,EAAY,EAAmC,CACtD,IAAM,EAAS,4FAEf,OAAQ,EAAR,CACE,IAAK,aACH,MAAO,8HAC8D,EACvE,IAAK,qBACH,MAAO,0JAEsB,EAC/B,IAAK,kBACH,MAAO,6IAEkB,EAC3B,IAAK,SACH,MAAO,8DAAgE,EACzE,IAAK,OACH,MAAO,6DAA+D,EACxE,IAAK,0BACH,MAAO,2HAC+D,EACxE,IAAK,QACH,MAAO,2DAA6D,EACtE,IAAK,iBACH,MAAO,mHACqD,CAChE,CACF,CAEA,eAAe,EACb,EACA,EACA,EACe,CACf,IAAK,IAAM,KAAQ,EACjB,GAAI,CAAE,MAAM,EAAO,EAAS,EAAM,CAAc,CAAE,MAC5C,CAA8E,CAExF,CAEA,eAAe,EACb,EACA,EACA,EAC8B,CAC9B,IAAI,EACA,EACA,EAAW,GACf,GAAI,CACF,IAAI,EACJ,GAAI,CACF,EAAU,MAAM,EAAe,EAAK,QAAS,EAAK,MAAM,CAC1D,OAAS,EAAgB,CACvB,GAAI,CAAC,EAAK,OAAO,QAAS,MAAM,EAEhC,GAAI,CAAC,MADiB,EAAkB,EAAK,QAAS,EAAK,iBAAiB,EAC9D,MAAM,EAAgB,EAAK,KAAK,SAAU,YAAa,EAAK,kBAAmB,CAAK,EAClG,EAAU,EAAgB,EAAK,QAAQ,CACzC,CACA,GAAI,EAAK,UAAY,IAAA,GAAW,CAG9B,GAAI,CAAC,MADgB,EAAK,QAAQ,MAAM,EAAK,iBAAiB,EACjD,MAAM,EAAgB,EAAK,KAAK,SAAU,cAAe,EAAK,kBAAmB,IAAA,EAAS,EACvG,IAAM,EAAmB,EAAK,QAAQ,MACtC,GAAI,IAAY,IAAA,GACd,MAAM,EAAqB,CAAO,IAAM,QACpC,EACA,EAAU,MAAM,EAAU,CAAO,EAAG,EAAU,CAAO,EAAG,CAAE,MAAO,CAAQ,CAAC,CAElF,CACA,IAAI,EAAY,EAChB,GAAI,EAAK,aAAe,IAAA,GAAW,CACjC,IAAM,EAAa,EAAiB,EAAK,WAAY,CAAO,EAC5D,GAAI,CACF,EAAY,MAAM,EAAe,EAAY,EAAK,MAAM,CAC1D,OAAS,EAAgB,CACvB,GAAI,CAAC,EAAK,OAAO,QAAS,MAAM,EAEhC,GAAI,CAAC,MADiB,EAAkB,EAAY,EAAK,iBAAiB,EAC5D,MAAM,EAAgB,EAAK,KAAK,SAAU,eAAgB,EAAK,kBAAmB,CAAK,EACrG,EAAY,EAAgB,EAAK,QAAQ,CAC3C,CACF,CACA,EAAS,EAAgB,MAAM,EAAY,EAAS,EAAM,CAAS,EAAG,CAAc,CACtF,OAAS,EAAgB,CACvB,EAAK,SAAS,MAAM,CAAK,EACzB,EAAW,EAAqB,CAAK,IAAM,QAC3C,EAAQ,EACR,EAAS,EAAgB,EAAY,EAAU,CAAK,EAAG,EAAU,CAAK,CAAC,EAAG,CAAc,CAC1F,CACA,IAAM,EAAU,EAAwB,CAAE,OAAQ,EAAK,KAAK,OAAQ,QAAS,CAAC,GAAG,EAAO,OAAO,EAAG,QAAS,EAAO,OAAQ,CAAC,EAiB3H,GAhBA,EAAQ,QAAQ,OAAO,CAAE,KAAM,cAAe,OAAQ,EAAK,KAAK,OAAQ,UAAS,QAAO,CAAC,EACzF,MAAM,EAAU,EAAS,CAAE,KAAM,cAAe,KAAM,EAAK,KAAM,SAAQ,MAAO,EAAK,MACnF,GAAG,EAAK,WAAa,GAAO,CAAE,SAAU,EAAc,EAAI,CAAC,EAC3D,GAAG,EAAK,YAAc,GAAO,CAAE,UAAW,EAAc,EAAI,CAAC,EAC7D,GAAG,EAAK,WAAa,CAAC,EAAI,CAAE,WAAY,EAAe,CAAE,CAAC,EAC5D,MAAM,EAAU,EAAS,CACvB,KAAM,WAAY,MAAO,EAAK,MAAO,GAAI,EAAI,EAAG,OAAQ,EAAO,QAAU,QAAU,UACnF,OAAQ,EAAO,QAAU,CAAE,MAAO,EAAO,KAAM,EAAI,EAAO,MAC1D,GAAG,EAAO,QAAU,CAAE,MAAO,CAAE,KAAM,YAAa,QAAS,EAAO,MAAM,QAAS,KAAM,EAAO,MAAM,IAAK,CAAE,EAAI,CAAC,CAClH,CAAC,EACG,EAAO,oBAAsB,IAAA,IAE/B,EAAQ,QAAQ,OAAO,CAAE,KAAM,OAAQ,QAAS,EAAkB,CAChE,QAAS,CAAC,GAAG,EAAO,iBAAiB,EAAG,OAAQ,CAAE,KAAM,MAAO,SAAU,QAAQ,EAAK,KAAK,UAAW,CACxG,CAAC,CAAE,CAAC,EAEF,EAAU,MAAM,EACpB,OAAO,CACT,CAkBA,eAAe,EACb,EACA,EACA,EAC8B,CAC9B,IAAM,EAAa,EAAQ,gBACrB,EAAa,EAAQ,QAAQ,IAAI,EAAK,KAAK,QAAQ,CAAC,EAAE,gBACtD,EAAS,IAAe,IAAA,GAC1B,EACA,IAAe,IAAA,GAAY,EAAa,KAAK,IAAI,EAAY,CAAU,EAC3E,GAAI,IAAW,IAAA,GAAW,OAAO,EACjC,IAAM,EAAS,EAAwB,EAAO,OAAO,EACrD,GAAI,GAAU,EAAQ,OAAO,EAK7B,IAAM,EAAQ,EAAO,QAAQ,OAAO,GAAS,EAAM,OAAS,MAAM,EAClE,GAAI,EAAM,SAAW,EAAG,OAAO,EAC/B,IAAM,EAAO,EAAM,IAAI,GAAS,EAAM,IAAI,CAAC,CAAC,KAAK;CAAI,EAC/C,EAAS,EAAQ,gBAAkB,OACnC,EAAQ,EAAQ,WAEtB,GAAI,IAAW,YAAc,IAAU,IAAA,GACrC,GAAI,CAGF,IAAM,EAAS,MAAM,EAAM,KAAK,EAAM,CACpC,SAAU,EAAK,KAAK,SAAU,OAAQ,OAAO,EAAK,KAAK,MAAM,CAC/D,CAAC,EAED,OAAO,EAAY,EAAQ,GADX,EAAgB,EAAM,KAAK,IAAI,EAAG,KAAK,MAAM,EAAS,GAAI,CAAC,CACvC,EAAE,6CAC/B,OAAO,CAAM,EAAE,8BAA8B,OAAO,EAAO,KAAK,EAAE,oBAClE,EAAO,UAAU,GAAI,CAC1B,cAAe,CAAE,QAAS,EAAO,QAAS,MAAO,EAAO,MAAO,gBAAiB,CAAO,CACzF,CAAC,CACH,MAAQ,CAGR,CAGF,IAAM,EAAY,EAAuB,EAAM,CAAM,EACrD,OAAO,EAAY,EAAQ,EAAU,KACjC,mBAAmB,OAAO,EAAU,cAAc,EAAE,gDACvC,OAAO,CAAM,EAAE,gKACiF,CAC/G,gBAAiB,CAAE,gBAAiB,EAAU,eAAgB,QAAO,CACvE,CAAC,CACH,CASA,SAAS,EACP,EACA,EACA,EACqB,CAIrB,IAAI,EAAS,GACP,EAA0B,CAAC,EACjC,IAAK,IAAM,KAAS,EAAO,QAAS,CAClC,GAAI,EAAM,OAAS,OAAQ,CAAE,EAAQ,KAAK,CAAK,EAAG,QAAS,CACvD,IACJ,EAAS,GACT,EAAQ,KAAK,CAAE,KAAM,OAAQ,MAAK,CAAC,EACrC,CAGA,MAAO,CAAE,GAAG,EAAQ,UAAS,KAAM,CAAE,GAAG,EAAO,KAAM,GAAG,CAAK,CAAE,CACjE,CAEA,SAAS,EAAc,EAAwC,CAC7D,GAAI,OAAO,EAAK,QAAW,UAAY,EAAK,OAAO,SAAW,GACzD,OAAO,EAAK,UAAa,UAAY,EAAK,SAAS,SAAW,GAC9D,OAAO,EAAK,cAAiB,SAChC,MAAU,UAAU,6DAA6D,EAEnF,OAAO,EAAe,CAAI,CAC5B,CAEA,SAAS,EAAgB,EAAuB,EAA6C,CAC3F,IAAM,EAAQ,IAAkB,IAAA,GAAY,wBAA0B,GAAG,EAAc,eACvF,OAAO,EAAS,QACZ,EACE,yBAAyB,IACzB,EAAiB,OACnB,EACA,EAAY,yBAA0B,EAAiB,OAAO,CACpE,CAEA,SAAS,EAAgB,EAAe,EAAsB,CAC5D,GAAI,CAAC,OAAO,UAAU,CAAK,GAAK,EAAQ,EAAG,MAAU,WAAW,GAAG,EAAK,4BAA4B,EACpG,OAAO,CACT"}
@@ -1,2 +1,2 @@
1
- const e=Object.freeze({maxSteps:16,maxToolCalls:64,toolBudgetRemindAt:Object.freeze([32,16,6]),onExhausted:`force-final-answer`,maxConsecutiveToolErrors:8,repeatToolWarningAt:3,repeatToolLimit:6,toolCycleWarningAt:2,toolCycleLimit:3,maxToolCycleLength:4,maxTotalTokens:`auto`,finalReportReserveTokens:0,maxParallel:8,maxToolResultBytes:4194304,maxToolResultTokens:1e4,toolResultOverflow:`auto`,maxToolDurationMs:6e5,toolTeardownTimeoutMs:3e4});function t(t){let n={...e,...t};if(n.maxTotalTokens!==`auto`&&(!Number.isSafeInteger(n.maxTotalTokens)||n.maxTotalTokens<1))throw RangeError(`maxTotalTokens must be a positive safe integer or 'auto'`);if(!Number.isSafeInteger(n.finalReportReserveTokens)||n.finalReportReserveTokens<0||n.maxTotalTokens!==`auto`&&n.finalReportReserveTokens>=n.maxTotalTokens)throw RangeError(`finalReportReserveTokens must be a non-negative safe integer below maxTotalTokens`);if(n.maxSteps!==`auto`&&(!Number.isSafeInteger(n.maxSteps)||n.maxSteps<1))throw RangeError(`maxSteps must be a positive safe integer or 'auto'`);for(let e of[`maxToolCalls`,`maxConsecutiveToolErrors`,`repeatToolWarningAt`,`repeatToolLimit`,`toolCycleWarningAt`,`toolCycleLimit`,`maxToolCycleLength`,`maxParallel`,`maxToolResultBytes`,`maxToolResultTokens`,`maxToolDurationMs`,`toolTeardownTimeoutMs`])if(!Number.isSafeInteger(n[e])||n[e]<1)throw RangeError(`${e} must be a positive safe integer`);if(![`auto`,`truncate`,`spill`].includes(n.toolResultOverflow))throw RangeError(`toolResultOverflow must be 'auto', 'truncate', or 'spill'`);if(![`force-final-answer`,`stop`,`continue`].includes(n.onExhausted))throw RangeError(`onExhausted must be 'force-final-answer', 'stop', or 'continue'`);if(n.repeatToolLimit<n.repeatToolWarningAt)throw RangeError(`repeatToolLimit must be >= repeatToolWarningAt`);if(n.toolCycleLimit<n.toolCycleWarningAt)throw RangeError(`toolCycleLimit must be >= toolCycleWarningAt`);if(!Array.isArray(n.toolBudgetRemindAt)||n.toolBudgetRemindAt.some(e=>!Number.isSafeInteger(e)||e<1))throw RangeError(`toolBudgetRemindAt must contain only positive safe integers`);return Object.freeze(n)}function n(e,t){if(!Number.isFinite(e)||e<=0)throw RangeError(`${t} must be a positive finite number`);return e}function r(e,t){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`${t} must be a positive safe integer`);return e}function i(e){let t=Object.freeze({provider:e.config.provider,model:e.config.model,...e.config.reasoningEffort===void 0?{}:{reasoningEffort:e.config.reasoningEffort},...e.config.temperature===void 0?{}:{temperature:e.config.temperature},...e.config.topP===void 0?{}:{topP:e.config.topP},...e.config.maxTokens===void 0?{}:{maxTokens:e.config.maxTokens},...e.config.contextWindow===void 0?{}:{contextWindow:e.config.contextWindow},...e.config.inputModalities===void 0?{}:{inputModalities:Object.freeze([...e.config.inputModalities])},...e.config.stop===void 0?{}:{stop:Object.freeze([...e.config.stop])}});return Object.freeze({...e,config:t,...e.bounds===void 0?{}:{bounds:Object.freeze({...e.bounds})},...e.nativeTools===void 0?{}:{nativeTools:Object.freeze([...e.nativeTools])},...e.interceptors===void 0?{}:{interceptors:Object.freeze([...e.interceptors])},...e.hooks===void 0?{}:{hooks:Object.freeze({...e.hooks})},...e.trace===void 0?{}:{trace:Object.freeze({...e.trace})}})}export{e as DEFAULT_BOUNDS,n as positiveFinite,r as positiveSafeInteger,t as resolveBounds,i as snapshotRunTurnOptions};
1
+ const e=Object.freeze({maxSteps:16,maxToolCalls:64,toolBudgetRemindAt:Object.freeze([32,16,6]),onExhausted:`force-final-answer`,maxConsecutiveToolErrors:8,repeatToolWarningAt:3,repeatToolLimit:6,toolCycleWarningAt:2,toolCycleLimit:3,maxToolCycleLength:4,maxTotalTokens:`auto`,finalReportReserveTokens:0,finalizeSteps:0,maxTurnDurationMs:`auto`,maxParallel:8,maxToolResultBytes:4194304,maxToolResultTokens:1e4,toolResultOverflow:`auto`,maxToolDurationMs:6e5,toolTeardownTimeoutMs:3e4});function t(t){let n={...e,...t};if(n.maxTotalTokens!==`auto`&&(!Number.isSafeInteger(n.maxTotalTokens)||n.maxTotalTokens<1))throw RangeError(`maxTotalTokens must be a positive safe integer or 'auto'`);if(!Number.isSafeInteger(n.finalReportReserveTokens)||n.finalReportReserveTokens<0||n.maxTotalTokens!==`auto`&&n.finalReportReserveTokens>=n.maxTotalTokens)throw RangeError(`finalReportReserveTokens must be a non-negative safe integer below maxTotalTokens`);if(n.maxTurnDurationMs!==`auto`&&(!Number.isSafeInteger(n.maxTurnDurationMs)||n.maxTurnDurationMs<1))throw RangeError(`maxTurnDurationMs must be a positive safe integer or 'auto'`);if(!Number.isSafeInteger(n.finalizeSteps)||n.finalizeSteps<0||n.finalizeSteps>8)throw RangeError(`finalizeSteps must be a safe integer from 0 to 8`);if(n.maxSteps!==`auto`&&(!Number.isSafeInteger(n.maxSteps)||n.maxSteps<1))throw RangeError(`maxSteps must be a positive safe integer or 'auto'`);for(let e of[`maxToolCalls`,`maxConsecutiveToolErrors`,`repeatToolWarningAt`,`repeatToolLimit`,`toolCycleWarningAt`,`toolCycleLimit`,`maxToolCycleLength`,`maxParallel`,`maxToolResultBytes`,`maxToolResultTokens`,`maxToolDurationMs`,`toolTeardownTimeoutMs`])if(!Number.isSafeInteger(n[e])||n[e]<1)throw RangeError(`${e} must be a positive safe integer`);if(![`auto`,`truncate`,`spill`].includes(n.toolResultOverflow))throw RangeError(`toolResultOverflow must be 'auto', 'truncate', or 'spill'`);if(![`force-final-answer`,`stop`,`continue`].includes(n.onExhausted))throw RangeError(`onExhausted must be 'force-final-answer', 'stop', or 'continue'`);if(n.repeatToolLimit<n.repeatToolWarningAt)throw RangeError(`repeatToolLimit must be >= repeatToolWarningAt`);if(n.toolCycleLimit<n.toolCycleWarningAt)throw RangeError(`toolCycleLimit must be >= toolCycleWarningAt`);if(!Array.isArray(n.toolBudgetRemindAt)||n.toolBudgetRemindAt.some(e=>!Number.isSafeInteger(e)||e<1))throw RangeError(`toolBudgetRemindAt must contain only positive safe integers`);return Object.freeze(n)}function n(e,t){if(!Number.isFinite(e)||e<=0)throw RangeError(`${t} must be a positive finite number`);return e}function r(e,t){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`${t} must be a positive safe integer`);return e}function i(e){let t=Object.freeze({provider:e.config.provider,model:e.config.model,...e.config.reasoningEffort===void 0?{}:{reasoningEffort:e.config.reasoningEffort},...e.config.temperature===void 0?{}:{temperature:e.config.temperature},...e.config.topP===void 0?{}:{topP:e.config.topP},...e.config.maxTokens===void 0?{}:{maxTokens:e.config.maxTokens},...e.config.contextWindow===void 0?{}:{contextWindow:e.config.contextWindow},...e.config.inputModalities===void 0?{}:{inputModalities:Object.freeze([...e.config.inputModalities])},...e.config.stop===void 0?{}:{stop:Object.freeze([...e.config.stop])}});return Object.freeze({...e,config:t,...e.bounds===void 0?{}:{bounds:Object.freeze({...e.bounds})},...e.nativeTools===void 0?{}:{nativeTools:Object.freeze([...e.nativeTools])},...e.interceptors===void 0?{}:{interceptors:Object.freeze([...e.interceptors])},...e.hooks===void 0?{}:{hooks:Object.freeze({...e.hooks})},...e.trace===void 0?{}:{trace:Object.freeze({...e.trace})}})}export{e as DEFAULT_BOUNDS,n as positiveFinite,r as positiveSafeInteger,t as resolveBounds,i as snapshotRunTurnOptions};
2
2
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","names":[],"sources":["../../../../src/agent/loop/turn/config.ts"],"sourcesContent":["import type { TurnBounds } from '../types.ts'\nimport { type RunTurnOptions } from './types.ts'\n\nexport const DEFAULT_BOUNDS: TurnBounds = Object.freeze({\n maxSteps: 16,\n maxToolCalls: 64,\n // Halfway, a quarter left, and nearly gone. Three notices, spaced so the\n // first is still early enough to change the plan and the last is impossible\n // to ignore.\n toolBudgetRemindAt: Object.freeze([32, 16, 6]),\n onExhausted: 'force-final-answer',\n maxConsecutiveToolErrors: 8,\n repeatToolWarningAt: 3,\n repeatToolLimit: 6,\n toolCycleWarningAt: 2,\n toolCycleLimit: 3,\n maxToolCycleLength: 4,\n maxTotalTokens: 'auto',\n finalReportReserveTokens: 0,\n maxParallel: 8,\n maxToolResultBytes: 4 * 1024 * 1024,\n // Codex's own default for a shell call. Large enough for a real build log,\n // small enough that a handful of them cannot spend a context window.\n maxToolResultTokens: 10_000,\n toolResultOverflow: 'auto',\n maxToolDurationMs: 10 * 60_000,\n toolTeardownTimeoutMs: 30_000,\n})\nexport function resolveBounds(input: Partial<TurnBounds> | undefined): TurnBounds {\n const bounds = { ...DEFAULT_BOUNDS, ...input }\n if (bounds.maxTotalTokens !== 'auto' && (!Number.isSafeInteger(bounds.maxTotalTokens) || bounds.maxTotalTokens < 1)) {\n throw new RangeError(\"maxTotalTokens must be a positive safe integer or 'auto'\")\n }\n if (!Number.isSafeInteger(bounds.finalReportReserveTokens) || bounds.finalReportReserveTokens < 0\n || (bounds.maxTotalTokens !== 'auto' && bounds.finalReportReserveTokens >= bounds.maxTotalTokens)) {\n throw new RangeError('finalReportReserveTokens must be a non-negative safe integer below maxTotalTokens')\n }\n if (bounds.maxSteps !== 'auto' && (!Number.isSafeInteger(bounds.maxSteps) || bounds.maxSteps < 1)) {\n throw new RangeError(\"maxSteps must be a positive safe integer or 'auto'\")\n }\n for (const key of [\n 'maxToolCalls', 'maxConsecutiveToolErrors', 'repeatToolWarningAt',\n 'repeatToolLimit', 'toolCycleWarningAt', 'toolCycleLimit', 'maxToolCycleLength',\n 'maxParallel', 'maxToolResultBytes', 'maxToolResultTokens',\n 'maxToolDurationMs',\n 'toolTeardownTimeoutMs',\n ] as const) {\n if (!Number.isSafeInteger(bounds[key]) || bounds[key] < 1) throw new RangeError(`${key} must be a positive safe integer`)\n }\n if (!['auto', 'truncate', 'spill'].includes(bounds.toolResultOverflow)) {\n throw new RangeError(\"toolResultOverflow must be 'auto', 'truncate', or 'spill'\")\n }\n if (!['force-final-answer', 'stop', 'continue'].includes(bounds.onExhausted)) {\n throw new RangeError(\"onExhausted must be 'force-final-answer', 'stop', or 'continue'\")\n }\n if (bounds.repeatToolLimit < bounds.repeatToolWarningAt) throw new RangeError('repeatToolLimit must be >= repeatToolWarningAt')\n if (bounds.toolCycleLimit < bounds.toolCycleWarningAt) {\n throw new RangeError('toolCycleLimit must be >= toolCycleWarningAt')\n }\n if (!Array.isArray(bounds.toolBudgetRemindAt)\n || bounds.toolBudgetRemindAt.some(value => !Number.isSafeInteger(value) || value < 1)) {\n // Out-of-range thresholds are dropped rather than rejected, but a\n // fractional or negative one is a mistake worth naming: silently ignoring\n // `0.25` would leave a host believing it had asked for a reminder.\n throw new RangeError('toolBudgetRemindAt must contain only positive safe integers')\n }\n return Object.freeze(bounds)\n}\nexport function positiveFinite(value: number, name: string): number {\n if (!Number.isFinite(value) || value <= 0) throw new RangeError(`${name} must be a positive finite number`)\n return value\n}\nexport function positiveSafeInteger(value: number, name: string): number {\n if (!Number.isSafeInteger(value) || value < 1) throw new RangeError(`${name} must be a positive safe integer`)\n return value\n}\nexport function snapshotRunTurnOptions(options: RunTurnOptions): RunTurnOptions {\n const config = Object.freeze({\n provider: options.config.provider,\n model: options.config.model,\n ...(options.config.reasoningEffort === undefined ? {} : { reasoningEffort: options.config.reasoningEffort }),\n ...(options.config.temperature === undefined ? {} : { temperature: options.config.temperature }),\n ...(options.config.topP === undefined ? {} : { topP: options.config.topP }),\n ...(options.config.maxTokens === undefined ? {} : { maxTokens: options.config.maxTokens }),\n ...(options.config.contextWindow === undefined ? {} : { contextWindow: options.config.contextWindow }),\n ...(options.config.inputModalities === undefined ? {} : {\n inputModalities: Object.freeze([...options.config.inputModalities]),\n }),\n ...(options.config.stop === undefined ? {} : { stop: Object.freeze([...options.config.stop]) }),\n })\n return Object.freeze({\n ...options,\n config,\n ...(options.bounds === undefined ? {} : { bounds: Object.freeze({ ...options.bounds }) }),\n ...(options.nativeTools === undefined ? {} : { nativeTools: Object.freeze([...options.nativeTools]) }),\n ...(options.interceptors === undefined ? {} : { interceptors: Object.freeze([...options.interceptors]) }),\n ...(options.hooks === undefined ? {} : { hooks: Object.freeze({ ...options.hooks }) }),\n ...(options.trace === undefined ? {} : { trace: Object.freeze({ ...options.trace }) }),\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config.js","names":[],"sources":["../../../../src/agent/loop/turn/config.ts"],"sourcesContent":["import type { TurnBounds } from '../types.ts'\nimport { type RunTurnOptions } from './types.ts'\n\nexport const DEFAULT_BOUNDS: TurnBounds = Object.freeze({\n maxSteps: 16,\n maxToolCalls: 64,\n // Halfway, a quarter left, and nearly gone. Three notices, spaced so the\n // first is still early enough to change the plan and the last is impossible\n // to ignore.\n toolBudgetRemindAt: Object.freeze([32, 16, 6]),\n onExhausted: 'force-final-answer',\n maxConsecutiveToolErrors: 8,\n repeatToolWarningAt: 3,\n repeatToolLimit: 6,\n toolCycleWarningAt: 2,\n toolCycleLimit: 3,\n maxToolCycleLength: 4,\n maxTotalTokens: 'auto',\n finalReportReserveTokens: 0,\n finalizeSteps: 0,\n maxTurnDurationMs: 'auto',\n maxParallel: 8,\n maxToolResultBytes: 4 * 1024 * 1024,\n // Codex's own default for a shell call. Large enough for a real build log,\n // small enough that a handful of them cannot spend a context window.\n maxToolResultTokens: 10_000,\n toolResultOverflow: 'auto',\n maxToolDurationMs: 10 * 60_000,\n toolTeardownTimeoutMs: 30_000,\n})\nexport function resolveBounds(input: Partial<TurnBounds> | undefined): TurnBounds {\n const bounds = { ...DEFAULT_BOUNDS, ...input }\n if (bounds.maxTotalTokens !== 'auto' && (!Number.isSafeInteger(bounds.maxTotalTokens) || bounds.maxTotalTokens < 1)) {\n throw new RangeError(\"maxTotalTokens must be a positive safe integer or 'auto'\")\n }\n if (!Number.isSafeInteger(bounds.finalReportReserveTokens) || bounds.finalReportReserveTokens < 0\n || (bounds.maxTotalTokens !== 'auto' && bounds.finalReportReserveTokens >= bounds.maxTotalTokens)) {\n throw new RangeError('finalReportReserveTokens must be a non-negative safe integer below maxTotalTokens')\n }\n if (bounds.maxTurnDurationMs !== 'auto' && (!Number.isSafeInteger(bounds.maxTurnDurationMs) || bounds.maxTurnDurationMs < 1)) {\n throw new RangeError(\"maxTurnDurationMs must be a positive safe integer or 'auto'\")\n }\n if (!Number.isSafeInteger(bounds.finalizeSteps) || bounds.finalizeSteps < 0 || bounds.finalizeSteps > 8) {\n throw new RangeError('finalizeSteps must be a safe integer from 0 to 8')\n }\n if (bounds.maxSteps !== 'auto' && (!Number.isSafeInteger(bounds.maxSteps) || bounds.maxSteps < 1)) {\n throw new RangeError(\"maxSteps must be a positive safe integer or 'auto'\")\n }\n for (const key of [\n 'maxToolCalls', 'maxConsecutiveToolErrors', 'repeatToolWarningAt',\n 'repeatToolLimit', 'toolCycleWarningAt', 'toolCycleLimit', 'maxToolCycleLength',\n 'maxParallel', 'maxToolResultBytes', 'maxToolResultTokens',\n 'maxToolDurationMs',\n 'toolTeardownTimeoutMs',\n ] as const) {\n if (!Number.isSafeInteger(bounds[key]) || bounds[key] < 1) throw new RangeError(`${key} must be a positive safe integer`)\n }\n if (!['auto', 'truncate', 'spill'].includes(bounds.toolResultOverflow)) {\n throw new RangeError(\"toolResultOverflow must be 'auto', 'truncate', or 'spill'\")\n }\n if (!['force-final-answer', 'stop', 'continue'].includes(bounds.onExhausted)) {\n throw new RangeError(\"onExhausted must be 'force-final-answer', 'stop', or 'continue'\")\n }\n if (bounds.repeatToolLimit < bounds.repeatToolWarningAt) throw new RangeError('repeatToolLimit must be >= repeatToolWarningAt')\n if (bounds.toolCycleLimit < bounds.toolCycleWarningAt) {\n throw new RangeError('toolCycleLimit must be >= toolCycleWarningAt')\n }\n if (!Array.isArray(bounds.toolBudgetRemindAt)\n || bounds.toolBudgetRemindAt.some(value => !Number.isSafeInteger(value) || value < 1)) {\n // Out-of-range thresholds are dropped rather than rejected, but a\n // fractional or negative one is a mistake worth naming: silently ignoring\n // `0.25` would leave a host believing it had asked for a reminder.\n throw new RangeError('toolBudgetRemindAt must contain only positive safe integers')\n }\n return Object.freeze(bounds)\n}\nexport function positiveFinite(value: number, name: string): number {\n if (!Number.isFinite(value) || value <= 0) throw new RangeError(`${name} must be a positive finite number`)\n return value\n}\nexport function positiveSafeInteger(value: number, name: string): number {\n if (!Number.isSafeInteger(value) || value < 1) throw new RangeError(`${name} must be a positive safe integer`)\n return value\n}\nexport function snapshotRunTurnOptions(options: RunTurnOptions): RunTurnOptions {\n const config = Object.freeze({\n provider: options.config.provider,\n model: options.config.model,\n ...(options.config.reasoningEffort === undefined ? {} : { reasoningEffort: options.config.reasoningEffort }),\n ...(options.config.temperature === undefined ? {} : { temperature: options.config.temperature }),\n ...(options.config.topP === undefined ? {} : { topP: options.config.topP }),\n ...(options.config.maxTokens === undefined ? {} : { maxTokens: options.config.maxTokens }),\n ...(options.config.contextWindow === undefined ? {} : { contextWindow: options.config.contextWindow }),\n ...(options.config.inputModalities === undefined ? {} : {\n inputModalities: Object.freeze([...options.config.inputModalities]),\n }),\n ...(options.config.stop === undefined ? {} : { stop: Object.freeze([...options.config.stop]) }),\n })\n return Object.freeze({\n ...options,\n config,\n ...(options.bounds === undefined ? {} : { bounds: Object.freeze({ ...options.bounds }) }),\n ...(options.nativeTools === undefined ? {} : { nativeTools: Object.freeze([...options.nativeTools]) }),\n ...(options.interceptors === undefined ? {} : { interceptors: Object.freeze([...options.interceptors]) }),\n ...(options.hooks === undefined ? {} : { hooks: Object.freeze({ ...options.hooks }) }),\n ...(options.trace === undefined ? {} : { trace: Object.freeze({ ...options.trace }) }),\n })\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=new WeakMap;function t(t,n){n===void 0?e.delete(t):e.set(t,n)}function n(t,n){e.get(t)?.(n)}const r=new WeakMap;function i(e,t){t===void 0?r.delete(e):r.set(e,t)}function a(e){return r.get(e)?.()??!1}export{t as bindModelRequestBoundary,i as bindQueuedInput,a as deliverQueuedInput,n as observeModelRequestBoundary};
1
+ const e=new WeakMap;function t(t,n){n===void 0?e.delete(t):e.set(t,n)}function n(t,n){e.get(t)?.(n)}const r=new WeakMap,i=new WeakMap;function a(e,t,n){t===void 0?r.delete(e):r.set(e,t),n===void 0?i.delete(e):i.set(e,n)}function o(e){return i.get(e)?.()??!1}function s(e){return r.get(e)?.()??!1}export{t as bindModelRequestBoundary,a as bindQueuedInput,s as deliverQueuedInput,o as hasQueuedInput,n as observeModelRequestBoundary};
2
2
  //# sourceMappingURL=model-request-boundary.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"model-request-boundary.js","names":[],"sources":["../../../../src/agent/loop/turn/model-request-boundary.ts"],"sourcesContent":["import type { History } from '../../history/history.ts'\n\n// Session bookkeeping is separate from user hooks and their operation ledger.\nconst observers = new WeakMap<History, (inFlight: boolean) => void>()\n\nexport function bindModelRequestBoundary(history: History, observer?: (inFlight: boolean) => void): void {\n if (observer === undefined) observers.delete(history)\n else observers.set(history, observer)\n}\n\nexport function observeModelRequestBoundary(history: History, inFlight: boolean): void {\n observers.get(history)?.(inFlight)\n}\n\n// Input the owning session queued while a final answer was being written.\nconst queuedInput = new WeakMap<History, () => boolean>()\n\n/** Let a session hand the loop input it is still holding when a turn would end. */\nexport function bindQueuedInput(history: History, deliver?: () => boolean): void {\n if (deliver === undefined) queuedInput.delete(history)\n else queuedInput.set(history, deliver)\n}\n\n/**\n * Append whatever input the owning session is holding, and say whether any\n * was. Called by the loop only when the turn is free to continue, so the\n * input is answered in this run rather than left behind for the next one.\n */\nexport function deliverQueuedInput(history: History): boolean {\n return queuedInput.get(history)?.() ?? false\n}\n"],"mappings":"AAGA,MAAM,EAAY,IAAI,QAEtB,SAAgB,EAAyB,EAAkB,EAA8C,CACnG,IAAa,IAAA,GAAW,EAAU,OAAO,CAAO,EAC/C,EAAU,IAAI,EAAS,CAAQ,CACtC,CAEA,SAAgB,EAA4B,EAAkB,EAAyB,CACrF,EAAU,IAAI,CAAO,CAAC,GAAG,CAAQ,CACnC,CAGA,MAAM,EAAc,IAAI,QAGxB,SAAgB,EAAgB,EAAkB,EAA+B,CAC3E,IAAY,IAAA,GAAW,EAAY,OAAO,CAAO,EAChD,EAAY,IAAI,EAAS,CAAO,CACvC,CAOA,SAAgB,EAAmB,EAA2B,CAC5D,OAAO,EAAY,IAAI,CAAO,CAAC,GAAG,GAAK,EACzC"}
1
+ {"version":3,"file":"model-request-boundary.js","names":[],"sources":["../../../../src/agent/loop/turn/model-request-boundary.ts"],"sourcesContent":["import type { History } from '../../history/history.ts'\n\n// Session bookkeeping is separate from user hooks and their operation ledger.\nconst observers = new WeakMap<History, (inFlight: boolean) => void>()\n\nexport function bindModelRequestBoundary(history: History, observer?: (inFlight: boolean) => void): void {\n if (observer === undefined) observers.delete(history)\n else observers.set(history, observer)\n}\n\nexport function observeModelRequestBoundary(history: History, inFlight: boolean): void {\n observers.get(history)?.(inFlight)\n}\n\n// Input the owning session queued while a final answer was being written.\nconst queuedInput = new WeakMap<History, () => boolean>()\nconst queuedInputPending = new WeakMap<History, () => boolean>()\n\n/**\n * Let a session hand the loop input it is still holding when a turn would end,\n * and let the loop ask, without taking it, whether a person's input is held.\n */\nexport function bindQueuedInput(history: History, deliver?: () => boolean, pending?: () => boolean): void {\n if (deliver === undefined) queuedInput.delete(history)\n else queuedInput.set(history, deliver)\n if (pending === undefined) queuedInputPending.delete(history)\n else queuedInputPending.set(history, pending)\n}\n\n/** Whether the owning session holds input that would extend this run. */\nexport function hasQueuedInput(history: History): boolean {\n return queuedInputPending.get(history)?.() ?? false\n}\n\n/**\n * Append whatever input the owning session is holding, and say whether any\n * was. Called by the loop only when the turn is free to continue, so the\n * input is answered in this run rather than left behind for the next one.\n */\nexport function deliverQueuedInput(history: History): boolean {\n return queuedInput.get(history)?.() ?? false\n}\n"],"mappings":"AAGA,MAAM,EAAY,IAAI,QAEtB,SAAgB,EAAyB,EAAkB,EAA8C,CACnG,IAAa,IAAA,GAAW,EAAU,OAAO,CAAO,EAC/C,EAAU,IAAI,EAAS,CAAQ,CACtC,CAEA,SAAgB,EAA4B,EAAkB,EAAyB,CACrF,EAAU,IAAI,CAAO,CAAC,GAAG,CAAQ,CACnC,CAGA,MAAM,EAAc,IAAI,QAClB,EAAqB,IAAI,QAM/B,SAAgB,EAAgB,EAAkB,EAAyB,EAA+B,CACpG,IAAY,IAAA,GAAW,EAAY,OAAO,CAAO,EAChD,EAAY,IAAI,EAAS,CAAO,EACjC,IAAY,IAAA,GAAW,EAAmB,OAAO,CAAO,EACvD,EAAmB,IAAI,EAAS,CAAO,CAC9C,CAGA,SAAgB,EAAe,EAA2B,CACxD,OAAO,EAAmB,IAAI,CAAO,CAAC,GAAG,GAAK,EAChD,CAOA,SAAgB,EAAmB,EAA2B,CAC5D,OAAO,EAAY,IAAI,CAAO,CAAC,GAAG,GAAK,EACzC"}
@@ -1,2 +1,2 @@
1
- import{createSpanId as e}from"../../../observation/context.js";import{MODEL_ERROR_CODES as t}from"../../../errors/model-error.js";import{BlockAssembler as n}from"../../../stream/assembler.js";import{normalizeToolPairing as r}from"../../history/normalize.js";import{observeModelRequestBoundary as i}from"./model-request-boundary.js";import{stepProjectionSources as a}from"./step-projection.js";import{positiveFinite as o,positiveSafeInteger as s}from"./config.js";import{messageOf as c,modelAbortedFinish as ee,modelFailureFinish as l,now as u,serializedBytes as te}from"./common.js";import{StreamAbortError as ne,closeIterator as re,nextWithAbort as ie}from"./cancellation.js";import{runOptionalHook as d}from"./hooks.js";import{classifyTextPhases as f,contentTiming as p,createAssistant as ae,dropDuplicateToolCalls as oe,invalidHostToolCall as se,recentToolResultIds as ce,systemText as le,textOf as m}from"./content.js";import{UNCHANGED_ANSWER_MARKER as ue}from"../control-text.js";import{validateStreamChunk as de}from"./validation.js";import{accountingUsageStop as fe}from"./usage-stop.js";function h(e){let t=e.replace(/\s+/gu,` `).trim();return t.length>600?`${t.slice(0,600)}…`:t}function g(e){let t=[],n=[];for(let r of e.content)r.type===`text`||r.type===`reasoning`?t.push(r.text):r.type===`tool-call`?n.push(`${r.name}(${h(r.arguments)})`):r.type===`tool-result`?t.push(m(r.content)):t.push(`[${r.type}]`);return{role:e.role,producer:e.source.kind,...t.length===0?{}:{text:h(t.join(` `))},...n.length===0?{}:{toolCalls:n}}}function pe(e){let t=e.messages??[],n=Math.max(0,t.length-8);return{messageCount:t.length,...t.at(-1)===void 0?{}:{lastMessageId:t.at(-1)?.id},...e.system===void 0?{}:{system:h(e.system)},...e.tools===void 0||e.tools.length===0?{}:{tools:e.tools.map(e=>e.name)},...e.toolChoice===void 0?{}:{toolChoice:e.toolChoice},...n===0?{}:{earlierMessagesOmitted:n},messages:t.slice(-8).map(g)}}async function _(h,g,_,he,ge,v,y,b){let x=b===`forced-final`,S=b===`final`||x,C={traceId:ge.traceId,spanId:e(),parentSpanId:ge.spanId},w=r(h.history.messages()),_e=w,T=ce(h.history),ve=h.history.generation(),ye=h.history.entries().length,E=()=>{let e=fe(h.accounting);if(!(e===void 0&&!g.aborted))return{trace:C,finish:g.aborted?{kind:`aborted`,failure:{code:`ABORTED`,message:c(g.reason)}}:{kind:`stop`},calls:[],afterToolCallIds:T,timing:p(!1,T.length>0),...e?.kind===`error`?{usageRequired:!0}:{},...e?.kind===`usage-unavailable`?{usageUnavailable:!0}:{}}},D=E();if(D!==void 0)return D;let O=await d(h.hooks?.beforeStep,[{turn:v,step:y,messages:w,snapshot:h.history.snapshot(),signal:g,...h.logger===void 0?{}:{logger:h.logger},emit:he}],h,g,`beforeStep`),k=E();if(k!==void 0)return k;if(O?.kind===`reject`)return{trace:C,finish:{kind:`error`,failure:{message:O.reason,code:`STEP_REJECTED`}},calls:[],afterToolCallIds:T,timing:p(!1,T.length>0)};if((h.history.generation()!==ve||h.history.entries().length!==ye)&&(w=r(h.history.messages())),O?.messages!==void 0){let e=a(O)??new Set(_e.map(e=>e.id)),t=new Set(O.messages.map(e=>e.id)),n=w.filter(n=>!e.has(n.id)&&!t.has(n.id)),i=new Set(w.map(e=>e.id)),o=[...e].some(e=>!i.has(e)),s=new Map(O.messages.map(e=>[e.id,e]));w=r(o?[...O.messages.filter(t=>!e.has(t.id)&&!i.has(t.id)&&t.source.kind===`app`),...w.flatMap(t=>{let n=s.get(t.id);return n===void 0?e.has(t.id)?[]:[t]:[n]})]:[...O.messages,...n])}O?.prepend!==void 0&&(w=Object.freeze([...O.prepend,...w]));let A=le(h,x),j=[...h.tools?.schemas()??[],...h.nativeTools??[]],M=!S&&h.outputFormat?.type===`json_schema`?{type:`text`}:h.outputFormat,N={...h.config,messages:w,...A.length===0?{}:{system:A},...j.length===0?{}:{tools:j},...S&&j.length>0?{toolChoice:`none`}:h.toolChoice===void 0?{}:{toolChoice:h.toolChoice},...h.imagePolicy===void 0?{}:{imagePolicy:h.imagePolicy},...h.documentPolicy===void 0?{}:{documentPolicy:h.documentPolicy},...M===void 0?{}:{outputFormat:M}},be={...N,signal:g};i(h.history,!0);try{await d(h.hooks?.checkpoint,[{kind:`before-model-request`,request:be,snapshot:h.history.snapshot(),signal:g,...h.logger===void 0?{}:{logger:h.logger}}],h,g,`checkpoint`)}catch(e){return i(h.history,!1),{trace:C,finish:{kind:`error`,failure:{message:`history checkpoint failed: ${c(e)}`,code:`CHECKPOINT_FAILED`}},calls:[],afterToolCallIds:T,timing:p(!1,T.length>0)}}let P=E();if(P!==void 0)return i(h.history,!1),P;await _({type:`span-start`,trace:C,at:u(),name:`chat ${h.config.model}`,kind:`chat`,attributes:{"gen_ai.operation.name":`chat`,"gen_ai.request.model":h.config.model,...h.config.reasoningEffort===void 0?{}:{"gen_ai.request.reasoning_effort":h.config.reasoningEffort},turn:v,step:y,forcedFinal:x,phase:b},input:pe(N)}),await _({type:`step-start`,turn:v,step:y,trace:C,...x?{forcedFinal:!0}:{}});let F=new n,I=new Set,L=o(h.modelTimeoutMs??6e5,`modelTimeoutMs`),R=s(h.maxModelRequestBytes??33554432,`maxModelRequestBytes`),z=s(h.maxModelResponseBytes??33554432,`maxModelResponseBytes`),B=s(h.maxModelStreamEvents??1e5,`maxModelStreamEvents`),xe=o(h.teardownTimeoutMs??3e4,`teardownTimeoutMs`),V,Se=!1,Ce=!1,H;try{H=te(N)}catch(e){H=-1,F.push(l(`model request is not serializable: ${c(e)}`,`INVALID_MODEL_REQUEST`))}if(H>R)F.push(l(`model request exceeds the ${R}-byte limit`,`MODEL_REQUEST_TOO_LARGE`));else if(H>=0){let e=new AbortController,t=AbortSignal.any([g,e.signal]),n=!1,r=setTimeout(()=>{n=!0,e.abort(Error(`model stream exceeded ${L}ms`))},L),i=h.registry.stream({...N,signal:t},h.accounting?.modelInvocation),a=i[Symbol.asyncIterator](),o=!1,s=0,u=0,d=!1;try{for(;;){let n=await ie(a.next(),t);if(n.done===!0){o=!0;break}let r=n.value;try{de(r,B)}catch(t){F.push(l(`model adapter emitted an invalid stream chunk: ${c(t)}`,`INVALID_MODEL_STREAM`)),e.abort(Error(`invalid model stream chunk`));break}s++;try{u+=te(r)}catch(t){F.push(l(`model adapter emitted a non-serializable stream chunk: ${c(t)}`,`INVALID_MODEL_STREAM`)),e.abort(Error(`non-serializable model stream chunk`));break}if(s>B||u>z){let t=s>B?`${B}-event`:`${z}-byte`;F.push(l(`model response exceeds the ${t} limit`,`MODEL_RESPONSE_TOO_LARGE`)),e.abort(Error(`model response resource limit exceeded`));break}if(d){F.push(l(`model adapter emitted data after its terminal finish chunk`,`INVALID_MODEL_STREAM`)),e.abort(Error(`invalid model stream`));break}F.push(r),!((r.type===`text-delta`||r.type===`reasoning-delta`||r.type===`tool-call-delta`)&&I.has(r.index))&&(r.type===`block-end`&&I.add(r.index),r.type===`finish`&&(d=!0),r.type===`text-delta`?await _({type:`text-delta`,index:r.index,text:r.text,phase:b===`process`?`commentary`:r.phase??`unknown`,trace:C}):r.type===`reasoning-delta`?await _({type:`reasoning-delta`,index:r.index,text:r.text,trace:C}):r.type===`image-delta`?await _({type:`image-delta`,itemId:r.itemId,data:r.data,mediaType:r.mediaType,...r.partialIndex===void 0?{}:{partialIndex:r.partialIndex},trace:C}):r.type===`usage`?await _({type:`usage`,usage:r.usage,trace:C}):r.type===`usage-progress`&&await _({...r,trace:C}))}}catch(e){if(!(e instanceof ne))throw e;F.push(g.aborted?ee(c(g.reason??e)):n?l(`model stream exceeded ${L}ms`,`MODEL_TIMEOUT`):l(c(e.cause??e),`MODEL_STREAM_ABORTED`))}finally{clearTimeout(r),o||(e.abort(Error(`model stream closed before exhaustion`)),await re(a,xe)||F.push(l(`model stream ignored cancellation for more than ${xe}ms; the adapter operation may still be running`,`MODEL_TEARDOWN_TIMEOUT`)))}let f=await i.report,p=h.accounting===void 0?void 0:await h.accounting.recordModelCall(f,{...N,signal:g});V=p?.report??f,Se=p?.usageRequired??!1,Ce=p?.usageUnavailable??!1}let U=F.finish,W=U.kind===`aborted`,G;try{G=U.kind===`aborted`?F.interruptedBlocks():F.blocks()}catch(e){U={kind:`error`,failure:{message:`model adapter emitted an invalid block sequence: ${c(e)}`,code:`INVALID_MODEL_STREAM`}},W=!0,G=F.interruptedBlocks()}let we=F.textBlocks().filter(({block:e})=>!W||e.text.trim()!==``),Te=0,Ee=f(G.map(e=>{if(e.type!==`text`)return e;let t=we[Te++];return e.phase===void 0&&t?.beforeNativeCall?{...e,phase:`commentary`}:e}),b===`process`,F.hasToolCalls),K=oe(Ee,h.history),q=K.blocks,De=S&&h.outputFormat?.type===`json_schema`&&U.kind===`stop`&&!me(m(q),h.validateOutput)?{message:`model returned invalid JSON or failed the structured output validator`,code:t.MALFORMED_RESPONSE}:void 0,J=(S&&q.some(e=>e.type===`tool-call`)?{message:`model emitted a host tool call during the final output phase`,code:`INVALID_TOOL_CALL`}:void 0)??De??se(q,h.history),Y=J===void 0?q:q.filter(e=>e.type!==`tool-call`),X=Y.some(e=>e.type===`tool-call`)?Y.map(e=>e.type===`text`?{...e,text:e.text.replaceAll(ue,``)}:e):Y,Z=J===void 0?U:{kind:`error`,failure:J},Q=X.length===0?void 0:ae(h,X,W?void 0:F.replayState),$=Q?.content.filter(e=>e.type===`tool-call`).map(e=>({callId:e.id,toolName:e.name,rawArguments:e.arguments}))??[],Oe=p($.length>0,T.length>0);if(X.some(e=>e.type===`text`)){let e=we.map(({index:e})=>e),t=Z.kind===`error`||Z.kind===`aborted`||Z.kind===`max-tokens`,n=0;for(let r of X){if(r.type!==`text`)continue;let i=e[n++];i!==void 0&&await _({type:`text-end`,index:i,text:r.text,phase:r.phase??`final-answer`,trace:C,...t?{incomplete:!0}:{}})}}return await _({type:`span-end`,trace:C,at:u(),status:Z.kind===`error`?`error`:Z.kind===`aborted`?`aborted`:`success`,output:Q===void 0?void 0:{text:m(Q.content),commentary:Q.content.flatMap(e=>e.type===`text`&&e.phase===`commentary`?[e.text]:[]),reasoning:Q.content.flatMap(e=>e.type===`reasoning`?[e.text]:[]),toolCalls:$.length},...F.usage===void 0?{}:{usage:F.usage},...Z.kind===`error`||Z.kind===`aborted`?{error:{type:`ModelError`,message:Z.failure.message,code:Z.failure.code}}:{}}),($.length===0||S)&&await _({type:`step-end`,turn:v,step:y,trace:C}),{trace:C,...Q===void 0?{}:{message:Q},finish:Z,...F.usage===void 0?{}:{usage:F.usage},...V===void 0?{}:{report:V},...Se?{usageRequired:!0}:{},...Ce?{usageUnavailable:!0}:{},calls:$,afterToolCallIds:T,timing:Oe,...K.dropped.length===0?{}:{droppedDuplicateCalls:K.dropped}}}function me(e,t){try{let n=JSON.parse(e);return t?.(n),!0}catch{return!1}}export{_ as modelRound};
1
+ import{createSpanId as e}from"../../../observation/context.js";import{MODEL_ERROR_CODES as t}from"../../../errors/model-error.js";import{BlockAssembler as n}from"../../../stream/assembler.js";import{normalizeToolPairing as r}from"../../history/normalize.js";import{observeModelRequestBoundary as i}from"./model-request-boundary.js";import{stepProjectionSources as a}from"./step-projection.js";import{positiveFinite as o,positiveSafeInteger as s}from"./config.js";import{messageOf as c,modelAbortedFinish as ee,modelFailureFinish as l,now as u,serializedBytes as te}from"./common.js";import{StreamAbortError as ne,closeIterator as re,nextWithAbort as ie}from"./cancellation.js";import{runOptionalHook as d}from"./hooks.js";import{classifyTextPhases as f,contentTiming as p,createAssistant as ae,dropDuplicateToolCalls as oe,invalidHostToolCall as se,recentToolResultIds as ce,systemText as le,textOf as m}from"./content.js";import{UNCHANGED_ANSWER_MARKER as ue}from"../control-text.js";import{validateStreamChunk as de}from"./validation.js";import{accountingUsageStop as fe}from"./usage-stop.js";function h(e){let t=e.replace(/\s+/gu,` `).trim();return t.length>600?`${t.slice(0,600)}…`:t}function g(e){let t=[],n=[];for(let r of e.content)r.type===`text`||r.type===`reasoning`?t.push(r.text):r.type===`tool-call`?n.push(`${r.name}(${h(r.arguments)})`):r.type===`tool-result`?t.push(m(r.content)):t.push(`[${r.type}]`);return{role:e.role,producer:e.source.kind,...t.length===0?{}:{text:h(t.join(` `))},...n.length===0?{}:{toolCalls:n}}}function pe(e){let t=e.messages??[],n=Math.max(0,t.length-8);return{messageCount:t.length,...t.at(-1)===void 0?{}:{lastMessageId:t.at(-1)?.id},...e.system===void 0?{}:{system:h(e.system)},...e.tools===void 0||e.tools.length===0?{}:{tools:e.tools.map(e=>e.name)},...e.toolChoice===void 0?{}:{toolChoice:e.toolChoice},...n===0?{}:{earlierMessagesOmitted:n},messages:t.slice(-8).map(g)}}async function _(h,g,_,he,ge,v,y,b,x={workStep:y,finalizing:!1}){let S=b===`forced-final`,C=b===`final`||S,w={traceId:ge.traceId,spanId:e(),parentSpanId:ge.spanId},T=r(h.history.messages()),_e=T,E=ce(h.history),ve=h.history.generation(),ye=h.history.entries().length,D=()=>{let e=fe(h.accounting);if(!(e===void 0&&!g.aborted))return{trace:w,finish:g.aborted?{kind:`aborted`,failure:{code:`ABORTED`,message:c(g.reason)}}:{kind:`stop`},calls:[],afterToolCallIds:E,timing:p(!1,E.length>0),...e?.kind===`error`?{usageRequired:!0}:{},...e?.kind===`usage-unavailable`?{usageUnavailable:!0}:{}}},O=D();if(O!==void 0)return O;let k=await d(h.hooks?.beforeStep,[{turn:v,step:y,workStep:x.workStep,phase:b,...x.finalizing?{finalizing:!0}:{},messages:T,snapshot:h.history.snapshot(),signal:g,...h.logger===void 0?{}:{logger:h.logger},emit:he}],h,g,`beforeStep`),A=D();if(A!==void 0)return A;if(k?.kind===`reject`)return{trace:w,finish:{kind:`error`,failure:{message:k.reason,code:`STEP_REJECTED`}},calls:[],afterToolCallIds:E,timing:p(!1,E.length>0)};if((h.history.generation()!==ve||h.history.entries().length!==ye)&&(T=r(h.history.messages())),k?.messages!==void 0){let e=a(k)??new Set(_e.map(e=>e.id)),t=new Set(k.messages.map(e=>e.id)),n=T.filter(n=>!e.has(n.id)&&!t.has(n.id)),i=new Set(T.map(e=>e.id)),o=[...e].some(e=>!i.has(e)),s=new Map(k.messages.map(e=>[e.id,e]));T=r(o?[...k.messages.filter(t=>!e.has(t.id)&&!i.has(t.id)&&t.source.kind===`app`),...T.flatMap(t=>{let n=s.get(t.id);return n===void 0?e.has(t.id)?[]:[t]:[n]})]:[...k.messages,...n])}k?.prepend!==void 0&&(T=Object.freeze([...k.prepend,...T]));let j=le(h,S),M=[...h.tools?.schemas()??[],...h.nativeTools??[]],N=!C&&h.outputFormat?.type===`json_schema`?{type:`text`}:h.outputFormat,P={...h.config,messages:T,...j.length===0?{}:{system:j},...M.length===0?{}:{tools:M},...C&&M.length>0?{toolChoice:`none`}:h.toolChoice===void 0?{}:{toolChoice:h.toolChoice},...h.imagePolicy===void 0?{}:{imagePolicy:h.imagePolicy},...h.documentPolicy===void 0?{}:{documentPolicy:h.documentPolicy},...N===void 0?{}:{outputFormat:N}},be={...P,signal:g};i(h.history,!0);try{await d(h.hooks?.checkpoint,[{kind:`before-model-request`,request:be,snapshot:h.history.snapshot(),signal:g,...h.logger===void 0?{}:{logger:h.logger}}],h,g,`checkpoint`)}catch(e){return i(h.history,!1),{trace:w,finish:{kind:`error`,failure:{message:`history checkpoint failed: ${c(e)}`,code:`CHECKPOINT_FAILED`}},calls:[],afterToolCallIds:E,timing:p(!1,E.length>0)}}let xe=D();if(xe!==void 0)return i(h.history,!1),xe;await _({type:`span-start`,trace:w,at:u(),name:`chat ${h.config.model}`,kind:`chat`,attributes:{"gen_ai.operation.name":`chat`,"gen_ai.request.model":h.config.model,...h.config.reasoningEffort===void 0?{}:{"gen_ai.request.reasoning_effort":h.config.reasoningEffort},turn:v,step:y,forcedFinal:S,phase:b},input:pe(P)}),await _({type:`step-start`,turn:v,step:y,trace:w,...S?{forcedFinal:!0}:{}});let F=new n,Se=new Set,I=o(h.modelTimeoutMs??6e5,`modelTimeoutMs`),Ce=s(h.maxModelRequestBytes??33554432,`maxModelRequestBytes`),we=s(h.maxModelResponseBytes??33554432,`maxModelResponseBytes`),L=s(h.maxModelStreamEvents??1e5,`maxModelStreamEvents`),Te=o(h.teardownTimeoutMs??3e4,`teardownTimeoutMs`),R,Ee=!1,De=!1,z;try{z=te(P)}catch(e){z=-1,F.push(l(`model request is not serializable: ${c(e)}`,`INVALID_MODEL_REQUEST`))}if(z>Ce)F.push(l(`model request exceeds the ${Ce}-byte limit`,`MODEL_REQUEST_TOO_LARGE`));else if(z>=0){let e=new AbortController,t=AbortSignal.any([g,e.signal]),n=!1,r=setTimeout(()=>{n=!0,e.abort(Error(`model stream exceeded ${I}ms`))},I),i=h.registry.stream({...P,signal:t},h.accounting?.modelInvocation),a=i[Symbol.asyncIterator](),o=!1,s=0,u=0,d=!1;try{for(;;){let n=await ie(a.next(),t);if(n.done===!0){o=!0;break}let r=n.value;try{de(r,L)}catch(t){F.push(l(`model adapter emitted an invalid stream chunk: ${c(t)}`,`INVALID_MODEL_STREAM`)),e.abort(Error(`invalid model stream chunk`));break}s++;try{u+=te(r)}catch(t){F.push(l(`model adapter emitted a non-serializable stream chunk: ${c(t)}`,`INVALID_MODEL_STREAM`)),e.abort(Error(`non-serializable model stream chunk`));break}if(s>L||u>we){let t=s>L?`${L}-event`:`${we}-byte`;F.push(l(`model response exceeds the ${t} limit`,`MODEL_RESPONSE_TOO_LARGE`)),e.abort(Error(`model response resource limit exceeded`));break}if(d){F.push(l(`model adapter emitted data after its terminal finish chunk`,`INVALID_MODEL_STREAM`)),e.abort(Error(`invalid model stream`));break}F.push(r),!((r.type===`text-delta`||r.type===`reasoning-delta`||r.type===`tool-call-delta`)&&Se.has(r.index))&&(r.type===`block-end`&&Se.add(r.index),r.type===`finish`&&(d=!0),r.type===`text-delta`?await _({type:`text-delta`,index:r.index,text:r.text,phase:b===`process`?`commentary`:r.phase??`unknown`,trace:w}):r.type===`reasoning-delta`?await _({type:`reasoning-delta`,index:r.index,text:r.text,trace:w}):r.type===`image-delta`?await _({type:`image-delta`,itemId:r.itemId,data:r.data,mediaType:r.mediaType,...r.partialIndex===void 0?{}:{partialIndex:r.partialIndex},trace:w}):r.type===`usage`?await _({type:`usage`,usage:r.usage,trace:w}):r.type===`usage-progress`&&await _({...r,trace:w}))}}catch(e){if(!(e instanceof ne))throw e;F.push(g.aborted?ee(c(g.reason??e)):n?l(`model stream exceeded ${I}ms`,`MODEL_TIMEOUT`):l(c(e.cause??e),`MODEL_STREAM_ABORTED`))}finally{clearTimeout(r),o||(e.abort(Error(`model stream closed before exhaustion`)),await re(a,Te)||F.push(l(`model stream ignored cancellation for more than ${Te}ms; the adapter operation may still be running`,`MODEL_TEARDOWN_TIMEOUT`)))}let f=await i.report,p=h.accounting===void 0?void 0:await h.accounting.recordModelCall(f,{...P,signal:g});R=p?.report??f,Ee=p?.usageRequired??!1,De=p?.usageUnavailable??!1}let B=F.finish,V=B.kind===`aborted`,H;try{H=B.kind===`aborted`?F.interruptedBlocks():F.blocks()}catch(e){B={kind:`error`,failure:{message:`model adapter emitted an invalid block sequence: ${c(e)}`,code:`INVALID_MODEL_STREAM`}},V=!0,H=F.interruptedBlocks()}let Oe=F.textBlocks().filter(({block:e})=>!V||e.text.trim()!==``),ke=0,U=H.map(e=>{if(e.type!==`text`)return e;let t=Oe[ke++];return e.phase===void 0&&t?.beforeNativeCall?{...e,phase:`commentary`}:e}),Ae=U.findLastIndex(e=>e.type===`tool-call`),W=S&&(B.kind===`tool-calls`||B.kind===`stop`)&&Ae>=0?U.flatMap((e,t)=>e.type===`tool-call`?[]:e.type===`text`&&e.phase===void 0?[{...e,phase:t>Ae?`final-answer`:`commentary`}]:[e]):void 0,G=W!==void 0&&W.some(e=>e.type===`text`&&e.phase!==`commentary`&&e.text.trim()!==``),je=G?W:f(U,b===`process`,F.hasToolCalls),K=oe(je,h.history),q=K.blocks,Me=C&&h.outputFormat?.type===`json_schema`&&(B.kind===`stop`||G)&&!me(m(q),h.validateOutput)?{message:`model returned invalid JSON or failed the structured output validator`,code:t.MALFORMED_RESPONSE}:void 0,J=(C&&q.some(e=>e.type===`tool-call`)?{message:`model emitted a host tool call during the final output phase`,code:`INVALID_TOOL_CALL`}:void 0)??Me??se(q,h.history),Y=J===void 0?q:q.filter(e=>e.type!==`tool-call`),X=Y.some(e=>e.type===`tool-call`)?Y.map(e=>e.type===`text`?{...e,text:e.text.replaceAll(ue,``)}:e):Y,Z=B.kind===`error`||B.kind===`aborted`?B:J===void 0?G?{kind:`stop`}:B:{kind:`error`,failure:J},Q=X.length===0?void 0:ae(h,X,V?void 0:F.replayState),$=Q?.content.filter(e=>e.type===`tool-call`).map(e=>({callId:e.id,toolName:e.name,rawArguments:e.arguments}))??[],Ne=p($.length>0,E.length>0);if(X.some(e=>e.type===`text`)){let e=Oe.map(({index:e})=>e),t=Z.kind===`error`||Z.kind===`aborted`||Z.kind===`max-tokens`,n=0;for(let r of X){if(r.type!==`text`)continue;let i=e[n++];i!==void 0&&await _({type:`text-end`,index:i,text:r.text,phase:r.phase??`final-answer`,trace:w,...t?{incomplete:!0}:{}})}}return await _({type:`span-end`,trace:w,at:u(),status:Z.kind===`error`?`error`:Z.kind===`aborted`?`aborted`:`success`,output:Q===void 0?void 0:{text:m(Q.content),commentary:Q.content.flatMap(e=>e.type===`text`&&e.phase===`commentary`?[e.text]:[]),reasoning:Q.content.flatMap(e=>e.type===`reasoning`?[e.text]:[]),toolCalls:$.length},...F.usage===void 0?{}:{usage:F.usage},...Z.kind===`error`||Z.kind===`aborted`?{error:{type:`ModelError`,message:Z.failure.message,code:Z.failure.code}}:{}}),($.length===0||C)&&await _({type:`step-end`,turn:v,step:y,trace:w}),{trace:w,...Q===void 0?{}:{message:Q},finish:Z,...F.usage===void 0?{}:{usage:F.usage},...R===void 0?{}:{report:R},...Ee?{usageRequired:!0}:{},...De?{usageUnavailable:!0}:{},calls:$,afterToolCallIds:E,timing:Ne,...K.dropped.length===0?{}:{droppedDuplicateCalls:K.dropped}}}function me(e,t){try{let n=JSON.parse(e);return t?.(n),!0}catch{return!1}}export{_ as modelRound};
2
2
  //# sourceMappingURL=model-round.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"model-round.js","names":[],"sources":["../../../../src/agent/loop/turn/model-round.ts"],"sourcesContent":["import type { GenerateOptions } from '../../../contract/index.ts'\nimport { UNCHANGED_ANSWER_MARKER } from '../control-text.ts'\nimport { MODEL_ERROR_CODES, type ModelFailure } from '../../../errors/index.ts'\nimport type { ContentBlock, Message, ToolCallBlock } from '../../../message/index.ts'\nimport { BlockAssembler } from '../../../stream/index.ts'\nimport type { FinishReason } from '../../../stream/index.ts'\nimport type { ModelCallReport } from '../../../observation/index.ts'\nimport { normalizeToolPairing } from '../../history/normalize.ts'\nimport { createSpanId, type TraceRef } from '../../trace/trace.ts'\nimport type { AgentEvent, AgentMaintenanceEvent } from '../types.ts'\nimport { type ModelRoundPhase, type RunTurnOptions, type RoundResult } from './types.ts'\nimport { positiveFinite, positiveSafeInteger } from './config.ts'\nimport { serializedBytes, modelFailureFinish, modelAbortedFinish, messageOf, now } from './common.ts'\nimport { validateStreamChunk } from './validation.ts'\nimport { StreamAbortError, nextWithAbort, closeIterator } from './cancellation.ts'\nimport { runOptionalHook } from './hooks.ts'\nimport { stepProjectionSources } from './step-projection.ts'\nimport { observeModelRequestBoundary } from './model-request-boundary.ts'\nimport { accountingUsageStop } from './usage-stop.ts'\nimport {\n classifyTextPhases, dropDuplicateToolCalls, invalidHostToolCall, contentTiming,\n recentToolResultIds, systemText,\n createAssistant, textOf,\n} from './content.ts'\n\n/** How much of one message the trace keeps. */\nconst PREVIEW_CHARS = 600\n\n/** How many trailing messages the trace keeps. */\nconst PREVIEW_MESSAGES = 8\n\nfunction clip(text: string): string {\n const flattened = text.replace(/\\s+/gu, ' ').trim()\n return flattened.length > PREVIEW_CHARS ? `${flattened.slice(0, PREVIEW_CHARS)}…` : flattened\n}\n\n/** One message as a line: who spoke, and what the model could read of it. */\nfunction previewMessage(message: Message): Record<string, unknown> {\n const parts: string[] = []\n const calls: string[] = []\n for (const block of message.content) {\n if (block.type === 'text' || block.type === 'reasoning') parts.push(block.text)\n else if (block.type === 'tool-call') calls.push(`${block.name}(${clip(block.arguments)})`)\n else if (block.type === 'tool-result') parts.push(textOf(block.content))\n else parts.push(`[${block.type}]`)\n }\n return {\n role: message.role,\n producer: message.source.kind,\n ...parts.length === 0 ? {} : { text: clip(parts.join(' ')) },\n ...calls.length === 0 ? {} : { toolCalls: calls },\n }\n}\n\n/**\n * What this round sent the model, in a form a person can read.\n *\n * A summary, not a copy: the request can be the whole conversation plus every\n * tool schema, and a trace that stored it verbatim would be larger than the\n * work it describes. What survives is the part that explains the round — which\n * tools were on offer, how long the context was, and the tail of the\n * conversation the model was actually answering, each message clipped.\n * @param request - The request about to be dispatched.\n * @returns The summary for the span.\n */\nfunction requestSummary(request: GenerateOptions): Record<string, unknown> {\n const messages = request.messages ?? []\n const dropped = Math.max(0, messages.length - PREVIEW_MESSAGES)\n return {\n messageCount: messages.length,\n // The identity of the request, for a host that records the provider call\n // separately and has to say WHICH round each recording belongs to. Message\n // ids are stable across every representation boundary, so the last one plus\n // the count names one round of one agent without ambiguity.\n ...messages.at(-1) === undefined ? {} : { lastMessageId: messages.at(-1)?.id },\n ...request.system === undefined ? {} : { system: clip(request.system) },\n ...request.tools === undefined || request.tools.length === 0\n ? {}\n : { tools: request.tools.map(tool => tool.name) },\n ...request.toolChoice === undefined ? {} : { toolChoice: request.toolChoice },\n ...dropped === 0 ? {} : { earlierMessagesOmitted: dropped },\n messages: messages.slice(-PREVIEW_MESSAGES).map(previewMessage),\n }\n}\n\nexport async function modelRound(\n options: RunTurnOptions,\n signal: AbortSignal,\n emit: (event: AgentEvent) => Promise<void>,\n emitMaintenance: (event: AgentMaintenanceEvent) => Promise<void>,\n root: TraceRef,\n turn: number,\n step: number,\n phase: ModelRoundPhase,\n): Promise<RoundResult> {\n const forcedFinal = phase === 'forced-final'\n const finalOutput = phase === 'final' || forcedFinal\n const trace: TraceRef = { traceId: root.traceId, spanId: createSpanId(), parentSpanId: root.spanId }\n let messages = normalizeToolPairing(options.history.messages())\n const initialMessages = messages\n const afterToolCallIds = recentToolResultIds(options.history)\n const generation = options.history.generation()\n const entries = options.history.entries().length\n const stopped = (): RoundResult | undefined => {\n const stop = accountingUsageStop(options.accounting)\n if (stop === undefined && !signal.aborted) return undefined\n return {\n trace, finish: signal.aborted\n ? { kind: 'aborted', failure: { code: 'ABORTED', message: messageOf(signal.reason) } }\n : { kind: 'stop' },\n calls: [], afterToolCallIds, timing: contentTiming(false, afterToolCallIds.length > 0),\n ...(stop?.kind === 'error' ? { usageRequired: true } : {}),\n ...(stop?.kind === 'usage-unavailable' ? { usageUnavailable: true } : {}),\n }\n }\n const beforeMaintenance = stopped()\n if (beforeMaintenance !== undefined) return beforeMaintenance\n const decision = await runOptionalHook(options.hooks?.beforeStep, [{\n turn, step, messages, snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n emit: emitMaintenance,\n }], options, signal, 'beforeStep')\n const afterMaintenance = stopped()\n if (afterMaintenance !== undefined) return afterMaintenance\n if (decision?.kind === 'reject') {\n const failure: ModelFailure = { message: decision.reason, code: 'STEP_REJECTED' }\n return {\n trace, finish: { kind: 'error', failure }, calls: [], afterToolCallIds,\n timing: contentTiming(false, afterToolCallIds.length > 0),\n }\n }\n // Hooks may append live steering as well as replacing compacted history.\n // Refresh for either mutation so new user input stays at the chronological\n // tail instead of needing to be prepended ahead of older history.\n if (options.history.generation() !== generation\n || options.history.entries().length !== entries) {\n messages = normalizeToolPairing(options.history.messages())\n }\n if (decision?.messages !== undefined) {\n const originalIds = stepProjectionSources(decision) ?? new Set(initialMessages.map(message => message.id))\n const projectedIds = new Set(decision.messages.map(message => message.id))\n const introduced = messages.filter(message => !originalIds.has(message.id) && !projectedIds.has(message.id))\n const currentIds = new Set(messages.map(message => message.id))\n const replaced = [...originalIds].some(id => !currentIds.has(id))\n // A hook can await external I/O while live steering or replacement arrives.\n // Replacements invalidate a stale projection; append-only steering stays at the tail.\n const projectedById = new Map(decision.messages.map(message => [message.id, message]))\n messages = normalizeToolPairing(replaced\n ? [...decision.messages.filter(message => !originalIds.has(message.id) && !currentIds.has(message.id) && message.source.kind === 'app'),\n ...messages.flatMap(message => {\n const projected = projectedById.get(message.id)\n return projected === undefined ? originalIds.has(message.id) ? [] : [message] : [projected]\n })]\n : [...decision.messages, ...introduced])\n }\n if (decision?.prepend !== undefined) messages = Object.freeze([...decision.prepend, ...messages])\n const system = systemText(options, forcedFinal)\n const availableTools = [\n ...options.tools?.schemas() ?? [],\n ...options.nativeTools ?? [],\n ]\n const tools = availableTools\n const outputFormat = !finalOutput && options.outputFormat?.type === 'json_schema'\n ? { type: 'text' as const }\n : options.outputFormat\n const requestBase: GenerateOptions = {\n ...options.config,\n messages,\n ...system.length === 0 ? {} : { system },\n ...tools.length === 0 ? {} : { tools },\n ...finalOutput && tools.length > 0\n ? { toolChoice: 'none' as const }\n : options.toolChoice === undefined ? {} : { toolChoice: options.toolChoice },\n ...options.imagePolicy === undefined ? {} : { imagePolicy: options.imagePolicy },\n ...options.documentPolicy === undefined ? {} : { documentPolicy: options.documentPolicy },\n ...outputFormat === undefined ? {} : { outputFormat },\n }\n const checkpointRequest: GenerateOptions = { ...requestBase, signal }\n observeModelRequestBoundary(options.history, true)\n try {\n await runOptionalHook(options.hooks?.checkpoint, [{\n kind: 'before-model-request', request: checkpointRequest,\n snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n }], options, signal, 'checkpoint')\n } catch (error: unknown) {\n // No output will be recorded for this request. Release queued input before\n // recovery hooks or the retry's beforeStep can read or inject into history.\n observeModelRequestBoundary(options.history, false)\n const failure: ModelFailure = { message: `history checkpoint failed: ${messageOf(error)}`, code: 'CHECKPOINT_FAILED' }\n return {\n trace, finish: { kind: 'error', failure }, calls: [], afterToolCallIds,\n timing: contentTiming(false, afterToolCallIds.length > 0),\n }\n }\n const beforeDispatch = stopped()\n if (beforeDispatch !== undefined) {\n observeModelRequestBoundary(options.history, false)\n return beforeDispatch\n }\n await emit({ type: 'span-start', trace, at: now(), name: `chat ${options.config.model}`, kind: 'chat', attributes: {\n 'gen_ai.operation.name': 'chat', 'gen_ai.request.model': options.config.model,\n // The effort is part of WHICH call this was: the same model at minimal and\n // at high is two different requests, priced and paced differently, and a\n // trace that omits it cannot explain either. Absent when the route has no\n // effort ladder, rather than reported as a default nobody chose.\n ...options.config.reasoningEffort === undefined\n ? {}\n : { 'gen_ai.request.reasoning_effort': options.config.reasoningEffort },\n turn, step, forcedFinal, phase,\n }, input: requestSummary(requestBase) })\n await emit({ type: 'step-start', turn, step, trace, ...forcedFinal ? { forcedFinal: true as const } : {} })\n const assembler = new BlockAssembler()\n const closedBlockIndexes = new Set<number>()\n const modelTimeoutMs = positiveFinite(options.modelTimeoutMs ?? 10 * 60_000, 'modelTimeoutMs')\n const maxRequestBytes = positiveSafeInteger(options.maxModelRequestBytes ?? 32 * 1024 * 1024, 'maxModelRequestBytes')\n const maxResponseBytes = positiveSafeInteger(options.maxModelResponseBytes ?? 32 * 1024 * 1024, 'maxModelResponseBytes')\n const maxStreamEvents = positiveSafeInteger(options.maxModelStreamEvents ?? 100_000, 'maxModelStreamEvents')\n const teardownTimeoutMs = positiveFinite(options.teardownTimeoutMs ?? 30_000, 'teardownTimeoutMs')\n let modelCallReport: ModelCallReport | undefined\n let usageRequired = false\n let usageUnavailable = false\n let requestBytes: number\n try {\n requestBytes = serializedBytes(requestBase)\n } catch (error: unknown) {\n requestBytes = -1\n assembler.push(modelFailureFinish(`model request is not serializable: ${messageOf(error)}`, 'INVALID_MODEL_REQUEST'))\n }\n if (requestBytes > maxRequestBytes) {\n assembler.push(modelFailureFinish(\n `model request exceeds the ${maxRequestBytes}-byte limit`,\n 'MODEL_REQUEST_TOO_LARGE',\n ))\n } else if (requestBytes >= 0) {\n const owned = new AbortController()\n const roundSignal = AbortSignal.any([signal, owned.signal])\n let timedOut = false\n const timeout = setTimeout(() => {\n timedOut = true\n owned.abort(new Error(`model stream exceeded ${modelTimeoutMs}ms`))\n }, modelTimeoutMs)\n const handle = options.registry.stream(\n { ...requestBase, signal: roundSignal },\n options.accounting?.modelInvocation,\n )\n const iterator = handle[Symbol.asyncIterator]()\n let exhausted = false\n let events = 0\n let responseBytes = 0\n let sawFinish = false\n try {\n while (true) {\n const next = await nextWithAbort(iterator.next(), roundSignal)\n if (next.done === true) {\n exhausted = true\n break\n }\n const chunk = next.value\n try {\n validateStreamChunk(chunk, maxStreamEvents)\n } catch (error: unknown) {\n assembler.push(modelFailureFinish(\n `model adapter emitted an invalid stream chunk: ${messageOf(error)}`,\n 'INVALID_MODEL_STREAM',\n ))\n owned.abort(new Error('invalid model stream chunk'))\n break\n }\n events++\n try {\n responseBytes += serializedBytes(chunk)\n } catch (error: unknown) {\n assembler.push(modelFailureFinish(\n `model adapter emitted a non-serializable stream chunk: ${messageOf(error)}`,\n 'INVALID_MODEL_STREAM',\n ))\n owned.abort(new Error('non-serializable model stream chunk'))\n break\n }\n if (events > maxStreamEvents || responseBytes > maxResponseBytes) {\n const dimension = events > maxStreamEvents\n ? `${maxStreamEvents}-event`\n : `${maxResponseBytes}-byte`\n assembler.push(modelFailureFinish(\n `model response exceeds the ${dimension} limit`,\n 'MODEL_RESPONSE_TOO_LARGE',\n ))\n owned.abort(new Error('model response resource limit exceeded'))\n break\n }\n if (sawFinish) {\n assembler.push(modelFailureFinish(\n 'model adapter emitted data after its terminal finish chunk',\n 'INVALID_MODEL_STREAM',\n ))\n owned.abort(new Error('invalid model stream'))\n break\n }\n assembler.push(chunk)\n // The assembler ignores deltas after the first close. The visible\n // stream must do the same or UI text can disagree with stored history.\n if ((chunk.type === 'text-delta' || chunk.type === 'reasoning-delta' || chunk.type === 'tool-call-delta')\n && closedBlockIndexes.has(chunk.index)) continue\n if (chunk.type === 'block-end') closedBlockIndexes.add(chunk.index)\n if (chunk.type === 'finish') sawFinish = true\n if (chunk.type === 'text-delta') await emit({\n type: 'text-delta', index: chunk.index, text: chunk.text,\n phase: phase === 'process' ? 'commentary' : chunk.phase ?? 'unknown', trace,\n })\n else if (chunk.type === 'reasoning-delta') await emit({\n type: 'reasoning-delta', index: chunk.index, text: chunk.text, trace,\n })\n else if (chunk.type === 'image-delta') await emit({\n type: 'image-delta', itemId: chunk.itemId, data: chunk.data,\n mediaType: chunk.mediaType, ...chunk.partialIndex === undefined ? {} : { partialIndex: chunk.partialIndex }, trace,\n })\n else if (chunk.type === 'usage') await emit({ type: 'usage', usage: chunk.usage, trace })\n else if (chunk.type === 'usage-progress') await emit({ ...chunk, trace })\n }\n } catch (error: unknown) {\n if (!(error instanceof StreamAbortError)) throw error\n assembler.push(signal.aborted\n ? modelAbortedFinish(messageOf(signal.reason ?? error))\n : timedOut\n ? modelFailureFinish(`model stream exceeded ${modelTimeoutMs}ms`, 'MODEL_TIMEOUT')\n : modelFailureFinish(messageOf(error.cause ?? error), 'MODEL_STREAM_ABORTED'))\n } finally {\n clearTimeout(timeout)\n if (!exhausted) {\n owned.abort(new Error('model stream closed before exhaustion'))\n const settled = await closeIterator(iterator, teardownTimeoutMs)\n if (!settled) {\n assembler.push(modelFailureFinish(\n `model stream ignored cancellation for more than ${teardownTimeoutMs}ms; the adapter operation may still be running`,\n 'MODEL_TEARDOWN_TIMEOUT',\n ))\n }\n }\n }\n const report = await handle.report\n const decision = options.accounting === undefined\n ? undefined\n : await options.accounting.recordModelCall(report, { ...requestBase, signal })\n modelCallReport = decision?.report ?? report\n usageRequired = decision?.usageRequired ?? false\n usageUnavailable = decision?.usageUnavailable ?? false\n }\n let providerFinish = assembler.finish\n let retainedPrefix = providerFinish.kind === 'aborted'\n let rawBlocks: ContentBlock[]\n try {\n rawBlocks = providerFinish.kind === 'aborted' ? assembler.interruptedBlocks() : assembler.blocks()\n } catch (error: unknown) {\n providerFinish = {\n kind: 'error',\n failure: {\n message: `model adapter emitted an invalid block sequence: ${messageOf(error)}`,\n code: 'INVALID_MODEL_STREAM',\n },\n }\n // A broken extension must not discard text already delivered to the user.\n // This prefix deliberately excludes tools and unassembled extension data.\n retainedPrefix = true\n rawBlocks = assembler.interruptedBlocks()\n }\n // Interrupted/truncated assembly drops unfinished calls, but the text that\n // introduced those calls is still process narration, not a final answer.\n const canonicalTexts = assembler.textBlocks()\n .filter(({ block }) => !retainedPrefix || block.text.trim() !== '')\n let textPosition = 0\n const classifiedRaw = classifyTextPhases(rawBlocks.map(block => {\n if (block.type !== 'text') return block\n const canonical = canonicalTexts[textPosition++]\n // Safe-prefix recovery removes native calls too. Retain their position so\n // search narration cannot become an answer merely because a call was removed.\n return block.phase === undefined && canonical?.beforeNativeCall\n ? { ...block, phase: 'commentary' as const } : block\n }), phase === 'process', assembler.hasToolCalls)\n // A repeated id costs the model that one call, not its whole turn.\n const deduped = dropDuplicateToolCalls(classifiedRaw, options.history)\n const classified = deduped.blocks\n const structuredOutputFailure = finalOutput\n && options.outputFormat?.type === 'json_schema'\n && providerFinish.kind === 'stop'\n && !isJsonText(textOf(classified), options.validateOutput)\n ? {\n message: 'model returned invalid JSON or failed the structured output validator',\n code: MODEL_ERROR_CODES.MALFORMED_RESPONSE,\n }\n : undefined\n const disabledCall = finalOutput && classified.some(block => block.type === 'tool-call')\n ? { message: 'model emitted a host tool call during the final output phase', code: 'INVALID_TOOL_CALL' }\n : undefined\n const invalidCall = disabledCall\n ?? structuredOutputFailure\n ?? invalidHostToolCall(classified, options.history)\n const rawContent = invalidCall === undefined\n ? classified\n : classified.filter(block => block.type !== 'tool-call')\n // A control reply cannot confirm an answer while dispatching more work.\n // Strip it before persistence so tool-call identities are recorded only once.\n const blocks = rawContent.some(block => block.type === 'tool-call')\n ? rawContent.map(block => block.type === 'text'\n ? { ...block, text: block.text.replaceAll(UNCHANGED_ANSWER_MARKER, '') } : block)\n : rawContent\n const finish: FinishReason = invalidCall === undefined\n ? providerFinish\n : { kind: 'error', failure: invalidCall }\n const message = blocks.length === 0\n ? undefined\n : createAssistant(options, blocks, retainedPrefix ? undefined : assembler.replayState)\n const calls = message?.content\n .filter((block): block is ToolCallBlock => block.type === 'tool-call')\n .map(block => ({ callId: block.id, toolName: block.name, rawArguments: block.arguments })) ?? []\n const timing = contentTiming(calls.length > 0, afterToolCallIds.length > 0)\n // Deltas may have no phase, or block-end may correct it. Publish the\n // canonical snapshot before consumers close this step, including providers\n // that only emit block-end. Keep provider indexes (which may be sparse).\n if (blocks.some(block => block.type === 'text')) {\n const indexes = canonicalTexts.map(({ index }) => index)\n const incomplete = finish.kind === 'error' || finish.kind === 'aborted' || finish.kind === 'max-tokens'\n let position = 0\n for (const block of blocks) {\n if (block.type !== 'text') continue\n const index = indexes[position++]\n if (index !== undefined) await emit({\n type: 'text-end', index, text: block.text,\n phase: block.phase ?? 'final-answer', trace,\n ...incomplete ? { incomplete: true as const } : {},\n })\n }\n }\n await emit({\n type: 'span-end', trace, at: now(),\n status: finish.kind === 'error' ? 'error' : finish.kind === 'aborted' ? 'aborted' : 'success',\n output: message === undefined ? undefined : {\n text: textOf(message.content),\n commentary: message.content.flatMap(block => block.type === 'text' && block.phase === 'commentary' ? [block.text] : []),\n reasoning: message.content.flatMap(block => block.type === 'reasoning' ? [block.text] : []),\n toolCalls: calls.length,\n },\n ...assembler.usage === undefined ? {} : { usage: assembler.usage },\n ...finish.kind === 'error' || finish.kind === 'aborted'\n ? { error: { type: 'ModelError', message: finish.failure.message, code: finish.failure.code } }\n : {},\n })\n if (calls.length === 0 || finalOutput) await emit({ type: 'step-end', turn, step, trace })\n return {\n trace, ...message === undefined ? {} : { message }, finish,\n ...assembler.usage === undefined ? {} : { usage: assembler.usage },\n ...modelCallReport === undefined ? {} : { report: modelCallReport },\n ...usageRequired ? { usageRequired: true as const } : {},\n ...usageUnavailable ? { usageUnavailable: true as const } : {},\n calls, afterToolCallIds, timing,\n ...deduped.dropped.length === 0 ? {} : { droppedDuplicateCalls: deduped.dropped },\n }\n}\n\nfunction isJsonText(value: string, validate?: (value: unknown) => void): boolean {\n try {\n const parsed: unknown = JSON.parse(value)\n validate?.(parsed)\n return true\n } catch {\n return false\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"model-round.js","names":[],"sources":["../../../../src/agent/loop/turn/model-round.ts"],"sourcesContent":["import type { GenerateOptions } from '../../../contract/index.ts'\nimport { UNCHANGED_ANSWER_MARKER } from '../control-text.ts'\nimport { MODEL_ERROR_CODES, type ModelFailure } from '../../../errors/index.ts'\nimport type { ContentBlock, Message, ToolCallBlock } from '../../../message/index.ts'\nimport { BlockAssembler } from '../../../stream/index.ts'\nimport type { FinishReason } from '../../../stream/index.ts'\nimport type { ModelCallReport } from '../../../observation/index.ts'\nimport { normalizeToolPairing } from '../../history/normalize.ts'\nimport { createSpanId, type TraceRef } from '../../trace/trace.ts'\nimport type { AgentEvent, AgentMaintenanceEvent } from '../types.ts'\nimport { type ModelRoundPhase, type RunTurnOptions, type RoundResult } from './types.ts'\nimport { positiveFinite, positiveSafeInteger } from './config.ts'\nimport { serializedBytes, modelFailureFinish, modelAbortedFinish, messageOf, now } from './common.ts'\nimport { validateStreamChunk } from './validation.ts'\nimport { StreamAbortError, nextWithAbort, closeIterator } from './cancellation.ts'\nimport { runOptionalHook } from './hooks.ts'\nimport { stepProjectionSources } from './step-projection.ts'\nimport { observeModelRequestBoundary } from './model-request-boundary.ts'\nimport { accountingUsageStop } from './usage-stop.ts'\nimport {\n classifyTextPhases, dropDuplicateToolCalls, invalidHostToolCall, contentTiming,\n recentToolResultIds, systemText,\n createAssistant, textOf,\n} from './content.ts'\n\n/** How much of one message the trace keeps. */\nconst PREVIEW_CHARS = 600\n\n/** How many trailing messages the trace keeps. */\nconst PREVIEW_MESSAGES = 8\n\nfunction clip(text: string): string {\n const flattened = text.replace(/\\s+/gu, ' ').trim()\n return flattened.length > PREVIEW_CHARS ? `${flattened.slice(0, PREVIEW_CHARS)}…` : flattened\n}\n\n/** One message as a line: who spoke, and what the model could read of it. */\nfunction previewMessage(message: Message): Record<string, unknown> {\n const parts: string[] = []\n const calls: string[] = []\n for (const block of message.content) {\n if (block.type === 'text' || block.type === 'reasoning') parts.push(block.text)\n else if (block.type === 'tool-call') calls.push(`${block.name}(${clip(block.arguments)})`)\n else if (block.type === 'tool-result') parts.push(textOf(block.content))\n else parts.push(`[${block.type}]`)\n }\n return {\n role: message.role,\n producer: message.source.kind,\n ...parts.length === 0 ? {} : { text: clip(parts.join(' ')) },\n ...calls.length === 0 ? {} : { toolCalls: calls },\n }\n}\n\n/**\n * What this round sent the model, in a form a person can read.\n *\n * A summary, not a copy: the request can be the whole conversation plus every\n * tool schema, and a trace that stored it verbatim would be larger than the\n * work it describes. What survives is the part that explains the round — which\n * tools were on offer, how long the context was, and the tail of the\n * conversation the model was actually answering, each message clipped.\n * @param request - The request about to be dispatched.\n * @returns The summary for the span.\n */\nfunction requestSummary(request: GenerateOptions): Record<string, unknown> {\n const messages = request.messages ?? []\n const dropped = Math.max(0, messages.length - PREVIEW_MESSAGES)\n return {\n messageCount: messages.length,\n // The identity of the request, for a host that records the provider call\n // separately and has to say WHICH round each recording belongs to. Message\n // ids are stable across every representation boundary, so the last one plus\n // the count names one round of one agent without ambiguity.\n ...messages.at(-1) === undefined ? {} : { lastMessageId: messages.at(-1)?.id },\n ...request.system === undefined ? {} : { system: clip(request.system) },\n ...request.tools === undefined || request.tools.length === 0\n ? {}\n : { tools: request.tools.map(tool => tool.name) },\n ...request.toolChoice === undefined ? {} : { toolChoice: request.toolChoice },\n ...dropped === 0 ? {} : { earlierMessagesOmitted: dropped },\n messages: messages.slice(-PREVIEW_MESSAGES).map(previewMessage),\n }\n}\n\nexport async function modelRound(\n options: RunTurnOptions,\n signal: AbortSignal,\n emit: (event: AgentEvent) => Promise<void>,\n emitMaintenance: (event: AgentMaintenanceEvent) => Promise<void>,\n root: TraceRef,\n turn: number,\n step: number,\n phase: ModelRoundPhase,\n position: { readonly workStep: number; readonly finalizing: boolean } = { workStep: step, finalizing: false },\n): Promise<RoundResult> {\n const forcedFinal = phase === 'forced-final'\n const finalOutput = phase === 'final' || forcedFinal\n const trace: TraceRef = { traceId: root.traceId, spanId: createSpanId(), parentSpanId: root.spanId }\n let messages = normalizeToolPairing(options.history.messages())\n const initialMessages = messages\n const afterToolCallIds = recentToolResultIds(options.history)\n const generation = options.history.generation()\n const entries = options.history.entries().length\n const stopped = (): RoundResult | undefined => {\n const stop = accountingUsageStop(options.accounting)\n if (stop === undefined && !signal.aborted) return undefined\n return {\n trace, finish: signal.aborted\n ? { kind: 'aborted', failure: { code: 'ABORTED', message: messageOf(signal.reason) } }\n : { kind: 'stop' },\n calls: [], afterToolCallIds, timing: contentTiming(false, afterToolCallIds.length > 0),\n ...(stop?.kind === 'error' ? { usageRequired: true } : {}),\n ...(stop?.kind === 'usage-unavailable' ? { usageUnavailable: true } : {}),\n }\n }\n const beforeMaintenance = stopped()\n if (beforeMaintenance !== undefined) return beforeMaintenance\n const decision = await runOptionalHook(options.hooks?.beforeStep, [{\n turn, step, workStep: position.workStep, phase, ...position.finalizing ? { finalizing: true as const } : {},\n messages, snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n emit: emitMaintenance,\n }], options, signal, 'beforeStep')\n const afterMaintenance = stopped()\n if (afterMaintenance !== undefined) return afterMaintenance\n if (decision?.kind === 'reject') {\n const failure: ModelFailure = { message: decision.reason, code: 'STEP_REJECTED' }\n return {\n trace, finish: { kind: 'error', failure }, calls: [], afterToolCallIds,\n timing: contentTiming(false, afterToolCallIds.length > 0),\n }\n }\n // Hooks may append live steering as well as replacing compacted history.\n // Refresh for either mutation so new user input stays at the chronological\n // tail instead of needing to be prepended ahead of older history.\n if (options.history.generation() !== generation\n || options.history.entries().length !== entries) {\n messages = normalizeToolPairing(options.history.messages())\n }\n if (decision?.messages !== undefined) {\n const originalIds = stepProjectionSources(decision) ?? new Set(initialMessages.map(message => message.id))\n const projectedIds = new Set(decision.messages.map(message => message.id))\n const introduced = messages.filter(message => !originalIds.has(message.id) && !projectedIds.has(message.id))\n const currentIds = new Set(messages.map(message => message.id))\n const replaced = [...originalIds].some(id => !currentIds.has(id))\n // A hook can await external I/O while live steering or replacement arrives.\n // Replacements invalidate a stale projection; append-only steering stays at the tail.\n const projectedById = new Map(decision.messages.map(message => [message.id, message]))\n messages = normalizeToolPairing(replaced\n ? [...decision.messages.filter(message => !originalIds.has(message.id) && !currentIds.has(message.id) && message.source.kind === 'app'),\n ...messages.flatMap(message => {\n const projected = projectedById.get(message.id)\n return projected === undefined ? originalIds.has(message.id) ? [] : [message] : [projected]\n })]\n : [...decision.messages, ...introduced])\n }\n if (decision?.prepend !== undefined) messages = Object.freeze([...decision.prepend, ...messages])\n const system = systemText(options, forcedFinal)\n const availableTools = [\n ...options.tools?.schemas() ?? [],\n ...options.nativeTools ?? [],\n ]\n const tools = availableTools\n const outputFormat = !finalOutput && options.outputFormat?.type === 'json_schema'\n ? { type: 'text' as const }\n : options.outputFormat\n const requestBase: GenerateOptions = {\n ...options.config,\n messages,\n ...system.length === 0 ? {} : { system },\n ...tools.length === 0 ? {} : { tools },\n ...finalOutput && tools.length > 0\n ? { toolChoice: 'none' as const }\n : options.toolChoice === undefined ? {} : { toolChoice: options.toolChoice },\n ...options.imagePolicy === undefined ? {} : { imagePolicy: options.imagePolicy },\n ...options.documentPolicy === undefined ? {} : { documentPolicy: options.documentPolicy },\n ...outputFormat === undefined ? {} : { outputFormat },\n }\n const checkpointRequest: GenerateOptions = { ...requestBase, signal }\n observeModelRequestBoundary(options.history, true)\n try {\n await runOptionalHook(options.hooks?.checkpoint, [{\n kind: 'before-model-request', request: checkpointRequest,\n snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n }], options, signal, 'checkpoint')\n } catch (error: unknown) {\n // No output will be recorded for this request. Release queued input before\n // recovery hooks or the retry's beforeStep can read or inject into history.\n observeModelRequestBoundary(options.history, false)\n const failure: ModelFailure = { message: `history checkpoint failed: ${messageOf(error)}`, code: 'CHECKPOINT_FAILED' }\n return {\n trace, finish: { kind: 'error', failure }, calls: [], afterToolCallIds,\n timing: contentTiming(false, afterToolCallIds.length > 0),\n }\n }\n const beforeDispatch = stopped()\n if (beforeDispatch !== undefined) {\n observeModelRequestBoundary(options.history, false)\n return beforeDispatch\n }\n await emit({ type: 'span-start', trace, at: now(), name: `chat ${options.config.model}`, kind: 'chat', attributes: {\n 'gen_ai.operation.name': 'chat', 'gen_ai.request.model': options.config.model,\n // The effort is part of WHICH call this was: the same model at minimal and\n // at high is two different requests, priced and paced differently, and a\n // trace that omits it cannot explain either. Absent when the route has no\n // effort ladder, rather than reported as a default nobody chose.\n ...options.config.reasoningEffort === undefined\n ? {}\n : { 'gen_ai.request.reasoning_effort': options.config.reasoningEffort },\n turn, step, forcedFinal, phase,\n }, input: requestSummary(requestBase) })\n await emit({ type: 'step-start', turn, step, trace, ...forcedFinal ? { forcedFinal: true as const } : {} })\n const assembler = new BlockAssembler()\n const closedBlockIndexes = new Set<number>()\n const modelTimeoutMs = positiveFinite(options.modelTimeoutMs ?? 10 * 60_000, 'modelTimeoutMs')\n const maxRequestBytes = positiveSafeInteger(options.maxModelRequestBytes ?? 32 * 1024 * 1024, 'maxModelRequestBytes')\n const maxResponseBytes = positiveSafeInteger(options.maxModelResponseBytes ?? 32 * 1024 * 1024, 'maxModelResponseBytes')\n const maxStreamEvents = positiveSafeInteger(options.maxModelStreamEvents ?? 100_000, 'maxModelStreamEvents')\n const teardownTimeoutMs = positiveFinite(options.teardownTimeoutMs ?? 30_000, 'teardownTimeoutMs')\n let modelCallReport: ModelCallReport | undefined\n let usageRequired = false\n let usageUnavailable = false\n let requestBytes: number\n try {\n requestBytes = serializedBytes(requestBase)\n } catch (error: unknown) {\n requestBytes = -1\n assembler.push(modelFailureFinish(`model request is not serializable: ${messageOf(error)}`, 'INVALID_MODEL_REQUEST'))\n }\n if (requestBytes > maxRequestBytes) {\n assembler.push(modelFailureFinish(\n `model request exceeds the ${maxRequestBytes}-byte limit`,\n 'MODEL_REQUEST_TOO_LARGE',\n ))\n } else if (requestBytes >= 0) {\n const owned = new AbortController()\n const roundSignal = AbortSignal.any([signal, owned.signal])\n let timedOut = false\n const timeout = setTimeout(() => {\n timedOut = true\n owned.abort(new Error(`model stream exceeded ${modelTimeoutMs}ms`))\n }, modelTimeoutMs)\n const handle = options.registry.stream(\n { ...requestBase, signal: roundSignal },\n options.accounting?.modelInvocation,\n )\n const iterator = handle[Symbol.asyncIterator]()\n let exhausted = false\n let events = 0\n let responseBytes = 0\n let sawFinish = false\n try {\n while (true) {\n const next = await nextWithAbort(iterator.next(), roundSignal)\n if (next.done === true) {\n exhausted = true\n break\n }\n const chunk = next.value\n try {\n validateStreamChunk(chunk, maxStreamEvents)\n } catch (error: unknown) {\n assembler.push(modelFailureFinish(\n `model adapter emitted an invalid stream chunk: ${messageOf(error)}`,\n 'INVALID_MODEL_STREAM',\n ))\n owned.abort(new Error('invalid model stream chunk'))\n break\n }\n events++\n try {\n responseBytes += serializedBytes(chunk)\n } catch (error: unknown) {\n assembler.push(modelFailureFinish(\n `model adapter emitted a non-serializable stream chunk: ${messageOf(error)}`,\n 'INVALID_MODEL_STREAM',\n ))\n owned.abort(new Error('non-serializable model stream chunk'))\n break\n }\n if (events > maxStreamEvents || responseBytes > maxResponseBytes) {\n const dimension = events > maxStreamEvents\n ? `${maxStreamEvents}-event`\n : `${maxResponseBytes}-byte`\n assembler.push(modelFailureFinish(\n `model response exceeds the ${dimension} limit`,\n 'MODEL_RESPONSE_TOO_LARGE',\n ))\n owned.abort(new Error('model response resource limit exceeded'))\n break\n }\n if (sawFinish) {\n assembler.push(modelFailureFinish(\n 'model adapter emitted data after its terminal finish chunk',\n 'INVALID_MODEL_STREAM',\n ))\n owned.abort(new Error('invalid model stream'))\n break\n }\n assembler.push(chunk)\n // The assembler ignores deltas after the first close. The visible\n // stream must do the same or UI text can disagree with stored history.\n if ((chunk.type === 'text-delta' || chunk.type === 'reasoning-delta' || chunk.type === 'tool-call-delta')\n && closedBlockIndexes.has(chunk.index)) continue\n if (chunk.type === 'block-end') closedBlockIndexes.add(chunk.index)\n if (chunk.type === 'finish') sawFinish = true\n if (chunk.type === 'text-delta') await emit({\n type: 'text-delta', index: chunk.index, text: chunk.text,\n phase: phase === 'process' ? 'commentary' : chunk.phase ?? 'unknown', trace,\n })\n else if (chunk.type === 'reasoning-delta') await emit({\n type: 'reasoning-delta', index: chunk.index, text: chunk.text, trace,\n })\n else if (chunk.type === 'image-delta') await emit({\n type: 'image-delta', itemId: chunk.itemId, data: chunk.data,\n mediaType: chunk.mediaType, ...chunk.partialIndex === undefined ? {} : { partialIndex: chunk.partialIndex }, trace,\n })\n else if (chunk.type === 'usage') await emit({ type: 'usage', usage: chunk.usage, trace })\n else if (chunk.type === 'usage-progress') await emit({ ...chunk, trace })\n }\n } catch (error: unknown) {\n if (!(error instanceof StreamAbortError)) throw error\n assembler.push(signal.aborted\n ? modelAbortedFinish(messageOf(signal.reason ?? error))\n : timedOut\n ? modelFailureFinish(`model stream exceeded ${modelTimeoutMs}ms`, 'MODEL_TIMEOUT')\n : modelFailureFinish(messageOf(error.cause ?? error), 'MODEL_STREAM_ABORTED'))\n } finally {\n clearTimeout(timeout)\n if (!exhausted) {\n owned.abort(new Error('model stream closed before exhaustion'))\n const settled = await closeIterator(iterator, teardownTimeoutMs)\n if (!settled) {\n assembler.push(modelFailureFinish(\n `model stream ignored cancellation for more than ${teardownTimeoutMs}ms; the adapter operation may still be running`,\n 'MODEL_TEARDOWN_TIMEOUT',\n ))\n }\n }\n }\n const report = await handle.report\n const decision = options.accounting === undefined\n ? undefined\n : await options.accounting.recordModelCall(report, { ...requestBase, signal })\n modelCallReport = decision?.report ?? report\n usageRequired = decision?.usageRequired ?? false\n usageUnavailable = decision?.usageUnavailable ?? false\n }\n let providerFinish = assembler.finish\n let retainedPrefix = providerFinish.kind === 'aborted'\n let rawBlocks: ContentBlock[]\n try {\n rawBlocks = providerFinish.kind === 'aborted' ? assembler.interruptedBlocks() : assembler.blocks()\n } catch (error: unknown) {\n providerFinish = {\n kind: 'error',\n failure: {\n message: `model adapter emitted an invalid block sequence: ${messageOf(error)}`,\n code: 'INVALID_MODEL_STREAM',\n },\n }\n // A broken extension must not discard text already delivered to the user.\n // This prefix deliberately excludes tools and unassembled extension data.\n retainedPrefix = true\n rawBlocks = assembler.interruptedBlocks()\n }\n // Interrupted/truncated assembly drops unfinished calls, but the text that\n // introduced those calls is still process narration, not a final answer.\n const canonicalTexts = assembler.textBlocks()\n .filter(({ block }) => !retainedPrefix || block.text.trim() !== '')\n let textPosition = 0\n const phaseHinted = rawBlocks.map(block => {\n if (block.type !== 'text') return block\n const canonical = canonicalTexts[textPosition++]\n // Safe-prefix recovery removes native calls too. Retain their position so\n // search narration cannot become an answer merely because a call was removed.\n return block.phase === undefined && canonical?.beforeNativeCall\n ? { ...block, phase: 'commentary' as const } : block\n })\n // A forced answer that also reaches for a tool it cannot use still answered,\n // when the stream itself ended normally: drop the call and keep the answer\n // rather than failing the run. Only text the provider labelled as the answer,\n // or unlabelled text written after the last call, counts: unlabelled text\n // before a call is its preamble (\"let me search…\"), and provider-labelled\n // commentary stays commentary. A round with no answer text keeps the call and\n // is reported as invalid below.\n const lastCall = phaseHinted.findLastIndex(block => block.type === 'tool-call')\n const strippedCandidate = forcedFinal\n && (providerFinish.kind === 'tool-calls' || providerFinish.kind === 'stop')\n && lastCall >= 0\n ? phaseHinted.flatMap((block, index): ContentBlock[] => block.type === 'tool-call' ? []\n : block.type === 'text' && block.phase === undefined\n ? [{ ...block, phase: index > lastCall ? 'final-answer' as const : 'commentary' as const }]\n : [block])\n : undefined\n const strippedForcedCalls = strippedCandidate !== undefined\n && strippedCandidate.some(block => block.type === 'text' && block.phase !== 'commentary' && block.text.trim() !== '')\n const classifiedRaw = strippedForcedCalls\n ? strippedCandidate!\n : classifyTextPhases(phaseHinted, phase === 'process', assembler.hasToolCalls)\n // A repeated id costs the model that one call, not its whole turn.\n const deduped = dropDuplicateToolCalls(classifiedRaw, options.history)\n const classified = deduped.blocks\n const structuredOutputFailure = finalOutput\n && options.outputFormat?.type === 'json_schema'\n && (providerFinish.kind === 'stop' || strippedForcedCalls)\n && !isJsonText(textOf(classified), options.validateOutput)\n ? {\n message: 'model returned invalid JSON or failed the structured output validator',\n code: MODEL_ERROR_CODES.MALFORMED_RESPONSE,\n }\n : undefined\n const disabledCall = finalOutput && classified.some(block => block.type === 'tool-call')\n ? { message: 'model emitted a host tool call during the final output phase', code: 'INVALID_TOOL_CALL' }\n : undefined\n const invalidCall = disabledCall\n ?? structuredOutputFailure\n ?? invalidHostToolCall(classified, options.history)\n const rawContent = invalidCall === undefined\n ? classified\n : classified.filter(block => block.type !== 'tool-call')\n // A control reply cannot confirm an answer while dispatching more work.\n // Strip it before persistence so tool-call identities are recorded only once.\n const blocks = rawContent.some(block => block.type === 'tool-call')\n ? rawContent.map(block => block.type === 'text'\n ? { ...block, text: block.text.replaceAll(UNCHANGED_ANSWER_MARKER, '') } : block)\n : rawContent\n // A broken stream is the real failure, and a retryable one; a malformed\n // call in what it left behind must not hide it.\n const finish: FinishReason = providerFinish.kind === 'error' || providerFinish.kind === 'aborted'\n ? providerFinish\n : invalidCall === undefined\n ? strippedForcedCalls ? { kind: 'stop' } : providerFinish\n : { kind: 'error', failure: invalidCall }\n const message = blocks.length === 0\n ? undefined\n : createAssistant(options, blocks, retainedPrefix ? undefined : assembler.replayState)\n const calls = message?.content\n .filter((block): block is ToolCallBlock => block.type === 'tool-call')\n .map(block => ({ callId: block.id, toolName: block.name, rawArguments: block.arguments })) ?? []\n const timing = contentTiming(calls.length > 0, afterToolCallIds.length > 0)\n // Deltas may have no phase, or block-end may correct it. Publish the\n // canonical snapshot before consumers close this step, including providers\n // that only emit block-end. Keep provider indexes (which may be sparse).\n if (blocks.some(block => block.type === 'text')) {\n const indexes = canonicalTexts.map(({ index }) => index)\n const incomplete = finish.kind === 'error' || finish.kind === 'aborted' || finish.kind === 'max-tokens'\n let position = 0\n for (const block of blocks) {\n if (block.type !== 'text') continue\n const index = indexes[position++]\n if (index !== undefined) await emit({\n type: 'text-end', index, text: block.text,\n phase: block.phase ?? 'final-answer', trace,\n ...incomplete ? { incomplete: true as const } : {},\n })\n }\n }\n await emit({\n type: 'span-end', trace, at: now(),\n status: finish.kind === 'error' ? 'error' : finish.kind === 'aborted' ? 'aborted' : 'success',\n output: message === undefined ? undefined : {\n text: textOf(message.content),\n commentary: message.content.flatMap(block => block.type === 'text' && block.phase === 'commentary' ? [block.text] : []),\n reasoning: message.content.flatMap(block => block.type === 'reasoning' ? [block.text] : []),\n toolCalls: calls.length,\n },\n ...assembler.usage === undefined ? {} : { usage: assembler.usage },\n ...finish.kind === 'error' || finish.kind === 'aborted'\n ? { error: { type: 'ModelError', message: finish.failure.message, code: finish.failure.code } }\n : {},\n })\n if (calls.length === 0 || finalOutput) await emit({ type: 'step-end', turn, step, trace })\n return {\n trace, ...message === undefined ? {} : { message }, finish,\n ...assembler.usage === undefined ? {} : { usage: assembler.usage },\n ...modelCallReport === undefined ? {} : { report: modelCallReport },\n ...usageRequired ? { usageRequired: true as const } : {},\n ...usageUnavailable ? { usageUnavailable: true as const } : {},\n calls, afterToolCallIds, timing,\n ...deduped.dropped.length === 0 ? {} : { droppedDuplicateCalls: deduped.dropped },\n }\n}\n\nfunction isJsonText(value: string, validate?: (value: unknown) => void): boolean {\n try {\n const parsed: unknown = JSON.parse(value)\n validate?.(parsed)\n return true\n } catch {\n return false\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { SpanId, TraceId } from "../../../observation/context.js";
2
2
  import { SdkLogger } from "../../../logging/types.js";
3
+ import { Message } from "../../../message/message.js";
3
4
  import { NativeToolSchema, ToolChoice } from "../../../contract/tool.js";
4
5
  import { ModelOutputFormat } from "../../../contract/output-format.js";
5
6
  import { CallConfig } from "../../../contract/call-config.js";
@@ -8,14 +9,46 @@ import { History } from "../../history/history.js";
8
9
  import { ApprovalBroker } from "../../tool/approval.js";
9
10
  import { ToolCatalog } from "../../tool/registry.js";
10
11
  import { ToolInterceptor } from "../../tool/pipeline.js";
11
- import { TurnHooks } from "../events.js";
12
+ import { TurnEndReason, TurnHooks } from "../events.js";
12
13
  import { TurnBounds } from "../types.js";
13
14
  import { ExperimentalProgramGrant } from "../../tool/nested.js";
14
15
  import { ModelRegistry } from "../../../runtime/registry.js";
15
16
  import { RunAccountingPort } from "../../accounting/contracts.js";
16
17
  import { ContextSection } from "../../context/types.js";
17
18
  //#region src/agent/loop/turn/types.d.ts
19
+ /** How a mode confirms an answer its budget forced; see `RunTurnOptions.finalize`. */
20
+ interface TurnFinalize {
21
+ /** The only tools that may run in the window, budget-exempt or not. */
22
+ readonly tools: readonly string[];
23
+ /**
24
+ * The instruction that opens the window, given the forced answer; undefined
25
+ * ends the turn on that answer as before.
26
+ */
27
+ prompt(forced: {
28
+ readonly text: string;
29
+ readonly reason: Extract<TurnEndReason, {
30
+ kind: 'budget-exhausted';
31
+ } | {
32
+ kind: 'completed';
33
+ }>;
34
+ }): Message | undefined;
35
+ /**
36
+ * Whether the window confirmed an answer. Only then may text written in the
37
+ * window replace the forced answer; a model that declines to confirm often
38
+ * writes a short note about what is missing, which must not erase the
39
+ * evidence-bearing answer it qualifies.
40
+ */
41
+ confirmed(): boolean;
42
+ }
18
43
  interface RunTurnOptions {
44
+ /**
45
+ * Lets a mode confirm an answer the budget forced, for up to
46
+ * `bounds.finalizeSteps` more steps in which only `tools` run. The turn still
47
+ * ends with the budget reason that forced the answer, and keeps that answer
48
+ * unless the window produced a new one. Without it the turn ends on the
49
+ * forced answer.
50
+ */
51
+ readonly finalize?: TurnFinalize;
19
52
  readonly validateOutput?: (value: unknown) => void;
20
53
  /** strict rejects known text-only models when request history contains images; project permits lossy conversion. */
21
54
  readonly imagePolicy?: 'strict' | 'project';
@@ -86,5 +119,5 @@ interface RunTurnOptions {
86
119
  readonly contextSections?: readonly ContextSection[];
87
120
  }
88
121
  //#endregion
89
- export { RunTurnOptions };
122
+ export { RunTurnOptions, TurnFinalize };
90
123
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/agent/loop/turn/types.ts"],"mappings":";;;;;;;;;;;;;;;;;UAmBiB;WACN,kBAAkB;;WAElB;;WAEA;WACA,UAAU;WACV,QAAQ;WACR,SAAS;WACT,QAAQ;;WAER,uBAAuB;;WAEvB,aAAa;;WAEb,eAAe;WACf;WACA,wBAAwB;WACxB,YAAY;WACZ,SAAS,QAAQ;WACjB,QAAQ;WACR,SAAS;;WAET,SAAS;;WAET;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;WACA;aACE,UAAU;aACV,eAAe;aACf;aACA;aACA;;;WAGF,aAAa;;;;;;;;WAQb,aAAa;;;;;WAKb,gCAAgC;;;;;;;WAOhC,2BAA2B"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/agent/loop/turn/types.ts"],"mappings":";;;;;;;;;;;;;;;;;;;UAoBiB;;WAEN;;;;;EAKT,OAAO;aAAmB;aAAuB,QAAQ,QAAQ;MAAiB;;MAA+B;;MAAyB;;;;;;;EAO1I;;UAGe;;;;;;;;WAQN,WAAW;WACX,kBAAkB;;WAElB;;WAEA;WACA,UAAU;WACV,QAAQ;WACR,SAAS;WACT,QAAQ;;WAER,uBAAuB;;WAEvB,aAAa;;WAEb,eAAe;WACf;WACA,wBAAwB;WACxB,YAAY;WACZ,SAAS,QAAQ;WACjB,QAAQ;WACR,SAAS;;WAET,SAAS;;WAET;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;WACA;aACE,UAAU;aACV,eAAe;aACf;aACA;aACA;;;WAGF,aAAa;;;;;;;;WAQb,aAAa;;;;;WAKb,gCAAgC;;;;;;;WAOhC,2BAA2B"}
@@ -51,6 +51,22 @@ interface TurnBounds {
51
51
  /** Stop tool work this many tokens before the hard total to reserve a report.
52
52
  * Ignored with maxTotalTokens: 'auto'. Zero disables the reserve. */
53
53
  readonly finalReportReserveTokens: number;
54
+ /**
55
+ * Extra steps a turn may take after its budget forced a final answer, used
56
+ * only when the caller supplies `RunTurnOptions.finalize` (deep mode does,
57
+ * defaulting this to 2). In them only the tools it names run, so a run that
58
+ * answered on its last step can still confirm that answer instead of ending
59
+ * unconfirmed. Zero (the default) ends the turn at the forced answer.
60
+ */
61
+ readonly finalizeSteps: number;
62
+ /**
63
+ * Wall-clock budget for the turn's work, excluding time spent waiting for a
64
+ * person. When it passes, the turn stops working and answers from what it
65
+ * has, like any other spent budget, rather than being cancelled with
66
+ * nothing. A model round already in flight is not interrupted. Default
67
+ * 'auto' imposes no time budget.
68
+ */
69
+ readonly maxTurnDurationMs: number | 'auto';
54
70
  readonly maxParallel: number;
55
71
  /** Maximum serialized bytes retained for one finalized tool result. */
56
72
  readonly maxToolResultBytes: number;