@graphorin/agent 0.6.0 → 0.7.0

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 (110) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +31 -11
  3. package/dist/errors/index.d.ts +17 -4
  4. package/dist/errors/index.d.ts.map +1 -1
  5. package/dist/errors/index.js +19 -3
  6. package/dist/errors/index.js.map +1 -1
  7. package/dist/factory.d.ts.map +1 -1
  8. package/dist/factory.js +153 -1930
  9. package/dist/factory.js.map +1 -1
  10. package/dist/fanout/index.d.ts +13 -1
  11. package/dist/fanout/index.d.ts.map +1 -1
  12. package/dist/fanout/index.js +13 -4
  13. package/dist/fanout/index.js.map +1 -1
  14. package/dist/index.d.ts +7 -6
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +11 -9
  17. package/dist/index.js.map +1 -1
  18. package/dist/lateral-leak/index.js +1 -1
  19. package/dist/package.js +6 -0
  20. package/dist/package.js.map +1 -0
  21. package/dist/run-state/index.d.ts +32 -6
  22. package/dist/run-state/index.d.ts.map +1 -1
  23. package/dist/run-state/index.js +46 -22
  24. package/dist/run-state/index.js.map +1 -1
  25. package/dist/runtime/agent-surface.js +122 -0
  26. package/dist/runtime/agent-surface.js.map +1 -0
  27. package/dist/runtime/agent-to-tool.d.ts +51 -0
  28. package/dist/runtime/agent-to-tool.d.ts.map +1 -0
  29. package/dist/runtime/agent-to-tool.js +145 -0
  30. package/dist/runtime/agent-to-tool.js.map +1 -0
  31. package/dist/runtime/approvals.js +0 -0
  32. package/dist/runtime/approvals.js.map +1 -0
  33. package/dist/runtime/dispatch.js +108 -0
  34. package/dist/runtime/dispatch.js.map +1 -0
  35. package/dist/runtime/executor-wiring.js +128 -0
  36. package/dist/runtime/executor-wiring.js.map +1 -0
  37. package/dist/runtime/fallback-chain.js +139 -0
  38. package/dist/runtime/fallback-chain.js.map +1 -0
  39. package/dist/runtime/handoff.js +307 -0
  40. package/dist/runtime/handoff.js.map +1 -0
  41. package/dist/runtime/messages.d.ts +22 -0
  42. package/dist/runtime/messages.d.ts.map +1 -0
  43. package/dist/runtime/messages.js +204 -0
  44. package/dist/runtime/messages.js.map +1 -0
  45. package/dist/runtime/provider-events.js +117 -0
  46. package/dist/runtime/provider-events.js.map +1 -0
  47. package/dist/runtime/run-compaction.js +210 -0
  48. package/dist/runtime/run-compaction.js.map +1 -0
  49. package/dist/runtime/run-finish.js +48 -0
  50. package/dist/runtime/run-finish.js.map +1 -0
  51. package/dist/runtime/run-gates.js +336 -0
  52. package/dist/runtime/run-gates.js.map +1 -0
  53. package/dist/runtime/run-init.js +81 -0
  54. package/dist/runtime/run-init.js.map +1 -0
  55. package/dist/runtime/run-input.js +46 -0
  56. package/dist/runtime/run-input.js.map +1 -0
  57. package/dist/runtime/step-catalogue.js +173 -0
  58. package/dist/runtime/step-catalogue.js.map +1 -0
  59. package/dist/runtime/tool-call-walk.js +189 -0
  60. package/dist/runtime/tool-call-walk.js.map +1 -0
  61. package/dist/runtime/tool-wiring.js +159 -0
  62. package/dist/runtime/tool-wiring.js.map +1 -0
  63. package/dist/tooling/adapters.js +1 -1
  64. package/dist/tooling/dataflow.js +1 -1
  65. package/dist/tooling/policy.js +2 -0
  66. package/dist/tooling/policy.js.map +1 -1
  67. package/dist/types.d.ts +63 -13
  68. package/dist/types.d.ts.map +1 -1
  69. package/package.json +20 -20
  70. package/src/errors/index.ts +320 -0
  71. package/src/evaluator-optimizer/index.ts +212 -0
  72. package/src/factory.ts +957 -0
  73. package/src/fallback/index.ts +108 -0
  74. package/src/fanout/index.ts +523 -0
  75. package/src/filters/index.ts +347 -0
  76. package/src/index.ts +180 -0
  77. package/src/internal/ids.ts +46 -0
  78. package/src/internal/usage-accumulator.ts +90 -0
  79. package/src/lateral-leak/causality-monitor.ts +221 -0
  80. package/src/lateral-leak/index.ts +35 -0
  81. package/src/lateral-leak/merge-guard.ts +151 -0
  82. package/src/lateral-leak/protocol-guard.ts +222 -0
  83. package/src/preferred-model/index.ts +210 -0
  84. package/src/progress/index.ts +238 -0
  85. package/src/run-state/index.ts +607 -0
  86. package/src/runtime/agent-surface.ts +218 -0
  87. package/src/runtime/agent-to-tool.ts +323 -0
  88. package/src/runtime/approvals.ts +0 -0
  89. package/src/runtime/dispatch.ts +183 -0
  90. package/src/runtime/executor-wiring.ts +331 -0
  91. package/src/runtime/fallback-chain.ts +250 -0
  92. package/src/runtime/handoff.ts +428 -0
  93. package/src/runtime/messages.ts +309 -0
  94. package/src/runtime/provider-events.ts +175 -0
  95. package/src/runtime/run-compaction.ts +288 -0
  96. package/src/runtime/run-finish.ts +93 -0
  97. package/src/runtime/run-gates.ts +419 -0
  98. package/src/runtime/run-init.ts +169 -0
  99. package/src/runtime/run-input.ts +102 -0
  100. package/src/runtime/step-catalogue.ts +338 -0
  101. package/src/runtime/tool-call-walk.ts +301 -0
  102. package/src/runtime/tool-wiring.ts +218 -0
  103. package/src/testing/replay-provider.ts +121 -0
  104. package/src/tooling/adapters.ts +403 -0
  105. package/src/tooling/catalogue.ts +36 -0
  106. package/src/tooling/dataflow.ts +171 -0
  107. package/src/tooling/plan.ts +123 -0
  108. package/src/tooling/policy.ts +67 -0
  109. package/src/tooling/registry-build.ts +191 -0
  110. package/src/types.ts +696 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["out: SerializedRunState","usage: Usage","error: RunState['error']","usageByModel: RunStateUsageByModel | undefined","m: Record<string, Usage & { attemptCount: number }>","taintSummary: RunTaintSummary | undefined","parsed: unknown","out: CompletedToolCall[]"],"sources":["../../src/run-state/index.ts"],"sourcesContent":["/**\n * `RunState` JSON serialization and rehydration.\n *\n * The on-disk shape carries an explicit `version` field so future\n * schema bumps can detect older payloads. v0.1 ships\n * `'graphorin-run-state/1.0'` - additive fields that older readers\n * do not understand are ignored under the lenient-forward-parse\n * discipline.\n *\n * @packageDocumentation\n */\n\nimport type {\n CompletedToolCall,\n HandoffRecord,\n Message,\n RunState,\n RunStateUsageByModel,\n RunStatus,\n RunStep,\n RunTaintSummary,\n TodoItem,\n ToolApproval,\n Usage,\n} from '@graphorin/core';\nimport { zeroUsage } from '@graphorin/core';\nimport { RunStateMalformedError, RunStateVersionUnsupportedError } from '../errors/index.js';\n\n/**\n * Canonical schema id for serialized {@link RunState} payloads.\n *\n * @stable\n */\nexport const RUN_STATE_SCHEMA_VERSION = 'graphorin-run-state/1.1' as const;\n\n/**\n * Reader-supported schema id range. Major version 1 only for v0.1.\n *\n * @stable\n */\nexport const RUN_STATE_SCHEMA_MAJOR_SUPPORTED = 1;\n\n/**\n * On-disk payload returned by {@link serializeRunState} and accepted\n * by {@link deserializeRunState}. The shape is JSON-stable.\n *\n * @stable\n */\nexport interface SerializedRunState {\n readonly version: typeof RUN_STATE_SCHEMA_VERSION;\n readonly id: string;\n readonly agentId: string;\n readonly currentAgentId: string;\n readonly sessionId: string;\n readonly userId?: string;\n readonly status: RunStatus;\n readonly steps: ReadonlyArray<RunStep>;\n readonly messages: ReadonlyArray<Message>;\n readonly pendingApprovals: ReadonlyArray<ToolApproval>;\n readonly handoffs: ReadonlyArray<HandoffRecord>;\n readonly usage: Usage;\n readonly usageByModel?: RunStateUsageByModel;\n /** AG-19: coarse data-flow taint summary (no untrusted text). */\n readonly taintSummary?: RunTaintSummary;\n /** AG-19: deferred tools promoted by `tool_search` this run. */\n readonly promotedTools?: ReadonlyArray<string>;\n /** D6: journaled structured plan/todo list. */\n readonly todos?: ReadonlyArray<TodoItem>;\n readonly startedAt: string;\n readonly finishedAt?: string;\n readonly error?: { readonly message: string; readonly code: string; readonly details?: unknown };\n}\n\n/**\n * Options accepted by {@link serializeRunState}.\n *\n * @stable\n */\nexport interface SerializeRunStateOptions {\n /**\n * Deep-redact secret-named keys (`apiKey`, `authorization`,\n * `bearerToken` / `accessToken` / `refreshToken`, `password`,\n * `secret`, …) anywhere in the snapshot - tool results and messages\n * included - replacing their values with `'[redacted]'`. Defaults to\n * `false` for the round-trip canonical helper; the agent runtime\n * passes `true` when persisting through the checkpoint store\n * (AG-23). Redaction is best-effort by key name: secrets stored\n * under unrelated keys are not detected.\n */\n readonly stripTracingApiKey?: boolean;\n}\n\n/**\n * Render a JSON-stable snapshot of the supplied {@link RunState}.\n * The returned value is plain JSON (no `Map`, `Set`, `Date`, ...).\n *\n * @stable\n */\nexport function serializeRunState(\n state: RunState,\n options: SerializeRunStateOptions = {},\n): SerializedRunState {\n const out: SerializedRunState = {\n version: RUN_STATE_SCHEMA_VERSION,\n id: state.id,\n agentId: state.agentId,\n currentAgentId: state.currentAgentId,\n sessionId: state.sessionId,\n ...(state.userId !== undefined ? { userId: state.userId } : {}),\n status: state.status,\n steps: state.steps,\n messages: state.messages,\n pendingApprovals: state.pendingApprovals,\n handoffs: state.handoffs,\n usage: state.usage,\n ...(state.usageByModel !== undefined ? { usageByModel: state.usageByModel } : {}),\n ...(state.taintSummary !== undefined ? { taintSummary: state.taintSummary } : {}),\n ...(state.promotedTools !== undefined ? { promotedTools: state.promotedTools } : {}),\n ...(state.todos !== undefined ? { todos: state.todos } : {}),\n startedAt: state.startedAt,\n ...(state.finishedAt !== undefined ? { finishedAt: state.finishedAt } : {}),\n ...(state.error !== undefined ? { error: state.error } : {}),\n };\n // AG-23: the snapshot must be DETACHED from the live MutableRunState -\n // a post-suspend mutation must never reach an already-persisted\n // checkpoint. The JSON round-trip doubles as the plain-JSON guarantee\n // this function documents (no Map/Set/Date survive it).\n const detached = JSON.parse(JSON.stringify(out)) as SerializedRunState;\n if (options.stripTracingApiKey === true) {\n redactSecretKeysInPlace(detached);\n }\n return detached;\n}\n\n/**\n * Key names whose values are redacted by\n * `serializeRunState(state, { stripTracingApiKey: true })`.\n */\nconst SECRET_KEY_PATTERN =\n /^(api[-_]?key|tracing[-_]?api[-_]?key|x-api-key|authorization|bearer[-_]?token|access[-_]?token|refresh[-_]?token|secret|password|passphrase)$/i;\n\nconst REDACTED = '[redacted]';\n\nfunction redactSecretKeysInPlace(value: unknown): void {\n if (typeof value !== 'object' || value === null) return;\n if (Array.isArray(value)) {\n for (const item of value) redactSecretKeysInPlace(item);\n return;\n }\n const record = value as Record<string, unknown>;\n // Tool messages embed the tool output as a JSON STRING in `content` -\n // parse, redact, and re-stringify so a secret inside the string form\n // is caught too.\n if (record.role === 'tool' && typeof record.content === 'string') {\n record.content = redactJsonString(record.content);\n }\n for (const [key, inner] of Object.entries(record)) {\n if (SECRET_KEY_PATTERN.test(key)) {\n record[key] = REDACTED;\n continue;\n }\n redactSecretKeysInPlace(inner);\n }\n}\n\nfunction redactJsonString(content: string): string {\n try {\n const parsed = JSON.parse(content) as unknown;\n if (typeof parsed !== 'object' || parsed === null) return content;\n redactSecretKeysInPlace(parsed);\n return JSON.stringify(parsed);\n } catch {\n return content;\n }\n}\n\n/**\n * Render the canonical JSON string representation of the supplied\n * {@link RunState}. `JSON.stringify(serializeRunState(state))` -\n * provided as a convenience.\n *\n * @stable\n */\nexport function runStateToJSON(state: RunState, options?: SerializeRunStateOptions): string {\n return JSON.stringify(serializeRunState(state, options));\n}\n\ninterface DeserializeOptions {\n /**\n * Synthesize `usageByModel` from a v0.1-alpha state that omits\n * the field. Defaults to `true` so callers can rehydrate older\n * states without explicit migration.\n */\n readonly synthesizeUsageByModel?: boolean;\n /**\n * Logger callback for one-time INFO messages emitted on\n * backwards-compat synthesis. Defaults to a no-op.\n */\n readonly logger?: (message: string) => void;\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/**\n * Rehydrate a {@link RunState} from the on-disk payload. Throws\n * {@link RunStateVersionUnsupportedError} when the payload version\n * is from a future major; throws\n * {@link RunStateMalformedError} when the payload is structurally\n * invalid.\n *\n * Backwards-compat: a payload that omits `usageByModel` is accepted\n * and the field is synthesized from the aggregate `usage` with\n * `attemptCount: 1` for the primary model.\n *\n * @stable\n */\nexport function deserializeRunState(payload: unknown, options: DeserializeOptions = {}): RunState {\n if (!isRecord(payload)) {\n throw new RunStateMalformedError('payload must be an object');\n }\n const versionRaw = payload.version;\n if (typeof versionRaw !== 'string') {\n throw new RunStateMalformedError(\"missing 'version' field\");\n }\n const versionMatch = /^graphorin-run-state\\/(\\d+)\\.(\\d+)$/.exec(versionRaw);\n if (versionMatch === null) {\n throw new RunStateMalformedError(`unrecognized version '${versionRaw}'`);\n }\n const major = Number(versionMatch[1]);\n if (major > RUN_STATE_SCHEMA_MAJOR_SUPPORTED) {\n throw new RunStateVersionUnsupportedError(versionRaw, RUN_STATE_SCHEMA_VERSION);\n }\n const requiredString = (key: string): string => {\n const v = payload[key];\n if (typeof v !== 'string') {\n throw new RunStateMalformedError(`missing string '${key}' field`);\n }\n return v;\n };\n const requiredArray = <T>(key: string): T[] => {\n const v = payload[key];\n if (!Array.isArray(v)) {\n throw new RunStateMalformedError(`missing array '${key}' field`);\n }\n return v as T[];\n };\n const id = requiredString('id');\n const agentId = requiredString('agentId');\n const currentAgentId =\n typeof payload.currentAgentId === 'string' ? payload.currentAgentId : agentId;\n const sessionId = requiredString('sessionId');\n const status = requiredString('status') as RunStatus;\n const steps = requiredArray<RunStep>('steps');\n const messages = requiredArray<Message>('messages');\n const pendingApprovals = requiredArray<ToolApproval>('pendingApprovals');\n const handoffs = requiredArray<HandoffRecord>('handoffs');\n const usageRaw = payload.usage;\n if (!isRecord(usageRaw)) {\n throw new RunStateMalformedError(\"missing object 'usage' field\");\n }\n const usage: Usage = {\n promptTokens: Number(usageRaw.promptTokens ?? 0),\n completionTokens: Number(usageRaw.completionTokens ?? 0),\n totalTokens: Number(usageRaw.totalTokens ?? 0),\n ...(usageRaw.reasoningTokens !== undefined\n ? { reasoningTokens: Number(usageRaw.reasoningTokens) }\n : {}),\n ...(usageRaw.cachedReadTokens !== undefined\n ? { cachedReadTokens: Number(usageRaw.cachedReadTokens) }\n : {}),\n ...(usageRaw.cacheWriteTokens !== undefined\n ? { cacheWriteTokens: Number(usageRaw.cacheWriteTokens) }\n : {}),\n ...(isRecord(usageRaw.cost)\n ? {\n cost: {\n amount: Number(usageRaw.cost.amount ?? 0),\n currency: String(usageRaw.cost.currency ?? 'USD'),\n },\n }\n : {}),\n };\n const startedAt = requiredString('startedAt');\n const userIdRaw = payload.userId;\n const finishedAtRaw = payload.finishedAt;\n const errorRaw = payload.error;\n let error: RunState['error'];\n if (isRecord(errorRaw)) {\n error = {\n message: typeof errorRaw.message === 'string' ? errorRaw.message : 'unknown',\n code: typeof errorRaw.code === 'string' ? errorRaw.code : 'unknown',\n ...(errorRaw.details !== undefined ? { details: errorRaw.details } : {}),\n };\n }\n\n // Backwards-compat synthesis for usageByModel.\n let usageByModel: RunStateUsageByModel | undefined;\n if (isRecord(payload.usageByModel)) {\n const m: Record<string, Usage & { attemptCount: number }> = {};\n for (const [modelId, entryRaw] of Object.entries(payload.usageByModel)) {\n if (!isRecord(entryRaw)) continue;\n m[modelId] = {\n promptTokens: Number(entryRaw.promptTokens ?? 0),\n completionTokens: Number(entryRaw.completionTokens ?? 0),\n totalTokens: Number(entryRaw.totalTokens ?? 0),\n attemptCount: Number(entryRaw.attemptCount ?? 1),\n ...(entryRaw.reasoningTokens !== undefined\n ? { reasoningTokens: Number(entryRaw.reasoningTokens) }\n : {}),\n ...(entryRaw.cachedReadTokens !== undefined\n ? { cachedReadTokens: Number(entryRaw.cachedReadTokens) }\n : {}),\n ...(entryRaw.cacheWriteTokens !== undefined\n ? { cacheWriteTokens: Number(entryRaw.cacheWriteTokens) }\n : {}),\n ...(isRecord(entryRaw.cost)\n ? {\n cost: {\n amount: Number(entryRaw.cost.amount ?? 0),\n currency: String(entryRaw.cost.currency ?? 'USD'),\n },\n }\n : {}),\n };\n }\n usageByModel = m;\n } else if (options.synthesizeUsageByModel !== false && Number.isFinite(usage.totalTokens)) {\n usageByModel = {\n [agentId]: { ...usage, attemptCount: 1 },\n };\n options.logger?.(\n `[graphorin/agent] RunState v0.1-alpha synthesis: per-model breakdown derived from aggregate usage for agent '${agentId}'.`,\n );\n }\n\n // AG-19: rehydrate the coarse taint summary + promoted-tool set (additive;\n // absent on older v1.0 payloads).\n let taintSummary: RunTaintSummary | undefined;\n if (isRecord(payload.taintSummary)) {\n const ts = payload.taintSummary;\n const tileHashes = Array.isArray(ts.spanTileHashes)\n ? ts.spanTileHashes.filter((h): h is string => typeof h === 'string')\n : undefined;\n taintSummary = {\n untrustedSeen: ts.untrustedSeen === true,\n sensitiveSeen: ts.sensitiveSeen === true,\n untrustedSourceKinds: Array.isArray(ts.untrustedSourceKinds)\n ? ts.untrustedSourceKinds.filter((k): k is string => typeof k === 'string')\n : [],\n ...(tileHashes !== undefined && tileHashes.length > 0 ? { spanTileHashes: tileHashes } : {}),\n };\n }\n const promotedTools = Array.isArray(payload.promotedTools)\n ? payload.promotedTools.filter((t): t is string => typeof t === 'string')\n : undefined;\n const todos = Array.isArray(payload.todos)\n ? payload.todos\n .filter(isRecord)\n .filter(\n (t): t is { id: string; content: string; status: TodoItem['status'] } =>\n typeof t.id === 'string' &&\n typeof t.content === 'string' &&\n (t.status === 'pending' || t.status === 'in_progress' || t.status === 'completed'),\n )\n .map((t) => ({ id: t.id, content: t.content, status: t.status }))\n : undefined;\n\n const out: RunState = {\n id,\n agentId,\n currentAgentId,\n sessionId,\n ...(typeof userIdRaw === 'string' ? { userId: userIdRaw } : {}),\n status,\n steps,\n messages,\n pendingApprovals,\n handoffs,\n usage,\n ...(usageByModel !== undefined ? { usageByModel } : {}),\n ...(taintSummary !== undefined ? { taintSummary } : {}),\n ...(promotedTools !== undefined ? { promotedTools } : {}),\n ...(todos !== undefined ? { todos } : {}),\n startedAt,\n ...(typeof finishedAtRaw === 'string' ? { finishedAt: finishedAtRaw } : {}),\n ...(error !== undefined ? { error } : {}),\n };\n void zeroUsage; // kept available; some callers prefer zeroUsage as a default.\n return out;\n}\n\n/** Convenience JSON-string parser pairing with {@link runStateToJSON}. */\nexport function runStateFromJSON(serialized: string, options?: DeserializeOptions): RunState {\n let parsed: unknown;\n try {\n parsed = JSON.parse(serialized);\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n throw new RunStateMalformedError(`invalid JSON: ${message}`);\n }\n return deserializeRunState(parsed, options);\n}\n\n/**\n * Build a fresh, minimal {@link RunState} for a new run. Helper used\n * by `createAgent({...})` so consumers can construct deterministic\n * run state in tests.\n *\n * @stable\n */\nexport function createInitialRunState(args: {\n readonly id: string;\n readonly agentId: string;\n readonly sessionId: string;\n readonly userId?: string;\n readonly startedAt?: string;\n}): RunState {\n return {\n id: args.id,\n agentId: args.agentId,\n currentAgentId: args.agentId,\n sessionId: args.sessionId,\n ...(args.userId !== undefined ? { userId: args.userId } : {}),\n status: 'running',\n steps: [],\n messages: [],\n pendingApprovals: [],\n handoffs: [],\n usage: zeroUsage(),\n startedAt: args.startedAt ?? new Date().toISOString(),\n };\n}\n\n/**\n * Append a per-model usage entry to {@link RunState.usageByModel}.\n * Mutates the supplied state in place - used by the agent runtime's\n * per-step retry loop. Pure callers that need an immutable update\n * should clone the state first.\n *\n * @stable\n */\nexport function addModelUsage(state: RunState, modelId: string, delta: Usage): void {\n const current = (state.usageByModel ?? {}) as Record<string, Usage & { attemptCount: number }>;\n const prev = current[modelId];\n const merged: Usage & { attemptCount: number } = prev\n ? {\n promptTokens: prev.promptTokens + delta.promptTokens,\n completionTokens: prev.completionTokens + delta.completionTokens,\n totalTokens: prev.totalTokens + delta.totalTokens,\n ...(delta.reasoningTokens !== undefined || prev.reasoningTokens !== undefined\n ? {\n reasoningTokens: (prev.reasoningTokens ?? 0) + (delta.reasoningTokens ?? 0),\n }\n : {}),\n ...(delta.cachedReadTokens !== undefined || prev.cachedReadTokens !== undefined\n ? {\n cachedReadTokens: (prev.cachedReadTokens ?? 0) + (delta.cachedReadTokens ?? 0),\n }\n : {}),\n ...(delta.cacheWriteTokens !== undefined || prev.cacheWriteTokens !== undefined\n ? {\n cacheWriteTokens: (prev.cacheWriteTokens ?? 0) + (delta.cacheWriteTokens ?? 0),\n }\n : {}),\n attemptCount: prev.attemptCount + 1,\n ...(prev.cost !== undefined ? { cost: prev.cost } : {}),\n }\n : { ...delta, attemptCount: 1 };\n current[modelId] = merged;\n // Mutate via the writable typing - `usageByModel` is declared\n // optional; the runtime owns the field's lifecycle.\n (state as { usageByModel?: RunStateUsageByModel }).usageByModel = current;\n}\n\n/**\n * Recompute the aggregate usage from `usageByModel`. Returns the\n * sum that callers can compare against `state.usage` to verify the\n * per-step retry loop maintained the documented invariant.\n *\n * @stable\n */\nexport function aggregateUsageFromByModel(byModel: RunStateUsageByModel | undefined): Usage {\n if (byModel === undefined) return zeroUsage();\n let pt = 0;\n let ct = 0;\n let tt = 0;\n let rt = 0;\n let cr = 0;\n let cw = 0;\n let hasReasoning = false;\n let hasCachedRead = false;\n let hasCacheWrite = false;\n for (const entry of Object.values(byModel)) {\n pt += entry.promptTokens;\n ct += entry.completionTokens;\n tt += entry.totalTokens;\n if (entry.reasoningTokens !== undefined) {\n rt += entry.reasoningTokens;\n hasReasoning = true;\n }\n if (entry.cachedReadTokens !== undefined) {\n cr += entry.cachedReadTokens;\n hasCachedRead = true;\n }\n if (entry.cacheWriteTokens !== undefined) {\n cw += entry.cacheWriteTokens;\n hasCacheWrite = true;\n }\n }\n return {\n promptTokens: pt,\n completionTokens: ct,\n totalTokens: tt,\n ...(hasReasoning ? { reasoningTokens: rt } : {}),\n ...(hasCachedRead ? { cachedReadTokens: cr } : {}),\n ...(hasCacheWrite ? { cacheWriteTokens: cw } : {}),\n };\n}\n\n/**\n * The \"tools used\" surface of a completed run. Cheap to compute\n * from `RunState.steps`; surfaced as a stand-alone helper for\n * Phase 17 example apps and operator-facing dashboards.\n *\n * @stable\n */\nexport function completedToolCallsFromState(state: RunState): ReadonlyArray<CompletedToolCall> {\n const out: CompletedToolCall[] = [];\n for (const step of state.steps) {\n for (const tc of step.toolCalls) out.push(tc);\n }\n return out;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["out: SerializedRunState","usage: Usage","error: RunState['error']","usageByModel: RunStateUsageByModel | undefined","m: Record<string, Usage & { attemptCount: number }>","taintSummary: RunTaintSummary | undefined","parsed: unknown","out: CompletedToolCall[]"],"sources":["../../src/run-state/index.ts"],"sourcesContent":["/**\n * `RunState` JSON serialization and rehydration.\n *\n * The on-disk shape carries an explicit `version` field so future\n * schema bumps can detect older payloads. v0.1 ships\n * `'graphorin-run-state/1.0'` - additive fields that older readers\n * do not understand are ignored under the lenient-forward-parse\n * discipline.\n *\n * @packageDocumentation\n */\n\nimport type {\n CompletedToolCall,\n HandoffRecord,\n RunState,\n RunStateUsageByModel,\n RunStatus,\n RunTaintSummary,\n TodoItem,\n ToolApproval,\n Usage,\n WireMessage,\n WireRunState,\n WireRunStep,\n} from '@graphorin/core';\nimport { fromJsonSafeRunState, toJsonSafeRunState, zeroUsage } from '@graphorin/core';\nimport { RunStateMalformedError, RunStateVersionUnsupportedError } from '../errors/index.js';\n\n/**\n * Canonical schema id for serialized {@link RunState} payloads.\n *\n * 1.2 encodes binary message/tool-outcome payloads (`Uint8Array | URL`)\n * through the core `WireRunState` projection (base64 / href envelopes)\n * instead of letting `JSON.stringify` corrupt them. 1.0/1.1 payloads\n * remain readable; their corrupted numeric-key byte objects are\n * repaired best-effort on rehydration.\n *\n * @stable\n */\nexport const RUN_STATE_SCHEMA_VERSION = 'graphorin-run-state/1.2' as const;\n\n/**\n * Reader-supported schema id range. Major version 1 only for v0.1.\n *\n * @stable\n */\nexport const RUN_STATE_SCHEMA_MAJOR_SUPPORTED = 1;\n\n/**\n * On-disk payload returned by {@link serializeRunState} and accepted\n * by {@link deserializeRunState}. The shape is JSON-stable: binary\n * message/tool-outcome payloads appear in their `WireMessage` /\n * `WireRunStep` (base64 / href envelope) form.\n *\n * @stable\n */\nexport interface SerializedRunState {\n readonly version: typeof RUN_STATE_SCHEMA_VERSION;\n readonly id: string;\n readonly agentId: string;\n readonly currentAgentId: string;\n readonly sessionId: string;\n readonly userId?: string;\n readonly status: RunStatus;\n readonly steps: ReadonlyArray<WireRunStep>;\n readonly messages: ReadonlyArray<WireMessage>;\n readonly pendingApprovals: ReadonlyArray<ToolApproval>;\n readonly handoffs: ReadonlyArray<HandoffRecord>;\n readonly usage: Usage;\n readonly usageByModel?: RunStateUsageByModel;\n /** AG-19: coarse data-flow taint summary (no untrusted text). */\n readonly taintSummary?: RunTaintSummary;\n /** AG-19: deferred tools promoted by `tool_search` this run. */\n readonly promotedTools?: ReadonlyArray<string>;\n /** D6: journaled structured plan/todo list. */\n readonly todos?: ReadonlyArray<TodoItem>;\n /**\n * W-001: parked sub-agent runs. Each child snapshot is itself a full\n * `SerializedRunState` - version-stamped and secret-redacted\n * recursively, to any nesting depth.\n */\n readonly pendingSubRuns?: ReadonlyArray<SerializedPendingSubRun>;\n readonly startedAt: string;\n readonly finishedAt?: string;\n readonly error?: { readonly message: string; readonly code: string; readonly details?: unknown };\n}\n\n/**\n * Serialized twin of core's `PendingSubRun` (W-001): the parked child\n * state travels as its own versioned {@link SerializedRunState}.\n *\n * @stable\n */\nexport interface SerializedPendingSubRun {\n readonly toolCallId: string;\n readonly toolName: string;\n readonly targetAgentName: string;\n readonly state: SerializedRunState;\n}\n\n/**\n * Options accepted by {@link serializeRunState}.\n *\n * @stable\n */\nexport interface SerializeRunStateOptions {\n /**\n * Deep-redact secret-named keys (`apiKey`, `authorization`,\n * `bearerToken` / `accessToken` / `refreshToken`, `password`,\n * `secret`, …) anywhere in the snapshot - tool results and messages\n * included - replacing their values with `'[redacted]'`. Defaults to\n * `false` for the round-trip canonical helper; the agent runtime\n * passes `true` when persisting through the checkpoint store\n * (AG-23). Redaction is best-effort by key name: secrets stored\n * under unrelated keys are not detected.\n */\n readonly stripTracingApiKey?: boolean;\n}\n\n/**\n * Render a JSON-stable snapshot of the supplied {@link RunState}.\n * The returned value is plain JSON (no `Map`, `Set`, `Date`, ...).\n *\n * @stable\n */\nexport function serializeRunState(\n state: RunState,\n options: SerializeRunStateOptions = {},\n): SerializedRunState {\n // W-004: binary payloads (Uint8Array | URL in messages and tool-outcome\n // contentParts) must be projected to their JSON-safe wire form BEFORE\n // the detach stringify below, or they corrupt silently.\n const wire = toJsonSafeRunState(state);\n // W-001: each parked child state serializes through THIS function\n // recursively (from the live `state`, not the wire projection) so\n // every nested snapshot carries its own `version` stamp and passes\n // the same secret redaction - a naive stringify would smuggle an\n // unversioned live child through the checkpoint.\n const pendingSubRuns =\n state.pendingSubRuns !== undefined && state.pendingSubRuns.length > 0\n ? state.pendingSubRuns.map(\n (sub): SerializedPendingSubRun => ({\n toolCallId: sub.toolCallId,\n toolName: sub.toolName,\n targetAgentName: sub.targetAgentName,\n state: serializeRunState(sub.state, options),\n }),\n )\n : undefined;\n const out: SerializedRunState = {\n version: RUN_STATE_SCHEMA_VERSION,\n id: wire.id,\n agentId: wire.agentId,\n currentAgentId: wire.currentAgentId,\n sessionId: wire.sessionId,\n ...(wire.userId !== undefined ? { userId: wire.userId } : {}),\n status: wire.status,\n steps: wire.steps,\n messages: wire.messages,\n pendingApprovals: wire.pendingApprovals,\n handoffs: wire.handoffs,\n usage: wire.usage,\n ...(wire.usageByModel !== undefined ? { usageByModel: wire.usageByModel } : {}),\n ...(wire.taintSummary !== undefined ? { taintSummary: wire.taintSummary } : {}),\n ...(wire.promotedTools !== undefined ? { promotedTools: wire.promotedTools } : {}),\n ...(wire.todos !== undefined ? { todos: wire.todos } : {}),\n ...(pendingSubRuns !== undefined ? { pendingSubRuns } : {}),\n startedAt: wire.startedAt,\n ...(wire.finishedAt !== undefined ? { finishedAt: wire.finishedAt } : {}),\n ...(wire.error !== undefined ? { error: wire.error } : {}),\n };\n // AG-23: the snapshot must be DETACHED from the live MutableRunState -\n // a post-suspend mutation must never reach an already-persisted\n // checkpoint. The JSON round-trip doubles as the plain-JSON guarantee\n // this function documents (no Map/Set/Date survive it).\n const detached = JSON.parse(JSON.stringify(out)) as SerializedRunState;\n if (options.stripTracingApiKey === true) {\n redactSecretKeysInPlace(detached);\n }\n return detached;\n}\n\n/**\n * Key names whose values are redacted by\n * `serializeRunState(state, { stripTracingApiKey: true })`.\n */\nconst SECRET_KEY_PATTERN =\n /^(api[-_]?key|tracing[-_]?api[-_]?key|x-api-key|authorization|bearer[-_]?token|access[-_]?token|refresh[-_]?token|secret|password|passphrase)$/i;\n\nconst REDACTED = '[redacted]';\n\nfunction redactSecretKeysInPlace(value: unknown): void {\n if (typeof value !== 'object' || value === null) return;\n if (Array.isArray(value)) {\n for (const item of value) redactSecretKeysInPlace(item);\n return;\n }\n const record = value as Record<string, unknown>;\n // Tool messages embed the tool output as a JSON STRING in `content` -\n // parse, redact, and re-stringify so a secret inside the string form\n // is caught too.\n if (record.role === 'tool' && typeof record.content === 'string') {\n record.content = redactJsonString(record.content);\n }\n for (const [key, inner] of Object.entries(record)) {\n if (SECRET_KEY_PATTERN.test(key)) {\n record[key] = REDACTED;\n continue;\n }\n redactSecretKeysInPlace(inner);\n }\n}\n\nfunction redactJsonString(content: string): string {\n try {\n const parsed = JSON.parse(content) as unknown;\n if (typeof parsed !== 'object' || parsed === null) return content;\n redactSecretKeysInPlace(parsed);\n return JSON.stringify(parsed);\n } catch {\n return content;\n }\n}\n\n/**\n * Render the canonical JSON string representation of the supplied\n * {@link RunState}. `JSON.stringify(serializeRunState(state))` -\n * provided as a convenience.\n *\n * @stable\n */\nexport function runStateToJSON(state: RunState, options?: SerializeRunStateOptions): string {\n return JSON.stringify(serializeRunState(state, options));\n}\n\ninterface DeserializeOptions {\n /**\n * Synthesize `usageByModel` from a v0.1-alpha state that omits\n * the field. Defaults to `true` so callers can rehydrate older\n * states without explicit migration.\n */\n readonly synthesizeUsageByModel?: boolean;\n /**\n * Logger callback for one-time INFO messages emitted on\n * backwards-compat synthesis. Defaults to a no-op.\n */\n readonly logger?: (message: string) => void;\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/**\n * Rehydrate a {@link RunState} from the on-disk payload. Throws\n * {@link RunStateVersionUnsupportedError} when the payload version\n * is from a future major; throws\n * {@link RunStateMalformedError} when the payload is structurally\n * invalid.\n *\n * Backwards-compat: a payload that omits `usageByModel` is accepted\n * and the field is synthesized from the aggregate `usage` with\n * `attemptCount: 1` for the primary model.\n *\n * @stable\n */\nexport function deserializeRunState(payload: unknown, options: DeserializeOptions = {}): RunState {\n if (!isRecord(payload)) {\n throw new RunStateMalformedError('payload must be an object');\n }\n const versionRaw = payload.version;\n if (typeof versionRaw !== 'string') {\n throw new RunStateMalformedError(\"missing 'version' field\");\n }\n const versionMatch = /^graphorin-run-state\\/(\\d+)\\.(\\d+)$/.exec(versionRaw);\n if (versionMatch === null) {\n throw new RunStateMalformedError(`unrecognized version '${versionRaw}'`);\n }\n const major = Number(versionMatch[1]);\n if (major > RUN_STATE_SCHEMA_MAJOR_SUPPORTED) {\n throw new RunStateVersionUnsupportedError(versionRaw, RUN_STATE_SCHEMA_VERSION);\n }\n const requiredString = (key: string): string => {\n const v = payload[key];\n if (typeof v !== 'string') {\n throw new RunStateMalformedError(`missing string '${key}' field`);\n }\n return v;\n };\n const requiredArray = <T>(key: string): T[] => {\n const v = payload[key];\n if (!Array.isArray(v)) {\n throw new RunStateMalformedError(`missing array '${key}' field`);\n }\n return v as T[];\n };\n const id = requiredString('id');\n const agentId = requiredString('agentId');\n const currentAgentId =\n typeof payload.currentAgentId === 'string' ? payload.currentAgentId : agentId;\n const sessionId = requiredString('sessionId');\n const status = requiredString('status') as RunStatus;\n const steps = requiredArray<WireRunStep>('steps');\n const messages = requiredArray<WireMessage>('messages');\n const pendingApprovals = requiredArray<ToolApproval>('pendingApprovals');\n const handoffs = requiredArray<HandoffRecord>('handoffs');\n const usageRaw = payload.usage;\n if (!isRecord(usageRaw)) {\n throw new RunStateMalformedError(\"missing object 'usage' field\");\n }\n const usage: Usage = {\n promptTokens: Number(usageRaw.promptTokens ?? 0),\n completionTokens: Number(usageRaw.completionTokens ?? 0),\n totalTokens: Number(usageRaw.totalTokens ?? 0),\n ...(usageRaw.reasoningTokens !== undefined\n ? { reasoningTokens: Number(usageRaw.reasoningTokens) }\n : {}),\n ...(usageRaw.cachedReadTokens !== undefined\n ? { cachedReadTokens: Number(usageRaw.cachedReadTokens) }\n : {}),\n ...(usageRaw.cacheWriteTokens !== undefined\n ? { cacheWriteTokens: Number(usageRaw.cacheWriteTokens) }\n : {}),\n ...(isRecord(usageRaw.cost)\n ? {\n cost: {\n amount: Number(usageRaw.cost.amount ?? 0),\n currency: String(usageRaw.cost.currency ?? 'USD'),\n },\n }\n : {}),\n };\n const startedAt = requiredString('startedAt');\n const userIdRaw = payload.userId;\n const finishedAtRaw = payload.finishedAt;\n const errorRaw = payload.error;\n let error: RunState['error'];\n if (isRecord(errorRaw)) {\n error = {\n message: typeof errorRaw.message === 'string' ? errorRaw.message : 'unknown',\n code: typeof errorRaw.code === 'string' ? errorRaw.code : 'unknown',\n ...(errorRaw.details !== undefined ? { details: errorRaw.details } : {}),\n };\n }\n\n // Backwards-compat synthesis for usageByModel.\n let usageByModel: RunStateUsageByModel | undefined;\n if (isRecord(payload.usageByModel)) {\n const m: Record<string, Usage & { attemptCount: number }> = {};\n for (const [modelId, entryRaw] of Object.entries(payload.usageByModel)) {\n if (!isRecord(entryRaw)) continue;\n m[modelId] = {\n promptTokens: Number(entryRaw.promptTokens ?? 0),\n completionTokens: Number(entryRaw.completionTokens ?? 0),\n totalTokens: Number(entryRaw.totalTokens ?? 0),\n attemptCount: Number(entryRaw.attemptCount ?? 1),\n ...(entryRaw.reasoningTokens !== undefined\n ? { reasoningTokens: Number(entryRaw.reasoningTokens) }\n : {}),\n ...(entryRaw.cachedReadTokens !== undefined\n ? { cachedReadTokens: Number(entryRaw.cachedReadTokens) }\n : {}),\n ...(entryRaw.cacheWriteTokens !== undefined\n ? { cacheWriteTokens: Number(entryRaw.cacheWriteTokens) }\n : {}),\n ...(isRecord(entryRaw.cost)\n ? {\n cost: {\n amount: Number(entryRaw.cost.amount ?? 0),\n currency: String(entryRaw.cost.currency ?? 'USD'),\n },\n }\n : {}),\n };\n }\n usageByModel = m;\n } else if (options.synthesizeUsageByModel !== false && Number.isFinite(usage.totalTokens)) {\n usageByModel = {\n [agentId]: { ...usage, attemptCount: 1 },\n };\n options.logger?.(\n `[graphorin/agent] RunState v0.1-alpha synthesis: per-model breakdown derived from aggregate usage for agent '${agentId}'.`,\n );\n }\n\n // AG-19: rehydrate the coarse taint summary + promoted-tool set (additive;\n // absent on older v1.0 payloads).\n let taintSummary: RunTaintSummary | undefined;\n if (isRecord(payload.taintSummary)) {\n const ts = payload.taintSummary;\n const tileHashes = Array.isArray(ts.spanTileHashes)\n ? ts.spanTileHashes.filter((h): h is string => typeof h === 'string')\n : undefined;\n taintSummary = {\n untrustedSeen: ts.untrustedSeen === true,\n sensitiveSeen: ts.sensitiveSeen === true,\n untrustedSourceKinds: Array.isArray(ts.untrustedSourceKinds)\n ? ts.untrustedSourceKinds.filter((k): k is string => typeof k === 'string')\n : [],\n ...(tileHashes !== undefined && tileHashes.length > 0 ? { spanTileHashes: tileHashes } : {}),\n };\n }\n const promotedTools = Array.isArray(payload.promotedTools)\n ? payload.promotedTools.filter((t): t is string => typeof t === 'string')\n : undefined;\n const todos = Array.isArray(payload.todos)\n ? payload.todos\n .filter(isRecord)\n .filter(\n (t): t is { id: string; content: string; status: TodoItem['status'] } =>\n typeof t.id === 'string' &&\n typeof t.content === 'string' &&\n (t.status === 'pending' || t.status === 'in_progress' || t.status === 'completed'),\n )\n .map((t) => ({ id: t.id, content: t.content, status: t.status }))\n : undefined;\n\n const wire: WireRunState = {\n id,\n agentId,\n currentAgentId,\n sessionId,\n ...(typeof userIdRaw === 'string' ? { userId: userIdRaw } : {}),\n status,\n steps,\n messages,\n pendingApprovals,\n handoffs,\n usage,\n ...(usageByModel !== undefined ? { usageByModel } : {}),\n ...(taintSummary !== undefined ? { taintSummary } : {}),\n ...(promotedTools !== undefined ? { promotedTools } : {}),\n ...(todos !== undefined ? { todos } : {}),\n startedAt,\n ...(typeof finishedAtRaw === 'string' ? { finishedAt: finishedAtRaw } : {}),\n ...(error !== undefined ? { error } : {}),\n };\n void zeroUsage; // kept available; some callers prefer zeroUsage as a default.\n // W-004: decode base64/href envelopes back into Uint8Array/URL. Legacy\n // 1.0/1.1 payloads whose binary fields were stringify-corrupted into\n // numeric-key objects are repaired best-effort inside the codec.\n const rehydrated = fromJsonSafeRunState(wire);\n // W-001: rehydrate each parked child through THIS function recursively\n // (the child payload is its own versioned SerializedRunState).\n if (Array.isArray(payload.pendingSubRuns) && payload.pendingSubRuns.length > 0) {\n const subs = payload.pendingSubRuns\n .filter(isRecord)\n .filter(\n (s): s is Record<string, unknown> & { toolCallId: string; toolName: string } =>\n typeof s.toolCallId === 'string' && typeof s.toolName === 'string',\n )\n .map((s) => ({\n toolCallId: s.toolCallId,\n toolName: s.toolName,\n targetAgentName: typeof s.targetAgentName === 'string' ? s.targetAgentName : s.toolName,\n state: deserializeRunState(s.state, options),\n }));\n if (subs.length > 0) {\n (rehydrated as { pendingSubRuns?: RunState['pendingSubRuns'] }).pendingSubRuns = subs;\n }\n }\n return rehydrated;\n}\n\n/** Convenience JSON-string parser pairing with {@link runStateToJSON}. */\nexport function runStateFromJSON(serialized: string, options?: DeserializeOptions): RunState {\n let parsed: unknown;\n try {\n parsed = JSON.parse(serialized);\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n throw new RunStateMalformedError(`invalid JSON: ${message}`);\n }\n return deserializeRunState(parsed, options);\n}\n\n/**\n * Build a fresh, minimal {@link RunState} for a new run. Helper used\n * by `createAgent({...})` so consumers can construct deterministic\n * run state in tests.\n *\n * @stable\n */\nexport function createInitialRunState(args: {\n readonly id: string;\n readonly agentId: string;\n readonly sessionId: string;\n readonly userId?: string;\n readonly startedAt?: string;\n}): RunState {\n return {\n id: args.id,\n agentId: args.agentId,\n currentAgentId: args.agentId,\n sessionId: args.sessionId,\n ...(args.userId !== undefined ? { userId: args.userId } : {}),\n status: 'running',\n steps: [],\n messages: [],\n pendingApprovals: [],\n handoffs: [],\n usage: zeroUsage(),\n startedAt: args.startedAt ?? new Date().toISOString(),\n };\n}\n\n/**\n * Append a per-model usage entry to {@link RunState.usageByModel}.\n * Mutates the supplied state in place - used by the agent runtime's\n * per-step retry loop. Pure callers that need an immutable update\n * should clone the state first.\n *\n * @stable\n */\nexport function addModelUsage(state: RunState, modelId: string, delta: Usage): void {\n const current = (state.usageByModel ?? {}) as Record<string, Usage & { attemptCount: number }>;\n const prev = current[modelId];\n const merged: Usage & { attemptCount: number } = prev\n ? {\n promptTokens: prev.promptTokens + delta.promptTokens,\n completionTokens: prev.completionTokens + delta.completionTokens,\n totalTokens: prev.totalTokens + delta.totalTokens,\n ...(delta.reasoningTokens !== undefined || prev.reasoningTokens !== undefined\n ? {\n reasoningTokens: (prev.reasoningTokens ?? 0) + (delta.reasoningTokens ?? 0),\n }\n : {}),\n ...(delta.cachedReadTokens !== undefined || prev.cachedReadTokens !== undefined\n ? {\n cachedReadTokens: (prev.cachedReadTokens ?? 0) + (delta.cachedReadTokens ?? 0),\n }\n : {}),\n ...(delta.cacheWriteTokens !== undefined || prev.cacheWriteTokens !== undefined\n ? {\n cacheWriteTokens: (prev.cacheWriteTokens ?? 0) + (delta.cacheWriteTokens ?? 0),\n }\n : {}),\n attemptCount: prev.attemptCount + 1,\n ...(prev.cost !== undefined ? { cost: prev.cost } : {}),\n }\n : { ...delta, attemptCount: 1 };\n current[modelId] = merged;\n // Mutate via the writable typing - `usageByModel` is declared\n // optional; the runtime owns the field's lifecycle.\n (state as { usageByModel?: RunStateUsageByModel }).usageByModel = current;\n}\n\n/**\n * Recompute the aggregate usage from `usageByModel`. Returns the\n * sum that callers can compare against `state.usage` to verify the\n * per-step retry loop maintained the documented invariant.\n *\n * @stable\n */\nexport function aggregateUsageFromByModel(byModel: RunStateUsageByModel | undefined): Usage {\n if (byModel === undefined) return zeroUsage();\n let pt = 0;\n let ct = 0;\n let tt = 0;\n let rt = 0;\n let cr = 0;\n let cw = 0;\n let hasReasoning = false;\n let hasCachedRead = false;\n let hasCacheWrite = false;\n for (const entry of Object.values(byModel)) {\n pt += entry.promptTokens;\n ct += entry.completionTokens;\n tt += entry.totalTokens;\n if (entry.reasoningTokens !== undefined) {\n rt += entry.reasoningTokens;\n hasReasoning = true;\n }\n if (entry.cachedReadTokens !== undefined) {\n cr += entry.cachedReadTokens;\n hasCachedRead = true;\n }\n if (entry.cacheWriteTokens !== undefined) {\n cw += entry.cacheWriteTokens;\n hasCacheWrite = true;\n }\n }\n return {\n promptTokens: pt,\n completionTokens: ct,\n totalTokens: tt,\n ...(hasReasoning ? { reasoningTokens: rt } : {}),\n ...(hasCachedRead ? { cachedReadTokens: cr } : {}),\n ...(hasCacheWrite ? { cacheWriteTokens: cw } : {}),\n };\n}\n\n/**\n * The \"tools used\" surface of a completed run. Cheap to compute\n * from `RunState.steps`; surfaced as a stand-alone helper for\n * Phase 17 example apps and operator-facing dashboards.\n *\n * @stable\n */\nexport function completedToolCallsFromState(state: RunState): ReadonlyArray<CompletedToolCall> {\n const out: CompletedToolCall[] = [];\n for (const step of state.steps) {\n for (const tc of step.toolCalls) out.push(tc);\n }\n return out;\n}\n"],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ import { newId } from "../internal/ids.js";
2
+ import { runFanOut } from "../fanout/index.js";
3
+ import { noopCompactionResult } from "./run-compaction.js";
4
+
5
+ //#region src/runtime/agent-surface.ts
6
+ /** Build the `stream` / `run` methods over the factory's run loop. */
7
+ function createRunMethods(runLoop) {
8
+ const stream = (input, options) => {
9
+ const opts = options ?? {};
10
+ return { [Symbol.asyncIterator]: () => {
11
+ const gen = runLoop(input, opts);
12
+ return {
13
+ async next() {
14
+ const r = await gen.next();
15
+ if (r.done === true) return {
16
+ done: true,
17
+ value: void 0
18
+ };
19
+ return {
20
+ done: false,
21
+ value: r.value
22
+ };
23
+ },
24
+ async return() {
25
+ await gen.return(void 0);
26
+ return {
27
+ done: true,
28
+ value: void 0
29
+ };
30
+ }
31
+ };
32
+ } };
33
+ };
34
+ const run = async (input, options) => {
35
+ const gen = runLoop(input, options ?? {});
36
+ let next = await gen.next();
37
+ while (next.done !== true) next = await gen.next();
38
+ const result = next.value;
39
+ if (result === void 0) throw new Error("unreachable: agent run loop ended without a result");
40
+ return result;
41
+ };
42
+ return {
43
+ stream,
44
+ run
45
+ };
46
+ }
47
+ /** Build the `compact` method (queued into the run loop, CE-3 / AG-13). */
48
+ function createCompactMethod(deps) {
49
+ const { config, memory, getActiveRunState, pendingManualCompacts } = deps;
50
+ return async (options) => {
51
+ if (memory === void 0) return noopCompactionResult("no-memory");
52
+ if (config.sensitivity === "secret") return noopCompactionResult("sensitivity-gated");
53
+ if (getActiveRunState() === void 0) return noopCompactionResult("no-active-run");
54
+ return await new Promise((resolve, reject) => {
55
+ pendingManualCompacts.push({
56
+ options,
57
+ resolve,
58
+ reject
59
+ });
60
+ });
61
+ };
62
+ }
63
+ /** Build the `fanOut` method (AG-7: lifecycle events reach the stream). */
64
+ function createFanOutMethod(deps) {
65
+ const { config, agentId, getActiveRunState, externalEventQueue } = deps;
66
+ return async (options) => {
67
+ const activeRunState = getActiveRunState();
68
+ const runId = activeRunState?.id ?? `run_${newId()}`;
69
+ const sessionId = activeRunState?.sessionId ?? `session_${newId()}`;
70
+ return runFanOut({
71
+ children: options.children,
72
+ ...options.maxConcurrentChildren !== void 0 ? { maxConcurrentChildren: options.maxConcurrentChildren } : {},
73
+ ...options.perBudget !== void 0 ? { perBudget: options.perBudget } : {},
74
+ ...options.mergeStrategy !== void 0 ? { mergeStrategy: options.mergeStrategy } : {},
75
+ ...options.signal !== void 0 ? { signal: options.signal } : {},
76
+ emit: (event) => {
77
+ externalEventQueue.push(event);
78
+ },
79
+ ...config.mergeGuard !== void 0 ? { mergeGuard: config.mergeGuard } : {},
80
+ runId,
81
+ sessionId,
82
+ agentId
83
+ });
84
+ };
85
+ }
86
+ /** Build the structured progress-artifact IO surface (AG-20). */
87
+ function createProgressSurface(deps) {
88
+ const { agentId, getActiveRunState, externalEventQueue, progressIO } = deps;
89
+ const progressFallbackRunId = `run_${newId()}`;
90
+ return {
91
+ write: async (content, opts) => {
92
+ const runId = getActiveRunState()?.id ?? progressFallbackRunId;
93
+ const ref = await progressIO.write(runId, content, opts);
94
+ externalEventQueue.push({
95
+ type: "agent.progress.written",
96
+ runId,
97
+ sessionId: getActiveRunState()?.sessionId ?? "",
98
+ agentId,
99
+ ref
100
+ });
101
+ return ref;
102
+ },
103
+ read: async (opts) => {
104
+ const queriedRunId = opts?.runId ?? getActiveRunState()?.id ?? progressFallbackRunId;
105
+ const refs = await progressIO.read(queriedRunId, opts);
106
+ externalEventQueue.push({
107
+ type: "agent.progress.read",
108
+ runId: getActiveRunState()?.id ?? queriedRunId,
109
+ sessionId: getActiveRunState()?.sessionId ?? "",
110
+ agentId,
111
+ refs,
112
+ queriedRunId,
113
+ queriedRole: opts?.role
114
+ });
115
+ return refs;
116
+ }
117
+ };
118
+ }
119
+
120
+ //#endregion
121
+ export { createCompactMethod, createFanOutMethod, createProgressSurface, createRunMethods };
122
+ //# sourceMappingURL=agent-surface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-surface.js","names":[],"sources":["../../src/runtime/agent-surface.ts"],"sourcesContent":["/**\n * The agent's public call surface, built over the factory's run loop:\n * `stream` / `run` (the AsyncIterable wrapper and the drained-run\n * variant, AG-9), `compact` (queued into the loop, CE-3 / AG-13),\n * `fanOut` (AG-7 lifecycle events onto the external queue), and the\n * structured progress-artifact IO (AG-20). Extracted verbatim from\n * `factory.ts` (issue #23); the factory's mutable `activeRunState`\n * scratch is read through a live getter so event payloads observe\n * exactly what the former closures observed.\n *\n * @packageDocumentation\n */\n\nimport type { AgentEvent, AgentResult, RunState } from '@graphorin/core';\nimport type { Memory } from '@graphorin/memory';\nimport {\n type FanOutResult,\n type FanOutOptions as RunFanOutOptions,\n runFanOut,\n} from '../fanout/index.js';\nimport { newId } from '../internal/ids.js';\nimport type { ProgressIO, ProgressReadOptions, ProgressWriteOptions } from '../progress/index.js';\nimport type {\n AgentCallOptions,\n AgentConfig,\n AgentFanOutOptions,\n AgentInput,\n AgentProgressIO,\n CompactionApiResult,\n CompactOptions,\n} from '../types.js';\nimport { noopCompactionResult, type PendingManualCompact } from './run-compaction.js';\n\n/** The factory's per-instance run entry point (concurrency-guarded). */\nexport type RunLoopFn<TDeps, TOutput> = (\n input: AgentInput | RunState,\n options: AgentCallOptions<TDeps>,\n) => AsyncGenerator<AgentEvent<TOutput>, AgentResult<TOutput>, void>;\n\n/** Build the `stream` / `run` methods over the factory's run loop. */\nexport function createRunMethods<TDeps, TOutput>(\n runLoop: RunLoopFn<TDeps, TOutput>,\n): {\n readonly stream: (\n input: AgentInput | RunState,\n options?: AgentCallOptions<TDeps>,\n ) => AsyncIterable<AgentEvent<TOutput>>;\n readonly run: (\n input: AgentInput | RunState,\n options?: AgentCallOptions<TDeps>,\n ) => Promise<AgentResult<TOutput>>;\n} {\n const stream = (\n input: AgentInput | RunState,\n options?: AgentCallOptions<TDeps>,\n ): AsyncIterable<AgentEvent<TOutput>> => {\n const opts = options ?? {};\n return {\n [Symbol.asyncIterator]: () => {\n const gen = runLoop(input, opts);\n return {\n async next(): Promise<IteratorResult<AgentEvent<TOutput>, void>> {\n const r = await gen.next();\n if (r.done === true) {\n return { done: true, value: undefined };\n }\n return { done: false, value: r.value };\n },\n async return(): Promise<IteratorResult<AgentEvent<TOutput>, void>> {\n await gen.return(undefined as unknown as AgentResult<TOutput>);\n return { done: true, value: undefined };\n },\n };\n },\n };\n };\n\n const run = async (\n input: AgentInput | RunState,\n options?: AgentCallOptions<TDeps>,\n ): Promise<AgentResult<TOutput>> => {\n const opts = options ?? {};\n const gen = runLoop(input, opts);\n let next = await gen.next();\n while (next.done !== true) {\n next = await gen.next();\n }\n // Every terminal path of the run loop returns `finalize(...)`; an\n // undefined return value would mean the generator was torn down\n // externally - an invariant violation, not a run outcome (AG-9).\n const result = next.value;\n if (result === undefined) {\n throw new Error('unreachable: agent run loop ended without a result');\n }\n return result;\n };\n\n return { stream, run };\n}\n\n/** What the out-of-loop surface methods need from the factory scope. */\nexport interface AgentSurfaceDeps<TDeps, TOutput> {\n readonly config: Pick<AgentConfig<TDeps, TOutput>, 'sensitivity' | 'mergeGuard'>;\n readonly memory: Memory | undefined;\n readonly agentId: string;\n readonly getActiveRunState: () => RunState | undefined;\n readonly externalEventQueue: AgentEvent<TOutput>[];\n readonly pendingManualCompacts: PendingManualCompact[];\n readonly progressIO: ProgressIO;\n}\n\n/** Build the `compact` method (queued into the run loop, CE-3 / AG-13). */\nexport function createCompactMethod<TDeps, TOutput>(\n deps: AgentSurfaceDeps<TDeps, TOutput>,\n): (options?: CompactOptions) => Promise<CompactionApiResult> {\n const { config, memory, getActiveRunState, pendingManualCompacts } = deps;\n return async (options?: CompactOptions): Promise<CompactionApiResult> => {\n // No memory wired - an explicit no-op (AG-13), intentionally\n // forgiving so example apps that don't wire memory don't crash\n // on `agent.compact()`.\n if (memory === undefined) return noopCompactionResult('no-memory');\n // Sensitivity gate (WI-09 step 2) applies to MANUAL compaction\n // too: secret-tier history never ships to the summarizer.\n if (config.sensitivity === 'secret') return noopCompactionResult('sensitivity-gated');\n // Idle - there is no live buffer to splice (AG-13's explicit\n // no-op marker, where the old surface silently reported zeros).\n if (getActiveRunState() === undefined) return noopCompactionResult('no-active-run');\n // CE-3/AG-13: the run loop owns the live buffer, so the splice\n // happens there - enqueue and let `maybeAutoCompact` service the\n // request at the next step boundary with the same prefix-pinned\n // splice as auto-compaction. Don't await this from inside a tool\n // handler: the loop can't reach the next step until the tool\n // returns.\n return await new Promise<CompactionApiResult>((resolve, reject) => {\n pendingManualCompacts.push({ options, resolve, reject });\n });\n };\n}\n\n/** Build the `fanOut` method (AG-7: lifecycle events reach the stream). */\nexport function createFanOutMethod<TDeps, TOutput>(\n deps: AgentSurfaceDeps<TDeps, TOutput>,\n): <TFanOutOutput = unknown>(\n options: AgentFanOutOptions<TFanOutOutput>,\n) => Promise<FanOutResult<TFanOutOutput>> {\n const { config, agentId, getActiveRunState, externalEventQueue } = deps;\n return async <TFanOutOutput = unknown>(\n options: AgentFanOutOptions<TFanOutOutput>,\n ): Promise<FanOutResult<TFanOutOutput>> => {\n const activeRunState = getActiveRunState();\n const runId = activeRunState?.id ?? `run_${newId()}`;\n const sessionId = activeRunState?.sessionId ?? `session_${newId()}`;\n const fanOutOptions: RunFanOutOptions<TFanOutOutput> = {\n children: options.children,\n ...(options.maxConcurrentChildren !== undefined\n ? { maxConcurrentChildren: options.maxConcurrentChildren }\n : {}),\n ...(options.perBudget !== undefined ? { perBudget: options.perBudget } : {}),\n ...(options.mergeStrategy !== undefined ? { mergeStrategy: options.mergeStrategy } : {}),\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n // AG-7: fanout lifecycle events reach the agent stream - queued\n // on the external-event queue and drained into the active (or\n // next consumed) run, like steer/follow-up/progress events.\n emit: (event) => {\n externalEventQueue.push(event as AgentEvent<TOutput>);\n },\n // AG-7: the configured sideways-injection merge guard finally\n // applies to the judge-merge path.\n ...(config.mergeGuard !== undefined ? { mergeGuard: config.mergeGuard } : {}),\n runId,\n sessionId,\n agentId,\n };\n return runFanOut<TFanOutOutput>(fanOutOptions);\n };\n}\n\n/** Build the structured progress-artifact IO surface (AG-20). */\nexport function createProgressSurface<TDeps, TOutput>(\n deps: AgentSurfaceDeps<TDeps, TOutput>,\n): AgentProgressIO {\n const { agentId, getActiveRunState, externalEventQueue, progressIO } = deps;\n // Stable fallback id so out-of-run `progress.write` → `progress.read`\n // pairs resolve to the same artifact directory (a fresh id per call\n // could never find what it just wrote).\n const progressFallbackRunId = `run_${newId()}`;\n const progress: AgentProgressIO = {\n write: async (content: string, opts?: ProgressWriteOptions) => {\n const runId = getActiveRunState()?.id ?? progressFallbackRunId;\n const ref = await progressIO.write(runId, content, opts);\n // AG-20: surface the documented `agent.progress.written` event -\n // queued here and drained into the active (or next consumed) stream.\n externalEventQueue.push({\n type: 'agent.progress.written',\n runId,\n sessionId: getActiveRunState()?.sessionId ?? '',\n agentId,\n ref,\n } as AgentEvent<TOutput>);\n return ref;\n },\n read: async (opts?: ProgressReadOptions) => {\n const queriedRunId = opts?.runId ?? getActiveRunState()?.id ?? progressFallbackRunId;\n const refs = await progressIO.read(queriedRunId, opts);\n externalEventQueue.push({\n type: 'agent.progress.read',\n runId: getActiveRunState()?.id ?? queriedRunId,\n sessionId: getActiveRunState()?.sessionId ?? '',\n agentId,\n refs,\n queriedRunId,\n queriedRole: opts?.role,\n } as AgentEvent<TOutput>);\n return refs;\n },\n };\n return progress;\n}\n"],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import { AgentInput } from "../types.js";
2
+ import { AgentEvent, AgentResult, Message, RunState, Tool } from "@graphorin/core";
3
+
4
+ //#region src/runtime/agent-to-tool.d.ts
5
+
6
+ /**
7
+ * W-001: well-known marker on every `toTool()` tool object. The
8
+ * graphorin tool-call walk detects it and executes the sub-agent
9
+ * INLINE (through the same seam as a handoff) so a child suspending on
10
+ * `awaiting_approval` parks on the parent instead of throwing from the
11
+ * executor; the resume router resolves parked toTool sub-runs through
12
+ * the same refs. `Symbol.for` so duplicate package copies agree.
13
+ * Foreign harnesses mounting the tool outside the graphorin loop keep
14
+ * the plain `execute()` behavior (a suspended child throws there).
15
+ */
16
+ declare const SUBAGENT_TOOL: unique symbol;
17
+ /** Child taint flags carried across the toTool fold (D2). */
18
+ interface SubAgentFoldTaint {
19
+ readonly untrusted?: boolean;
20
+ readonly sensitive?: boolean;
21
+ readonly sourceKind?: string;
22
+ }
23
+ /**
24
+ * The live references {@link SUBAGENT_TOOL} carries (W-001). Typed
25
+ * loosely on purpose: the walk is generic over foreign TDeps/TOutput
26
+ * and only ever passes through values it received from the same
27
+ * factory instance.
28
+ */
29
+ interface SubAgentToolRefs {
30
+ readonly agentName: string;
31
+ readonly run: (input: unknown, options?: Record<string, unknown>) => Promise<AgentResult<unknown>>;
32
+ readonly stream: (input: unknown, options?: Record<string, unknown>) => AsyncIterable<AgentEvent<unknown>>;
33
+ /** D2 capability restriction from `AgentToToolOptions.capability`. */
34
+ readonly capability?: 'read-only';
35
+ /** W-036: forwarding policy from `AgentToToolOptions.forwardEvents`. */
36
+ readonly forwardEvents?: 'none' | 'lifecycle' | 'all';
37
+ /** Reproduce `execute()`'s seed semantics (inputFilter + input string). */
38
+ readonly buildSeed: (input: {
39
+ readonly input: string;
40
+ }, parentMessages: ReadonlyArray<Message>) => AgentInput;
41
+ /** Reproduce `execute()`'s output shaping (exposeTurns/contextFold/taint). */
42
+ readonly shapeCompleted: (result: AgentResult<unknown>, turns: ReadonlyArray<string>) => {
43
+ readonly output: unknown;
44
+ readonly taint?: SubAgentFoldTaint;
45
+ };
46
+ }
47
+ /** Read the {@link SUBAGENT_TOOL} refs off a (possibly wrapped) tool. */
48
+ declare function getSubAgentToolRefs(tool: unknown): SubAgentToolRefs | undefined;
49
+ //#endregion
50
+ export { SUBAGENT_TOOL, SubAgentFoldTaint, SubAgentToolRefs, getSubAgentToolRefs };
51
+ //# sourceMappingURL=agent-to-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-to-tool.d.ts","names":[],"sources":["../../src/runtime/agent-to-tool.ts"],"sourcesContent":[],"mappings":";;;;;;;AAwHA;;;;;;;;cA1Ca;;UAGI,iBAAA;;;;;;;;;;;UAYA,gBAAA;;2CAIH,4BACP,QAAQ;8CAGD,4BACP,cAAc;;;;;;;;qBAQD,cAAc,aAC3B;;oCAGK,6BACD;;qBACyC;;;;iBAIpC,mBAAA,iBAAoC"}
@@ -0,0 +1,145 @@
1
+ import { foldChildRunUsage } from "./messages.js";
2
+ import { toolReturn } from "@graphorin/core";
3
+
4
+ //#region src/runtime/agent-to-tool.ts
5
+ /**
6
+ * W-033: fold the child's usage into the parent run through the tool
7
+ * context. `RunContext.state` is typed as a readonly projection for
8
+ * user tools (W-047); this runtime-internal seam is the sanctioned
9
+ * mutation point - the runtime owns the state lifecycle.
10
+ */
11
+ function foldIntoParent(ctx, childState, childName) {
12
+ if (ctx === void 0) return;
13
+ const parentState = ctx.runContext.state;
14
+ const usageAcc = ctx.runContext.usage;
15
+ if (parentState !== void 0 && typeof parentState.usage?.totalTokens === "number") {
16
+ foldChildRunUsage(parentState, usageAcc, childState, childName);
17
+ return;
18
+ }
19
+ const byModel = childState.usageByModel;
20
+ if (byModel !== void 0 && Object.keys(byModel).length > 0) {
21
+ for (const [modelId, usage] of Object.entries(byModel)) usageAcc?.add(modelId, usage);
22
+ return;
23
+ }
24
+ const aggregate = childState.usage;
25
+ if (aggregate.promptTokens > 0 || aggregate.completionTokens > 0 || aggregate.totalTokens > 0) usageAcc?.add(`sub-agent:${childName}`, aggregate);
26
+ }
27
+ /**
28
+ * W-001: well-known marker on every `toTool()` tool object. The
29
+ * graphorin tool-call walk detects it and executes the sub-agent
30
+ * INLINE (through the same seam as a handoff) so a child suspending on
31
+ * `awaiting_approval` parks on the parent instead of throwing from the
32
+ * executor; the resume router resolves parked toTool sub-runs through
33
+ * the same refs. `Symbol.for` so duplicate package copies agree.
34
+ * Foreign harnesses mounting the tool outside the graphorin loop keep
35
+ * the plain `execute()` behavior (a suspended child throws there).
36
+ */
37
+ const SUBAGENT_TOOL = Symbol.for("graphorin.SubAgentTool");
38
+ /** Read the {@link SUBAGENT_TOOL} refs off a (possibly wrapped) tool. */
39
+ function getSubAgentToolRefs(tool) {
40
+ if (typeof tool !== "object" || tool === null) return void 0;
41
+ const refs = tool[SUBAGENT_TOOL];
42
+ return refs === void 0 ? void 0 : refs;
43
+ }
44
+ /** Build the agent's `toTool` method over the factory's run surface. */
45
+ function createToTool(deps) {
46
+ const { config, run, stream } = deps;
47
+ const foldRunOutcome = (result, maxChars) => {
48
+ const steps = result.state.steps;
49
+ const toolNames = [...new Set(steps.flatMap((step) => step.toolCalls.map((c) => c.call.toolName)))];
50
+ const header = `[sub-agent '${config.name}' outcome] status=${result.status}; steps=${steps.length}; toolCalls=${steps.reduce((n, st) => n + st.toolCalls.length, 0)}` + (toolNames.length > 0 ? `; tools=${toolNames.join(", ")}` : "");
51
+ const text = typeof result.output === "string" ? result.output : JSON.stringify(result.output);
52
+ return `${header}\n${text.length > maxChars ? `${text.slice(0, maxChars)}\n[... ${text.length - maxChars} chars truncated by contextFold]` : text}`;
53
+ };
54
+ const taintFromChildState = (state) => {
55
+ const summary = state.taintSummary;
56
+ if (summary === void 0 || !summary.untrustedSeen && !summary.sensitiveSeen) return;
57
+ return {
58
+ ...summary.untrustedSeen ? { untrusted: true } : {},
59
+ ...summary.sensitiveSeen ? { sensitive: true } : {},
60
+ sourceKind: "sub-agent"
61
+ };
62
+ };
63
+ const toTool = (options = {}) => {
64
+ const exposeTurns = options.exposeTurns ?? "final";
65
+ const foldMaxChars = options.contextFold === void 0 || options.contextFold === false ? null : typeof options.contextFold === "object" ? options.contextFold.maxChars ?? 2e3 : 2e3;
66
+ const propagateTaint = options.propagateTaint !== false;
67
+ const toolName = options.name ?? `subagent_${config.name}`;
68
+ const description = options.description ?? `Invoke sub-agent '${config.name}'.`;
69
+ const buildSeed = (input, parentMessages) => options.inputFilter !== void 0 && parentMessages !== void 0 ? [...options.inputFilter(parentMessages), {
70
+ role: "user",
71
+ content: input.input
72
+ }] : input.input;
73
+ const shapeCompleted = (result, turns) => {
74
+ const taint = propagateTaint ? taintFromChildState(result.state) : void 0;
75
+ return {
76
+ output: exposeTurns === "all" ? foldMaxChars !== null ? foldRunOutcome(result, foldMaxChars) : turns.join("\n\n") : exposeTurns === "none" ? "" : foldMaxChars !== null ? foldRunOutcome(result, foldMaxChars) : result.output,
77
+ ...taint !== void 0 ? { taint } : {}
78
+ };
79
+ };
80
+ const tool = {
81
+ name: toolName,
82
+ description,
83
+ inputSchema: {
84
+ parse: (v) => v,
85
+ safeParse: (v) => ({
86
+ success: true,
87
+ data: v
88
+ }),
89
+ toJSON: () => ({
90
+ type: "object",
91
+ properties: { input: { type: "string" } },
92
+ required: ["input"]
93
+ })
94
+ },
95
+ sideEffectClass: "side-effecting",
96
+ async execute(input, ctx) {
97
+ const callOpts = {
98
+ ...ctx?.signal !== void 0 ? { signal: ctx.signal } : {},
99
+ ...ctx?.runContext.deps !== void 0 ? { deps: ctx.runContext.deps } : {},
100
+ ...ctx?.runContext.sessionId !== void 0 ? { sessionId: ctx.runContext.sessionId } : {},
101
+ ...options.capability !== void 0 ? { capability: options.capability } : {},
102
+ ...ctx?.runContext.span !== void 0 ? { parentSpan: ctx.runContext.span } : {}
103
+ };
104
+ const seed = buildSeed(input, ctx?.runContext.messages);
105
+ if (exposeTurns === "all") {
106
+ const turns = [];
107
+ let endResult;
108
+ for await (const ev of stream(seed, callOpts)) if (ev.type === "text.complete") turns.push(ev.text);
109
+ else if (ev.type === "agent.end") endResult = ev.result;
110
+ if (endResult !== void 0) foldIntoParent(ctx, endResult.state, config.name);
111
+ if (endResult !== void 0 && endResult.status !== "completed") throw new Error(`sub-agent '${config.name}' ${endResult.status}${endResult.error !== void 0 ? `: ${endResult.error.message}` : ""}`);
112
+ if (endResult === void 0) return turns.join("\n\n");
113
+ const shapedAll = shapeCompleted(endResult, turns);
114
+ return shapedAll.taint !== void 0 ? toolReturn({
115
+ output: shapedAll.output,
116
+ taint: shapedAll.taint
117
+ }) : shapedAll.output;
118
+ }
119
+ const result = await run(seed, callOpts);
120
+ foldIntoParent(ctx, result.state, config.name);
121
+ if (result.status !== "completed") throw new Error(`sub-agent '${config.name}' ${result.status}${result.error !== void 0 ? `: ${result.error.message}` : ""}`);
122
+ const shaped = shapeCompleted(result, []);
123
+ return shaped.taint !== void 0 ? toolReturn({
124
+ output: shaped.output,
125
+ taint: shaped.taint
126
+ }) : shaped.output;
127
+ }
128
+ };
129
+ tool[SUBAGENT_TOOL] = {
130
+ agentName: config.name,
131
+ run,
132
+ stream,
133
+ ...options.capability !== void 0 ? { capability: options.capability } : {},
134
+ ...options.forwardEvents !== void 0 ? { forwardEvents: options.forwardEvents } : {},
135
+ buildSeed,
136
+ shapeCompleted
137
+ };
138
+ return tool;
139
+ };
140
+ return toTool;
141
+ }
142
+
143
+ //#endregion
144
+ export { SUBAGENT_TOOL, createToTool, getSubAgentToolRefs };
145
+ //# sourceMappingURL=agent-to-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-to-tool.js","names":["SUBAGENT_TOOL: unique symbol","tool: Tool<{ readonly input: string }, TOutput, TDeps>","callOpts: AgentCallOptions<TDeps>","turns: string[]","endResult: AgentResult<TOutput> | undefined"],"sources":["../../src/runtime/agent-to-tool.ts"],"sourcesContent":["/**\n * `agent.toTool()` - expose an agent as a callable sub-agent tool\n * (AG-17 / D2): least-authority seeding from the parent history, the\n * contextFold distillation of a completed child run, and the coarse\n * child-taint propagation that re-arms the parent's data-flow ledger.\n * Extracted verbatim from `factory.ts` (issue #23); the former factory\n * closures now hang off {@link createToTool}'s deps.\n *\n * @packageDocumentation\n */\n\nimport type {\n AgentEvent,\n AgentResult,\n Message,\n RunState,\n Tool,\n ToolExecutionContext,\n UsageAccumulator,\n} from '@graphorin/core';\nimport { toolReturn } from '@graphorin/core';\nimport type { AgentCallOptions, AgentConfig, AgentInput, AgentToToolOptions } from '../types.js';\nimport { foldChildRunUsage } from './messages.js';\n\n/**\n * W-033: fold the child's usage into the parent run through the tool\n * context. `RunContext.state` is typed as a readonly projection for\n * user tools (W-047); this runtime-internal seam is the sanctioned\n * mutation point - the runtime owns the state lifecycle.\n */\nfunction foldIntoParent(\n ctx: ToolExecutionContext<unknown> | undefined,\n childState: RunState,\n childName: string,\n): void {\n if (ctx === undefined) return;\n const parentState = ctx.runContext.state as unknown as RunState | undefined;\n const usageAcc = ctx.runContext.usage as UsageAccumulator | undefined;\n if (parentState !== undefined && typeof parentState.usage?.totalTokens === 'number') {\n foldChildRunUsage(parentState, usageAcc, childState, childName);\n return;\n }\n // A foreign harness mounting this tool outside the graphorin loop may\n // hand a structurally-minimal context - fold into the accumulator only.\n const byModel = childState.usageByModel;\n if (byModel !== undefined && Object.keys(byModel).length > 0) {\n for (const [modelId, usage] of Object.entries(byModel)) usageAcc?.add(modelId, usage);\n return;\n }\n const aggregate = childState.usage;\n if (aggregate.promptTokens > 0 || aggregate.completionTokens > 0 || aggregate.totalTokens > 0) {\n usageAcc?.add(`sub-agent:${childName}`, aggregate);\n }\n}\n\n/** What {@link createToTool} needs from the agent factory scope. */\nexport interface ToToolDeps<TDeps, TOutput> {\n readonly config: AgentConfig<TDeps, TOutput>;\n readonly run: (\n input: AgentInput | RunState,\n options?: AgentCallOptions<TDeps>,\n ) => Promise<AgentResult<TOutput>>;\n readonly stream: (\n input: AgentInput | RunState,\n options?: AgentCallOptions<TDeps>,\n ) => AsyncIterable<AgentEvent<TOutput>>;\n}\n\n/**\n * W-001: well-known marker on every `toTool()` tool object. The\n * graphorin tool-call walk detects it and executes the sub-agent\n * INLINE (through the same seam as a handoff) so a child suspending on\n * `awaiting_approval` parks on the parent instead of throwing from the\n * executor; the resume router resolves parked toTool sub-runs through\n * the same refs. `Symbol.for` so duplicate package copies agree.\n * Foreign harnesses mounting the tool outside the graphorin loop keep\n * the plain `execute()` behavior (a suspended child throws there).\n */\nexport const SUBAGENT_TOOL: unique symbol = Symbol.for('graphorin.SubAgentTool');\n\n/** Child taint flags carried across the toTool fold (D2). */\nexport interface SubAgentFoldTaint {\n readonly untrusted?: boolean;\n readonly sensitive?: boolean;\n readonly sourceKind?: string;\n}\n\n/**\n * The live references {@link SUBAGENT_TOOL} carries (W-001). Typed\n * loosely on purpose: the walk is generic over foreign TDeps/TOutput\n * and only ever passes through values it received from the same\n * factory instance.\n */\nexport interface SubAgentToolRefs {\n readonly agentName: string;\n readonly run: (\n input: unknown,\n options?: Record<string, unknown>,\n ) => Promise<AgentResult<unknown>>;\n readonly stream: (\n input: unknown,\n options?: Record<string, unknown>,\n ) => AsyncIterable<AgentEvent<unknown>>;\n /** D2 capability restriction from `AgentToToolOptions.capability`. */\n readonly capability?: 'read-only';\n /** W-036: forwarding policy from `AgentToToolOptions.forwardEvents`. */\n readonly forwardEvents?: 'none' | 'lifecycle' | 'all';\n /** Reproduce `execute()`'s seed semantics (inputFilter + input string). */\n readonly buildSeed: (\n input: { readonly input: string },\n parentMessages: ReadonlyArray<Message>,\n ) => AgentInput;\n /** Reproduce `execute()`'s output shaping (exposeTurns/contextFold/taint). */\n readonly shapeCompleted: (\n result: AgentResult<unknown>,\n turns: ReadonlyArray<string>,\n ) => { readonly output: unknown; readonly taint?: SubAgentFoldTaint };\n}\n\n/** Read the {@link SUBAGENT_TOOL} refs off a (possibly wrapped) tool. */\nexport function getSubAgentToolRefs(tool: unknown): SubAgentToolRefs | undefined {\n if (typeof tool !== 'object' || tool === null) return undefined;\n const refs = (tool as Record<PropertyKey, unknown>)[SUBAGENT_TOOL];\n return refs === undefined ? undefined : (refs as SubAgentToolRefs);\n}\n\n/** Build the agent's `toTool` method over the factory's run surface. */\nexport function createToTool<TDeps, TOutput>(\n deps: ToToolDeps<TDeps, TOutput>,\n): (options?: AgentToToolOptions) => Tool<{ readonly input: string }, TOutput, TDeps> {\n const { config, run, stream } = deps;\n\n // D2: distil a completed child run into a compact, bounded outcome the\n // parent folds into its context instead of the raw transcript/output.\n const foldRunOutcome = (result: AgentResult<TOutput>, maxChars: number): string => {\n const steps = result.state.steps;\n const toolNames = [\n ...new Set(steps.flatMap((step) => step.toolCalls.map((c) => c.call.toolName))),\n ];\n const header =\n `[sub-agent '${config.name}' outcome] status=${result.status}; ` +\n `steps=${steps.length}; toolCalls=${steps.reduce((n, st) => n + st.toolCalls.length, 0)}` +\n (toolNames.length > 0 ? `; tools=${toolNames.join(', ')}` : '');\n const text = typeof result.output === 'string' ? result.output : JSON.stringify(result.output);\n const body =\n text.length > maxChars\n ? `${text.slice(0, maxChars)}\\n[... ${text.length - maxChars} chars truncated by contextFold]`\n : text;\n return `${header}\\n${body}`;\n };\n\n // D2: carry the child's coarse taint flags across the fold so the\n // parent's data-flow ledger re-arms (widen-only; a no-op when the\n // parent has no dataFlowPolicy).\n const taintFromChildState = (\n state: RunState,\n ): { untrusted?: boolean; sensitive?: boolean; sourceKind?: string } | undefined => {\n const summary = state.taintSummary;\n if (summary === undefined || (!summary.untrustedSeen && !summary.sensitiveSeen)) {\n return undefined;\n }\n return {\n ...(summary.untrustedSeen ? { untrusted: true } : {}),\n ...(summary.sensitiveSeen ? { sensitive: true } : {}),\n sourceKind: 'sub-agent',\n };\n };\n\n const toTool = (\n options: AgentToToolOptions = {},\n ): Tool<{ readonly input: string }, TOutput, TDeps> => {\n const exposeTurns = options.exposeTurns ?? 'final';\n const foldMaxChars =\n options.contextFold === undefined || options.contextFold === false\n ? null\n : typeof options.contextFold === 'object'\n ? (options.contextFold.maxChars ?? 2000)\n : 2000;\n const propagateTaint = options.propagateTaint !== false;\n const toolName = options.name ?? `subagent_${config.name}`;\n const description = options.description ?? `Invoke sub-agent '${config.name}'.`;\n\n // Shared by `execute()` and the W-001 inline walk: seed semantics\n // (least authority - input string only unless an inputFilter opts\n // parent history in) and completed-result shaping.\n const buildSeed = (\n input: { readonly input: string },\n parentMessages: ReadonlyArray<Message> | undefined,\n ): AgentInput =>\n options.inputFilter !== undefined && parentMessages !== undefined\n ? ([\n ...options.inputFilter(parentMessages),\n { role: 'user' as const, content: input.input },\n ] as Message[])\n : input.input;\n const shapeCompleted = (\n result: AgentResult<TOutput>,\n turns: ReadonlyArray<string>,\n ): { readonly output: unknown; readonly taint?: SubAgentFoldTaint } => {\n const taint = propagateTaint ? taintFromChildState(result.state) : undefined;\n const output =\n exposeTurns === 'all'\n ? foldMaxChars !== null\n ? foldRunOutcome(result, foldMaxChars)\n : turns.join('\\n\\n')\n : exposeTurns === 'none'\n ? ''\n : foldMaxChars !== null\n ? foldRunOutcome(result, foldMaxChars)\n : result.output;\n return { output, ...(taint !== undefined ? { taint } : {}) };\n };\n\n const schema = {\n parse: (v: unknown): { readonly input: string } => v as { readonly input: string },\n safeParse: (v: unknown) => ({\n success: true as const,\n data: v as { readonly input: string },\n }),\n toJSON: (): Record<string, unknown> => ({\n type: 'object',\n properties: { input: { type: 'string' } },\n required: ['input'],\n }),\n };\n const tool: Tool<{ readonly input: string }, TOutput, TDeps> = {\n name: toolName,\n description,\n inputSchema: schema as unknown as Tool<\n { readonly input: string },\n TOutput,\n TDeps\n >['inputSchema'],\n sideEffectClass: 'side-effecting',\n async execute(input, ctx) {\n // AG-17: the parent ToolExecutionContext propagates - the\n // parent's abort stops the sub-agent, deps/sessionId flow\n // through, and the optional `inputFilter` shapes a seed from\n // the parent history. Least authority by default: without a\n // filter the sub-agent sees ONLY the input string, never the\n // parent conversation.\n const callOpts: AgentCallOptions<TDeps> = {\n ...(ctx?.signal !== undefined ? { signal: ctx.signal } : {}),\n ...(ctx?.runContext.deps !== undefined ? { deps: ctx.runContext.deps as TDeps } : {}),\n ...(ctx?.runContext.sessionId !== undefined\n ? { sessionId: ctx.runContext.sessionId }\n : {}),\n // D2: run the child under a restricted capability (read-only\n // workers in an orchestrator-worker fan-out).\n ...(options.capability !== undefined ? { capability: options.capability } : {}),\n // W-036: the child's run span parents under the calling tool's\n // span - one trace tree even through the executor path.\n ...(ctx?.runContext.span !== undefined ? { parentSpan: ctx.runContext.span } : {}),\n };\n const seed = buildSeed(input, ctx?.runContext.messages);\n if (exposeTurns === 'all') {\n // Replay the streamed text completions as the result so\n // the parent agent sees every turn the sub-agent\n // produced. `exposeTurns: 'final'` (default) and\n // `'none'` skip the per-turn assembly.\n const turns: string[] = [];\n let endResult: AgentResult<TOutput> | undefined;\n for await (const ev of stream(seed, callOpts)) {\n if (ev.type === 'text.complete') turns.push(ev.text);\n else if (ev.type === 'agent.end') endResult = ev.result;\n }\n // W-033: fold BEFORE the non-completed throw - tokens were\n // spent whether or not the child completed.\n if (endResult !== undefined) {\n foldIntoParent(\n ctx as ToolExecutionContext<unknown> | undefined,\n endResult.state,\n config.name,\n );\n }\n if (endResult !== undefined && endResult.status !== 'completed') {\n throw new Error(\n `sub-agent '${config.name}' ${endResult.status}${\n endResult.error !== undefined ? `: ${endResult.error.message}` : ''\n }`,\n );\n }\n if (endResult === undefined) return turns.join('\\n\\n') as unknown as TOutput;\n const shapedAll = shapeCompleted(endResult, turns);\n return (shapedAll.taint !== undefined\n ? toolReturn({ output: shapedAll.output, taint: shapedAll.taint })\n : shapedAll.output) as unknown as TOutput;\n }\n const result = await run(seed, callOpts);\n // W-033: fold BEFORE the non-completed throw (see 'all' branch).\n foldIntoParent(ctx as ToolExecutionContext<unknown> | undefined, result.state, config.name);\n // AG-17/AG-22 class: a non-completed sub-run is a TOOL ERROR,\n // never an empty-string success.\n if (result.status !== 'completed') {\n throw new Error(\n `sub-agent '${config.name}' ${result.status}${\n result.error !== undefined ? `: ${result.error.message}` : ''\n }`,\n );\n }\n const shaped = shapeCompleted(result, []);\n return (shaped.taint !== undefined\n ? toolReturn({ output: shaped.output, taint: shaped.taint })\n : shaped.output) as unknown as TOutput;\n },\n };\n // W-001: attach the inline-walk refs (see SUBAGENT_TOOL). Symbol\n // properties survive the registry's `{...tool}` normalization spread.\n const refs: SubAgentToolRefs = {\n agentName: config.name,\n run: run as unknown as SubAgentToolRefs['run'],\n stream: stream as unknown as SubAgentToolRefs['stream'],\n ...(options.capability !== undefined ? { capability: options.capability } : {}),\n ...(options.forwardEvents !== undefined ? { forwardEvents: options.forwardEvents } : {}),\n buildSeed,\n shapeCompleted: shapeCompleted as unknown as SubAgentToolRefs['shapeCompleted'],\n };\n (tool as unknown as Record<PropertyKey, unknown>)[SUBAGENT_TOOL] = refs;\n return tool;\n };\n\n return toTool;\n}\n"],"mappings":"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"}
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvals.js","names":["probeCtx: ToolExecutionContext","stillPending: ToolApproval[]"],"sources":["../../src/runtime/approvals.ts"],"sourcesContent":["/**\n * Durable HITL approval handling for the agent runtime: the\n * pre-execution `needsApproval` probe (with its rejecting secrets\n * accessor), gated-args validation at the pre-screen, resume-directive\n * processing (grant / deny decisions against persisted pending\n * approvals), and the exactly-once dispatch of resumed approved calls\n * with write-ahead intent checkpoints. Extracted verbatim from\n * `factory.ts` (issue #23).\n *\n * @packageDocumentation\n */\n\nimport type {\n AgentEvent,\n AgentResult,\n CompletedToolCall,\n Message,\n RunContext,\n RunState,\n Tool,\n ToolApproval,\n ToolCall,\n ToolExecutionContext,\n UsageAccumulator,\n} from '@graphorin/core';\nimport { NOOP_LOGGER, zeroUsage } from '@graphorin/core';\nimport type { ToolExecutor } from '@graphorin/tools/executor';\nimport type { ToolRegistry } from '@graphorin/tools/registry';\nimport { SubAgentResumeTargetNotFoundError } from '../errors/index.js';\nimport { serializeRunState } from '../run-state/index.js';\nimport type { AgentCallOptions, AgentConfig, ResumeDirective } from '../types.js';\nimport { getSubAgentToolRefs } from './agent-to-tool.js';\nimport type { DispatchBatchFn } from './dispatch.js';\nimport { composeSubRunPath, type HandoffEntry, splitSubRunPath } from './handoff.js';\nimport { foldChildRunUsage, renderToolErrorMessage } from './messages.js';\nimport type { MutableRunState } from './run-input.js';\n\n/**\n * Pre-execution approval screen (Adapter G / durable HITL). Evaluates a\n * (registry-resolved) tool's `needsApproval` against the realized args.\n * Returns `true` when the run must suspend before the tool executes.\n *\n * Actual execution flows through the `@graphorin/tools` executor, whose\n * `ApprovalGate` auto-grants because only no-approval / pre-approved\n * calls ever reach it; this probe is what keeps the suspend in the\n * agent so the durable-HITL contract (persist `RunState`, resume via\n * directive) is preserved.\n */\nexport async function invokeNeedsApproval(\n tool: Pick<Tool, 'needsApproval'> | undefined,\n args: unknown,\n baseCtx: RunContext,\n signal: AbortSignal,\n): Promise<boolean> {\n const predicate = tool?.needsApproval;\n if (predicate === undefined || predicate === false) return false;\n if (predicate === true) return true;\n const probeCtx: ToolExecutionContext = {\n toolCallId: 'probe',\n runContext: baseCtx,\n signal,\n tracer: baseCtx.tracer,\n logger: NOOP_LOGGER,\n secrets: probeSecretsAccessor(),\n reportProgress: () => {},\n streamContent: () => {},\n };\n return Boolean(await predicate(args as never, probeCtx));\n}\n\n/**\n * Rejecting secrets accessor used only by the {@link invokeNeedsApproval}\n * probe. Real tool execution resolves secrets through the executor's\n * ACL-scoped accessor; an approval predicate has no legitimate need to\n * read secret material, so every `require(...)` rejects.\n */\nfunction probeSecretsAccessor(): ToolExecutionContext['secrets'] {\n const rejector = (_key: string, _options?: { readonly optional?: boolean }): Promise<never> =>\n Promise.reject(new Error('secrets.require is unavailable inside a needsApproval predicate'));\n return { require: rejector } as unknown as ToolExecutionContext['secrets'];\n}\n\n/**\n * tools-02: validate an approval-gated call's args at the pre-screen so\n * the gate decision - and what a human is asked to approve - is the input\n * that will actually execute. Structural + defensive: `undefined` when\n * the tool exposes no callable `safeParse` (nothing to validate here; the\n * executor still validates at dispatch); a throwing schema counts as a\n * validation failure rather than crashing the loop.\n */\nexport function safeParseGatedArgs(\n tool: { readonly inputSchema?: unknown },\n args: unknown,\n):\n | { readonly success: true; readonly data: unknown }\n | { readonly success: false; readonly message: string }\n | undefined {\n const schema = tool.inputSchema as { safeParse?: (value: unknown) => unknown } | null | undefined;\n const safeParse = schema?.safeParse;\n if (typeof safeParse !== 'function') return undefined;\n try {\n const parsed = safeParse.call(schema, args) as {\n readonly success?: boolean;\n readonly data?: unknown;\n readonly error?: { readonly message?: string };\n };\n if (parsed.success === true) return { success: true, data: parsed.data };\n return {\n success: false,\n message: parsed.error?.message ?? 'schema validation failed',\n };\n } catch (cause) {\n return {\n success: false,\n message: cause instanceof Error ? cause.message : String(cause),\n };\n }\n}\n\n/** The run-scoped context the resume-directive pass operates on. */\nexport interface ResumeRunEnv {\n readonly state: MutableRunState & RunState;\n readonly messages: Message[];\n}\n\n/**\n * Process resume directive - apply approval decisions to any\n * pending approvals captured in the previous suspend.\n *\n * Fills `resumedApprovedCalls` / `grantedApprovals` in place (they are\n * declared by the run loop, which dispatches the granted subset next).\n */\n/** One decision routed into a parked sub-run (W-001). */\nexport interface RoutedSubRunDecision {\n readonly toolCallId: string;\n readonly granted: boolean;\n readonly reason?: string;\n /** Remaining routing path for nested parks (one segment per level). */\n readonly subRunToolCallId?: string;\n}\n\n/**\n * Composite decision key (W-001): child-local toolCallIds of two\n * different parked children may collide, so a decision matches a\n * pending approval only when BOTH `toolCallId` and `subRunToolCallId`\n * agree (`undefined` on both sides = the parent's own approvals).\n */\nfunction decisionKey(toolCallId: string, subRunToolCallId: string | undefined): string {\n return `${toolCallId}\u0000${subRunToolCallId ?? ''}`;\n}\n\nexport async function* processResumeDirective<TOutput>(\n env: ResumeRunEnv,\n approvals: NonNullable<ResumeDirective['approvals']>,\n resumedApprovedCalls: ToolCall[],\n grantedApprovals: ToolApproval[],\n subRunDecisionsOut?: Map<string, Array<RoutedSubRunDecision>>,\n): AsyncGenerator<AgentEvent<TOutput>, void, void> {\n const { state, messages } = env;\n // Step-journal: tool calls already completed on a prior resume are recorded\n // in the journal (`state.steps`); a re-resume must not run their side effects\n // again. Collect their ids so an approved call already journaled is replayed,\n // not re-executed (exactly-once; AG-1).\n const journaledCallIds = new Set<string>();\n for (const step of state.steps) {\n for (const completed of step.toolCalls) journaledCallIds.add(completed.call.toolCallId);\n }\n\n const decisions = new Map(\n approvals.map((d) => [decisionKey(d.toolCallId, d.subRunToolCallId), d]),\n );\n const stillPending: ToolApproval[] = [];\n for (const approval of state.pendingApprovals) {\n const decision = decisions.get(decisionKey(approval.toolCallId, approval.subRunToolCallId));\n if (decision === undefined) {\n stillPending.push(approval);\n continue;\n }\n // W-001: a decision addressed to a PARKED sub-run resolves here (the\n // approval leaves the parent queue and the grant/deny event streams)\n // but EXECUTES inside the child: one path segment is stripped and the\n // decision routes to the parked run's own resume (deeper segments\n // route recursively at each level). No parent tool message is written\n // for a denied child id - the parent transcript has no matching\n // tool_use.\n if (approval.subRunToolCallId !== undefined) {\n yield decision.granted\n ? { type: 'tool.approval.granted', toolCallId: approval.toolCallId }\n : {\n type: 'tool.approval.denied',\n toolCallId: approval.toolCallId,\n ...(decision.reason !== undefined ? { reason: decision.reason } : {}),\n };\n if (subRunDecisionsOut !== undefined) {\n const { head, rest } = splitSubRunPath(approval.subRunToolCallId);\n const bucket = subRunDecisionsOut.get(head) ?? [];\n bucket.push({\n toolCallId: approval.toolCallId,\n granted: decision.granted,\n ...(decision.reason !== undefined ? { reason: decision.reason } : {}),\n ...(rest !== undefined ? { subRunToolCallId: rest } : {}),\n });\n subRunDecisionsOut.set(head, bucket);\n }\n continue;\n }\n if (decision.granted) {\n yield {\n type: 'tool.approval.granted',\n toolCallId: approval.toolCallId,\n };\n // Step-journal: if this approved call already ran on a prior resume -\n // journaled in `state.steps` with its result still in the message\n // buffer - replay it instead of running the side effect again\n // (exactly-once across re-resumes). If the journal entry exists but its\n // result message was lost, fall through to a single re-execution (the\n // documented \"at most one re-execution\" bound).\n if (\n journaledCallIds.has(approval.toolCallId) &&\n messages.some((m) => m.role === 'tool' && m.toolCallId === approval.toolCallId)\n ) {\n continue;\n }\n // AG-1: queue the approved call for REAL execution (dispatched\n // below). It runs through the same ToolExecutor as any other tool\n // call - taint / audit / result recording - instead of pushing a\n // \"[not actually executed]\" placeholder that left the gated side\n // effect unreachable.\n resumedApprovedCalls.push({\n toolCallId: approval.toolCallId,\n toolName: approval.toolName,\n args: approval.args,\n });\n grantedApprovals.push(approval);\n } else {\n yield {\n type: 'tool.approval.denied',\n toolCallId: approval.toolCallId,\n ...(decision.reason !== undefined ? { reason: decision.reason } : {}),\n };\n messages.push({\n role: 'tool',\n toolCallId: approval.toolCallId,\n content: `Error: tool approval denied${decision.reason ? `: ${decision.reason}` : ''}`,\n });\n state.messages.push({\n role: 'tool',\n toolCallId: approval.toolCallId,\n content: `Error: tool approval denied${decision.reason ? `: ${decision.reason}` : ''}`,\n });\n }\n }\n // Clear the queue + restore the running status so the loop\n // resumes from where it paused.\n state.pendingApprovals.splice(0, state.pendingApprovals.length, ...stillPending);\n if (stillPending.length === 0) {\n state.status = 'running';\n }\n}\n\n/** What the resumed-approval dispatch needs from the run loop's scope. */\nexport interface ResumedDispatchEnv<TDeps, TOutput> {\n readonly config: Pick<AgentConfig<TDeps, TOutput>, 'checkpointStore'>;\n readonly state: MutableRunState & RunState;\n readonly messages: Message[];\n readonly runContextBase: RunContext<TDeps>;\n readonly toolExecutor: ToolExecutor;\n readonly dispatchBatch: DispatchBatchFn<TDeps, TOutput>;\n}\n\n/**\n * AG-1: execute the approved gated calls for REAL before the provider\n * loop - the model sees their genuine results on the first step. They\n * run through the shared ToolExecutor (taint / audit) and record\n * CompletedToolCalls in a resume step. Dispatching here (outside the\n * loop's approval pre-screen) also means the gated call never\n * re-suspends, so there is no livelock.\n *\n * agent-07: this dispatch runs even when OTHER approvals remain\n * pending. A granted call has already been removed from\n * `pendingApprovals`, so skipping it (as the old order did - the\n * suspended-guard returned before the dispatch) stranded it\n * unrunnable forever; the run re-suspends with the remainder below.\n */\nexport async function* dispatchResumedApprovals<TDeps, TOutput>(\n env: ResumedDispatchEnv<TDeps, TOutput>,\n resumedApprovedCalls: ToolCall[],\n grantedApprovals: ToolApproval[],\n): AsyncGenerator<AgentEvent<TOutput>, void, void> {\n const { config, state, messages, runContextBase, toolExecutor, dispatchBatch } = env;\n // W-035: the resume step continues the journal's numbering instead of\n // aliasing step 0 - `RunStep.stepNumber` stays unique across any\n // number of suspend/resume cycles (replay itself keys on toolCallId\n // and never depends on this number).\n const resumeStepNumber = state.steps.reduce((max, s) => Math.max(max, s.stepNumber), 0) + 1;\n // agent-02 write-ahead intent: persist a checkpoint equivalent to\n // the pre-dispatch suspended state (granted approvals re-attached\n // to `pendingApprovals`) BEFORE any side effect runs. A crash-retry\n // against this checkpoint re-resumes with the same directive and\n // re-dispatches - the documented at-most-one-re-execution bound -\n // and its nodeName records that a grant arrived and dispatch was in\n // flight.\n if (config.checkpointStore !== undefined) {\n const prevStatus = state.status;\n state.status = 'awaiting_approval';\n state.pendingApprovals.unshift(...grantedApprovals);\n const intentState = serializeRunState(state, { stripTracingApiKey: true });\n state.pendingApprovals.splice(0, grantedApprovals.length);\n state.status = prevStatus;\n await config.checkpointStore.put(\n state.id,\n 'agent',\n {\n id: state.id,\n threadId: state.id,\n namespace: 'agent',\n state: intentState,\n channelVersions: {},\n stepNumber: resumeStepNumber,\n createdAt: new Date().toISOString(),\n },\n {\n source: 'sync',\n status: 'suspended',\n nodeName: 'agent.resume.intent',\n sessionId: state.sessionId,\n },\n );\n }\n state.steps.push({\n stepNumber: resumeStepNumber,\n startedAt: new Date().toISOString(),\n endedAt: new Date().toISOString(),\n usage: zeroUsage(),\n toolCalls: [],\n agentId: state.currentAgentId,\n });\n // tools-02: a human granted exactly `approval.args` - the repair\n // hook must not rewrite a pre-approved payload behind the grant, so\n // a (should-be-impossible) validation failure surfaces as\n // `invalid_input` instead of executing args nobody saw.\n yield* dispatchBatch(\n resumedApprovedCalls,\n toolExecutor,\n { ...runContextBase, stepNumber: resumeStepNumber, messages },\n resumeStepNumber,\n { disableRepair: true },\n );\n // agent-02: persist the journaled post-dispatch state. From THIS\n // checkpoint a re-delivered resume is exactly-once: the granted ids\n // are no longer pending and their journal entries + tool messages\n // are present, so nothing re-dispatches. (For the manual JSON flow,\n // the same state is returned as `result.state` - persist it after\n // every resume to get the same guarantee.)\n if (config.checkpointStore !== undefined) {\n await config.checkpointStore.put(\n state.id,\n 'agent',\n {\n id: state.id,\n threadId: state.id,\n namespace: 'agent',\n state: serializeRunState(state, { stripTracingApiKey: true }),\n channelVersions: {},\n stepNumber: resumeStepNumber,\n createdAt: new Date().toISOString(),\n },\n {\n source: 'sync',\n status: state.status === 'awaiting_approval' ? 'suspended' : 'running',\n nodeName: 'agent.resume.dispatched',\n sessionId: state.sessionId,\n },\n );\n }\n}\n\n/** What the W-001 sub-run resume router needs from the run loop's scope. */\nexport interface SubRunResumeEnv<TDeps, TOutput> {\n readonly config: Pick<AgentConfig<TDeps, TOutput>, 'deps' | 'checkpointStore'>;\n readonly options: AgentCallOptions<TDeps>;\n readonly state: MutableRunState & RunState;\n readonly messages: Message[];\n readonly handoffMap: ReadonlyMap<string, HandoffEntry<TDeps>>;\n readonly toolRegistry: ToolRegistry;\n readonly usageAcc: UsageAccumulator;\n readonly signal: AbortSignal;\n readonly sessionId: string;\n}\n\n/**\n * W-001: resume the parked sub-agent runs a directive addressed. Each\n * routed decision batch replays into its child via `child.run(parked\n * state, { directive })` on the SAME parent instance's target (the\n * handoff map or the tool registry's `toTool` refs). Outcomes settle\n * exactly like the inline seam: a completed child's shaped output\n * becomes the parent's tool message for the parked toolCallId, a\n * re-suspended child refreshes its park and re-suspends the parent\n * with the remainder, a failed child surfaces the typed tool error.\n * Nesting composes recursively: a grandchild's park rides inside the\n * child's own parked state.\n */\nexport async function* processSubRunResumes<TDeps, TOutput>(\n env: SubRunResumeEnv<TDeps, TOutput>,\n subRunDecisions: ReadonlyMap<string, ReadonlyArray<RoutedSubRunDecision>>,\n): AsyncGenerator<AgentEvent<TOutput>, void, void> {\n if (subRunDecisions.size === 0) return;\n const { config, options, state, messages, handoffMap, toolRegistry } = env;\n const { usageAcc, signal, sessionId } = env;\n const resumeStepNumber = state.steps.reduce((max, s) => Math.max(max, s.stepNumber), 0) + 1;\n const stepEntry = {\n stepNumber: resumeStepNumber,\n startedAt: new Date().toISOString(),\n endedAt: new Date().toISOString(),\n usage: zeroUsage(),\n toolCalls: [] as CompletedToolCall[],\n agentId: state.currentAgentId,\n };\n state.steps.push(stepEntry);\n\n for (const [parentToolCallId, decisions] of subRunDecisions) {\n const subs = state.pendingSubRuns ?? [];\n const parkedIdx = subs.findIndex((s) => s.toolCallId === parentToolCallId);\n const parked = subs[parkedIdx];\n if (parked === undefined) {\n throw new SubAgentResumeTargetNotFoundError(\n '(unparked)',\n `no parked sub-run matches subRunToolCallId '${parentToolCallId}'`,\n );\n }\n // Resolve the child seam on THIS instance: handoff targets first\n // (their tool names live only in the handoff map), then `toTool`\n // sub-agent tools via their SUBAGENT_TOOL refs.\n const handoffEntry = handoffMap.get(parked.toolName);\n const subRefs =\n handoffEntry === undefined\n ? getSubAgentToolRefs(toolRegistry.get(parked.toolName))\n : undefined;\n if (handoffEntry === undefined && subRefs === undefined) {\n throw new SubAgentResumeTargetNotFoundError(\n parked.toolName,\n 'it matches neither a configured handoff target nor a toTool sub-agent tool on this instance',\n );\n }\n const childRun =\n handoffEntry !== undefined\n ? (input: unknown, opts?: Record<string, unknown>) =>\n handoffEntry.agent.run(input as RunState, opts as AgentCallOptions<TDeps>)\n : (subRefs as NonNullable<typeof subRefs>).run;\n const agentName = parked.targetAgentName;\n yield { type: 'tool.execute.start', toolCallId: parked.toolCallId };\n const childStart = Date.now();\n const childResult = (await childRun(parked.state, {\n signal,\n ...(options.deps !== undefined || config.deps !== undefined\n ? { deps: options.deps ?? config.deps }\n : {}),\n sessionId,\n directive: {\n approvals: decisions as unknown as Array<{ toolCallId: string; granted: boolean }>,\n },\n })) as AgentResult<unknown>;\n const childDurationMs = Date.now() - childStart;\n\n if (childResult.status === 'awaiting_approval') {\n // Nested (or partial) approval round: refresh the park and\n // re-suspend the parent with the remainder. Stale mirrors of this\n // park (unresolved approvals a partial grant left in the parent\n // queue) are dropped first - the child's CURRENT remainder is the\n // single source of truth.\n subs[parkedIdx] = { ...parked, state: childResult.state };\n const keep = state.pendingApprovals.filter(\n (a) =>\n a.subRunToolCallId === undefined ||\n splitSubRunPath(a.subRunToolCallId).head !== parked.toolCallId,\n );\n state.pendingApprovals.splice(0, state.pendingApprovals.length, ...keep);\n for (const approval of childResult.state.pendingApprovals) {\n state.pendingApprovals.push({\n ...approval,\n subRunToolCallId: composeSubRunPath(parked.toolCallId, approval.subRunToolCallId),\n });\n yield {\n type: 'tool.approval.requested',\n toolCallId: approval.toolCallId,\n ...(approval.reason !== undefined ? { reason: approval.reason } : {}),\n };\n }\n state.status = 'awaiting_approval';\n continue;\n }\n\n // Terminal outcome: fold the child's cumulative usage exactly once.\n foldChildRunUsage(state, usageAcc, childResult.state, agentName);\n subs.splice(parkedIdx, 1);\n\n if (childResult.status !== 'completed') {\n const toolError = {\n toolCallId: parked.toolCallId,\n toolName: parked.toolName,\n kind:\n childResult.status === 'aborted' ? ('aborted' as const) : ('execution_failed' as const),\n message: `sub-run '${agentName}' ${childResult.status}${\n childResult.error !== undefined ? `: ${childResult.error.message}` : ''\n }`,\n };\n stepEntry.toolCalls.push({\n call: callOf(parked),\n outcome: toolError,\n stepNumber: resumeStepNumber,\n });\n yield { type: 'tool.execute.error', toolCallId: parked.toolCallId, error: toolError };\n const text = renderToolErrorMessage(toolError);\n messages.push({ role: 'tool', toolCallId: parked.toolCallId, content: text });\n state.messages.push({ role: 'tool', toolCallId: parked.toolCallId, content: text });\n continue;\n }\n\n const shaped =\n subRefs !== undefined\n ? subRefs.shapeCompleted(childResult, collectTurns(childResult))\n : { output: collectTurns(childResult).join('') };\n const rendered =\n typeof shaped.output === 'string' ? shaped.output : (JSON.stringify(shaped.output) ?? '');\n stepEntry.toolCalls.push({\n call: callOf(parked),\n outcome: {\n toolCallId: parked.toolCallId,\n toolName: parked.toolName,\n output: rendered,\n durationMs: childDurationMs,\n },\n stepNumber: resumeStepNumber,\n });\n yield {\n type: 'tool.execute.end',\n toolCallId: parked.toolCallId,\n result: rendered,\n durationMs: childDurationMs,\n };\n messages.push({ role: 'tool', toolCallId: parked.toolCallId, content: rendered });\n state.messages.push({ role: 'tool', toolCallId: parked.toolCallId, content: rendered });\n }\n\n if (state.pendingSubRuns !== undefined && state.pendingSubRuns.length === 0) {\n delete (state as { pendingSubRuns?: unknown }).pendingSubRuns;\n }\n\n // Persist the post-phase state (agent-02 shape): from this checkpoint a\n // re-delivered resume is a no-op - the routed approvals are gone and the\n // settled parks removed.\n if (config.checkpointStore !== undefined) {\n await config.checkpointStore.put(\n state.id,\n 'agent',\n {\n id: state.id,\n threadId: state.id,\n namespace: 'agent',\n state: serializeRunState(state, { stripTracingApiKey: true }),\n channelVersions: {},\n stepNumber: resumeStepNumber,\n createdAt: new Date().toISOString(),\n },\n {\n source: 'sync',\n status: state.status === 'awaiting_approval' ? 'suspended' : 'running',\n nodeName: 'agent.subrun.resumed',\n sessionId: state.sessionId,\n },\n );\n }\n}\n\n/** Rebuild the parent-side ToolCall of a parked sub-run for the journal. */\nfunction callOf(parked: { readonly toolCallId: string; readonly toolName: string }): ToolCall {\n return { toolCallId: parked.toolCallId, toolName: parked.toolName, args: {} };\n}\n\n/**\n * A resumed child's `text.complete` turns are not observable through\n * `run()` - derive the final text from the completed state's last\n * assistant message (the same content `output` carries for plain runs).\n */\nfunction collectTurns(result: AgentResult<unknown>): string[] {\n return typeof result.output === 'string' && result.output.length > 0 ? [result.output] : [];\n}\n"],"mappings":"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"}