@arnilo/prism 0.3.2 → 0.5.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 (208) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +42 -62
  3. package/dist/agent-run-lifecycle.js +4 -0
  4. package/dist/agent-run-state.d.ts +5 -2
  5. package/dist/agent-run-state.js +18 -8
  6. package/dist/agent-session/session/assemble.d.ts +6 -0
  7. package/dist/agent-session/session/assemble.js +391 -0
  8. package/dist/agent-session/session/persist.d.ts +28 -0
  9. package/dist/agent-session/session/persist.js +166 -0
  10. package/dist/agent-session/session/provider-round.d.ts +6 -0
  11. package/dist/agent-session/session/provider-round.js +231 -0
  12. package/dist/agent-session/session/tool-round.d.ts +31 -0
  13. package/dist/agent-session/session/tool-round.js +473 -0
  14. package/dist/agent-session/session/types.d.ts +115 -0
  15. package/dist/agent-session/session/types.js +5 -0
  16. package/dist/agent-session/session.d.ts +54 -41
  17. package/dist/agent-session/session.js +23 -1132
  18. package/dist/capture.d.ts +63 -0
  19. package/dist/capture.js +67 -0
  20. package/dist/cli-dev.d.ts +29 -0
  21. package/dist/cli-dev.js +52 -0
  22. package/dist/cli-init.d.ts +34 -3
  23. package/dist/cli-init.js +192 -24
  24. package/dist/cli-runner.d.ts +6 -2
  25. package/dist/cli-runner.js +57 -10
  26. package/dist/content.d.ts +3 -3
  27. package/dist/content.js +3 -1
  28. package/dist/contracts-core/agent.d.ts +8 -0
  29. package/dist/contracts-core/batch.d.ts +97 -0
  30. package/dist/contracts-core/batch.js +65 -0
  31. package/dist/contracts-core/content.d.ts +72 -1
  32. package/dist/contracts-core/embeddings.d.ts +30 -0
  33. package/dist/contracts-core/embeddings.js +17 -0
  34. package/dist/contracts-core/images.d.ts +60 -0
  35. package/dist/contracts-core/images.js +17 -0
  36. package/dist/contracts-core/moderation.d.ts +46 -0
  37. package/dist/contracts-core/moderation.js +34 -0
  38. package/dist/contracts-core/speech.d.ts +39 -0
  39. package/dist/contracts-core/speech.js +17 -0
  40. package/dist/contracts-core/transcription.d.ts +48 -0
  41. package/dist/contracts-core/transcription.js +17 -0
  42. package/dist/contracts-core/video.d.ts +61 -0
  43. package/dist/contracts-core/video.js +17 -0
  44. package/dist/contracts-core.d.ts +7 -0
  45. package/dist/contracts-core.js +7 -0
  46. package/dist/contracts-protocol.d.ts +18 -0
  47. package/dist/contracts-run-state.d.ts +1 -2
  48. package/dist/index.d.ts +7 -3
  49. package/dist/index.js +5 -3
  50. package/dist/input.d.ts +8 -0
  51. package/dist/input.js +4 -0
  52. package/dist/node/agent-definitions.d.ts +1 -8
  53. package/dist/node/agent-definitions.js +0 -34
  54. package/dist/node/settings.d.ts +0 -1
  55. package/dist/node/settings.js +0 -5
  56. package/dist/pinned-fetch.js +29 -3
  57. package/dist/provider-events.js +3 -4
  58. package/dist/providers/media.d.ts +1 -2
  59. package/dist/providers/media.js +1 -4
  60. package/dist/rpc.d.ts +1 -1
  61. package/dist/rpc.js +4 -4
  62. package/dist/testing/persistence-schema.d.ts +1 -1
  63. package/dist/testing/persistence-schema.js +32 -28
  64. package/dist/testing/provider-conformance.d.ts +114 -5
  65. package/dist/testing/provider-conformance.js +342 -0
  66. package/dist/testing/tool-conformance.d.ts +25 -0
  67. package/dist/testing/tool-conformance.js +128 -1
  68. package/dist/testing/tool-effect-store-conformance.d.ts +0 -1
  69. package/dist/testing/tool-effect-store-conformance.js +0 -3
  70. package/dist/thinking.d.ts +48 -9
  71. package/dist/thinking.js +134 -8
  72. package/dist/tool-search.d.ts +76 -0
  73. package/dist/tool-search.js +199 -0
  74. package/docs/0.1.0-readiness.md +3 -3
  75. package/docs/a2a.md +2 -2
  76. package/docs/acp-agent.md +1 -1
  77. package/docs/acp.md +3 -3
  78. package/docs/ag-ui-adoption.md +1 -1
  79. package/docs/ag-ui.md +1 -2
  80. package/docs/agent-definitions.md +1 -1
  81. package/docs/agent-events.md +5 -5
  82. package/docs/agent-identity.md +13 -2
  83. package/docs/audit-export.md +3 -3
  84. package/docs/batch-jobs.md +120 -0
  85. package/docs/browser-automation.md +5 -5
  86. package/docs/caveman.md +2 -2
  87. package/docs/cli-rpc.md +43 -9
  88. package/docs/coding-agent-tools.md +19 -19
  89. package/docs/coding-review-and-diagnostics.md +2 -2
  90. package/docs/coding-security.md +5 -5
  91. package/docs/coding-tools.md +82 -0
  92. package/docs/coding-workspaces.md +2 -2
  93. package/docs/compaction-and-retry.md +2 -2
  94. package/docs/compaction-llm.md +4 -4
  95. package/docs/compaction-observational-memory.md +3 -3
  96. package/docs/computer-use-linux.md +13 -2
  97. package/docs/context-and-skills.md +3 -1
  98. package/docs/conversations.md +4 -4
  99. package/docs/core.md +85 -0
  100. package/docs/credential-storage.md +12 -8
  101. package/docs/credentials-and-redaction.md +1 -1
  102. package/docs/data-classification.md +1 -1
  103. package/docs/database-persistence.md +7 -3
  104. package/docs/dev-inspector.md +103 -0
  105. package/docs/device-adapters.md +2 -2
  106. package/docs/diagrams.md +247 -0
  107. package/docs/document-reader.md +6 -6
  108. package/docs/documents.md +214 -0
  109. package/docs/embeddings.md +112 -0
  110. package/docs/enterprise-postgres-state.md +7 -7
  111. package/docs/evaluations.md +41 -7
  112. package/docs/extensions.md +3 -3
  113. package/docs/forge-integration.md +3 -3
  114. package/docs/graft.md +5 -5
  115. package/docs/guardrails.md +2 -2
  116. package/docs/host-security.md +16 -15
  117. package/docs/image-generation.md +129 -0
  118. package/docs/impeccable.md +7 -5
  119. package/docs/index.md +84 -46
  120. package/docs/indexed-code-search.md +2 -2
  121. package/docs/language-intelligence.md +4 -4
  122. package/docs/live-testing.md +126 -0
  123. package/docs/mcp-tools.md +44 -13
  124. package/docs/middleware-hooks.md +1 -1
  125. package/docs/migrate-to-0.4.md +312 -0
  126. package/docs/migrate-to-0.5.md +122 -0
  127. package/docs/migration.md +51 -1
  128. package/docs/model-registry.md +38 -0
  129. package/docs/model-routing.md +6 -6
  130. package/docs/moderation.md +117 -0
  131. package/docs/multi-agent-patterns.md +177 -0
  132. package/docs/multimodal-content.md +27 -3
  133. package/docs/obscura.md +12 -12
  134. package/docs/observability.md +32 -7
  135. package/docs/openapi-tools.md +14 -4
  136. package/docs/operations.md +11 -0
  137. package/docs/performance.md +30 -10
  138. package/docs/persistence-credentials-multimodality-primitives.md +7 -7
  139. package/docs/policy-and-audit.md +18 -8
  140. package/docs/ponytail.md +3 -3
  141. package/docs/postgres-persistence.md +5 -5
  142. package/docs/process-sessions.md +2 -2
  143. package/docs/prompt-registry.md +106 -0
  144. package/docs/provider-caching.md +36 -32
  145. package/docs/provider-conformance.md +24 -2
  146. package/docs/provider-packages.md +58 -22
  147. package/docs/provider-primitives.md +5 -5
  148. package/docs/provider-request-policies.md +1 -1
  149. package/docs/providers/ai-sdk.md +18 -6
  150. package/docs/providers/alibaba.md +10 -6
  151. package/docs/providers/anthropic.md +10 -6
  152. package/docs/providers/azure.md +20 -4
  153. package/docs/providers/bedrock.md +18 -3
  154. package/docs/providers/clinepass.md +7 -3
  155. package/docs/providers/commandcode.md +253 -0
  156. package/docs/providers/deepseek.md +7 -3
  157. package/docs/providers/google.md +8 -4
  158. package/docs/providers/hyper.md +284 -0
  159. package/docs/providers/kimi.md +7 -3
  160. package/docs/providers/neuralwatt.md +12 -8
  161. package/docs/providers/ollama.md +18 -3
  162. package/docs/providers/openai-compatible.md +5 -1
  163. package/docs/providers/openai.md +9 -5
  164. package/docs/providers/opencode-go.md +8 -4
  165. package/docs/providers/openrouter.md +8 -4
  166. package/docs/providers/vertex.md +21 -5
  167. package/docs/providers/xai.md +7 -3
  168. package/docs/providers/zai.md +7 -3
  169. package/docs/rag.md +31 -9
  170. package/docs/release-and-install.md +181 -76
  171. package/docs/resource-loading.md +1 -1
  172. package/docs/runs-and-usage.md +28 -3
  173. package/docs/server.md +94 -5
  174. package/docs/settings-auth-trust-security.md +7 -5
  175. package/docs/sheets.md +229 -0
  176. package/docs/speech.md +126 -0
  177. package/docs/sqlite-persistence.md +4 -4
  178. package/docs/supervisors.md +4 -3
  179. package/docs/thinking-and-reasoning.md +93 -60
  180. package/docs/tool-conformance.md +28 -3
  181. package/docs/tool-execution-primitives.md +8 -8
  182. package/docs/tools.md +32 -5
  183. package/docs/web-tools.md +3 -3
  184. package/docs/wiki.md +7 -7
  185. package/docs/work-artifacts-and-review.md +17 -6
  186. package/docs/work-connectors.md +4 -4
  187. package/docs/work-tools.md +5 -5
  188. package/docs/workflow-orchestration-primitives.md +35 -11
  189. package/docs/workflows.md +74 -13
  190. package/docs/working-and-semantic-memory.md +53 -5
  191. package/package.json +14 -31
  192. package/templates/README.md +23 -0
  193. package/templates/deep-research/README.md.tmpl +47 -0
  194. package/templates/deep-research/env.example.tmpl +12 -0
  195. package/templates/deep-research/gitignore.tmpl +7 -0
  196. package/templates/deep-research/manifest.json +12 -0
  197. package/templates/deep-research/package.json.tmpl +23 -0
  198. package/templates/deep-research/src/agent.ts.tmpl +81 -0
  199. package/templates/deep-research/src/index.ts.tmpl +53 -0
  200. package/templates/deep-research/src/tests/research.test.ts.tmpl +114 -0
  201. package/templates/deep-research/src/tools.ts.tmpl +86 -0
  202. package/templates/deep-research/src/types.ts.tmpl +45 -0
  203. package/templates/deep-research/src/workflow.ts.tmpl +156 -0
  204. package/templates/deep-research/tsconfig.json.tmpl +15 -0
  205. package/templates/init/manifest.json +5 -0
  206. package/templates/init/package.json.tmpl +2 -1
  207. package/templates/init/providers.json +40 -24
  208. package/docs/antigravity-agent.md +0 -207
@@ -0,0 +1,473 @@
1
+ /** Tool-round phase of runInternal (plan 059). */
2
+ import { AgentRunSuspended, decisionIdentityRef, decisionScopesEqual, nestedApprovalId, nestedOutcomeToolResult, pathsEqual, } from "../../agent-approval.js";
3
+ import { toolElicitationRequest } from "../../agent-tool-dispatch.js";
4
+ import { AgentDecisionError, AgentDelegationSuspendedError, AgentRunStateError, DEFAULT_MAX_PENDING_DECISIONS, HARD_MAX_PENDING_DECISIONS, MAX_ATTRIBUTION_DEPTH, } from "../../contracts.js";
5
+ import { canonicalToolEffectJson, toolEffectArgumentsHash } from "../../tool-effects.js";
6
+ import { dispatchToolCall, resolveToolEffectDeclaration } from "../../tools.js";
7
+ import { randomId } from "../helpers.js";
8
+ import { persistDurable, suspendDurable } from "./persist.js";
9
+ export function matchNestedSticky(session, decision) {
10
+ const stickies = session.activeDurable?.state?.stickyDecisions;
11
+ return stickies?.find((sticky) => sticky.attribution !== undefined &&
12
+ pathsEqual(sticky.attribution.path, decision.attribution?.path) &&
13
+ decisionScopesEqual(sticky.scope, decision.scope));
14
+ }
15
+ export function matchStickyDecision(session, call, registry) {
16
+ const stickies = session.activeDurable?.state?.stickyDecisions;
17
+ if (!stickies?.length)
18
+ return undefined;
19
+ const identityRef = decisionIdentityRef(session.activeIdentity);
20
+ let argumentsHash;
21
+ let effectKind;
22
+ let effectResolved = false;
23
+ return stickies.find((sticky) => {
24
+ if (sticky.attribution !== undefined)
25
+ return false;
26
+ const scope = sticky.scope;
27
+ if (scope.toolName !== undefined && scope.toolName !== call.name)
28
+ return false;
29
+ if (scope.identity !== undefined && scope.identity !== identityRef)
30
+ return false;
31
+ if (scope.argumentsHash !== undefined) {
32
+ argumentsHash ??= toolEffectArgumentsHash(call.arguments);
33
+ if (scope.argumentsHash !== argumentsHash)
34
+ return false;
35
+ }
36
+ if (scope.effectKind !== undefined) {
37
+ if (!effectResolved) {
38
+ effectResolved = true;
39
+ const tool = registry.get(call.name);
40
+ effectKind = tool?.effect
41
+ ? resolveToolEffectDeclaration(tool, call.arguments, { sessionId: session.id, runId: "", toolCallId: call.id })?.kind
42
+ : undefined;
43
+ }
44
+ if (scope.effectKind !== effectKind)
45
+ return false;
46
+ }
47
+ if (scope.actionConstraints) {
48
+ for (const [key, value] of Object.entries(scope.actionConstraints)) {
49
+ const actual = call.arguments[key];
50
+ if (canonicalToolEffectJson(actual === undefined ? null : actual) !== canonicalToolEffectJson(value))
51
+ return false;
52
+ }
53
+ }
54
+ return true;
55
+ });
56
+ }
57
+ export function buildPendingDecision(session, call, approvalId, registry, runId, metadata, signal) {
58
+ const tool = registry.get(call.name);
59
+ const declaration = tool?.effect
60
+ ? resolveToolEffectDeclaration(tool, call.arguments, {
61
+ sessionId: session.id,
62
+ runId,
63
+ toolCallId: call.id,
64
+ signal,
65
+ metadata,
66
+ })
67
+ : undefined;
68
+ const identityRef = decisionIdentityRef(session.activeIdentity);
69
+ const elicitation = toolElicitationRequest(tool, call.arguments, {
70
+ sessionId: session.id,
71
+ runId,
72
+ toolCallId: call.id,
73
+ signal,
74
+ metadata,
75
+ });
76
+ return {
77
+ approvalId,
78
+ kind: elicitation ? "elicitation" : "tool_approval",
79
+ toolCallId: call.id,
80
+ scope: {
81
+ toolName: call.name,
82
+ argumentsHash: toolEffectArgumentsHash(call.arguments),
83
+ ...(declaration && declaration.kind !== "none" ? { effectKind: declaration.kind } : {}),
84
+ ...(identityRef ? { identity: identityRef } : {}),
85
+ },
86
+ reason: elicitation?.reason ?? "Tool side effect requires approval",
87
+ ...(elicitation ? { elicitationSchema: elicitation.schema } : {}),
88
+ };
89
+ }
90
+ export async function applyNestedRun(session, input) {
91
+ let current = input.pending;
92
+ for (let depth = 0;; depth += 1) {
93
+ const attributed = current.map((decision) => {
94
+ const id = nestedApprovalId(input.ref.runId, decision.approvalId);
95
+ return {
96
+ id,
97
+ childApprovalId: decision.approvalId,
98
+ decision: { ...decision, approvalId: id, attribution: decision.attribution ?? { path: input.path } },
99
+ };
100
+ });
101
+ if (attributed.some(({ decision }) => (decision.attribution?.path.length ?? 0) > MAX_ATTRIBUTION_DEPTH)) {
102
+ throw new AgentDecisionError("ERR_PRISM_DECISION_LIMIT", `Attribution path exceeds ${MAX_ATTRIBUTION_DEPTH} entries`);
103
+ }
104
+ const allSticky = attributed.length > 0 && attributed.every(({ decision }) => matchNestedSticky(session, decision) !== undefined);
105
+ if (!input.hook || !allSticky || depth >= 4) {
106
+ return {
107
+ entry: {
108
+ runId: input.ref.runId,
109
+ ...(input.ref.sessionId ? { sessionId: input.ref.sessionId } : {}),
110
+ toolCallId: input.toolCall.id,
111
+ path: attributed[0]?.decision.attribution?.path ?? input.path,
112
+ approvals: attributed.map(({ id, childApprovalId }) => ({ id, childApprovalId })),
113
+ },
114
+ pending: attributed.map(({ decision }) => decision),
115
+ };
116
+ }
117
+ const outcome = await input.hook({ ref: input.ref, toolCallId: input.toolCall.id, path: input.path }, attributed.map(({ childApprovalId, decision }) => {
118
+ const sticky = matchNestedSticky(session, decision);
119
+ return {
120
+ approvalId: childApprovalId,
121
+ outcome: sticky.outcome,
122
+ ...(sticky.reason !== undefined ? { reason: sticky.reason } : {}),
123
+ };
124
+ }));
125
+ if (outcome.status === "suspended") {
126
+ current = outcome.pendingDecisions;
127
+ continue;
128
+ }
129
+ return { toolResult: nestedOutcomeToolResult(outcome, input.toolCall.id, input.toolCall.name) };
130
+ }
131
+ }
132
+ export async function suspendGatedRound(ctx) {
133
+ const gated = ctx.session.activeGatedRound;
134
+ if (!gated?.size)
135
+ return;
136
+ const entries = [...gated.values()];
137
+ const decisions = entries.map((gatedCall) => gatedCall.decision);
138
+ const single = decisions.length === 1 ? decisions[0] : undefined;
139
+ const interruption = {
140
+ kind: single?.kind === "elicitation" ? "elicitation" : "tool_approval",
141
+ reason: single ? single.reason : `${decisions.length} tool side effects require approval`,
142
+ ...(single?.toolCallId ? { toolCallId: single.toolCallId } : {}),
143
+ ...(single?.scope.toolName ? { toolName: single.scope.toolName } : {}),
144
+ pendingDecisions: decisions,
145
+ };
146
+ throw new AgentRunSuspended(await suspendDurable(ctx.session, {
147
+ runId: ctx.runId,
148
+ model: ctx.model,
149
+ limits: ctx.limits,
150
+ interruption,
151
+ pendingCalls: entries.map((gatedCall) => gatedCall.entry),
152
+ }), interruption);
153
+ }
154
+ export async function suspendNested(ctx, nested) {
155
+ const state = ctx.session.activeDurable?.state;
156
+ const kept = (state?.pendingCalls ?? [])
157
+ .filter((entry) => entry.status === "ready")
158
+ .map((entry) => {
159
+ const decision = entry.decision ?? ctx.resumed?.decisions?.get(entry.approvalId);
160
+ return decision ? { ...entry, decision } : entry;
161
+ });
162
+ const gated = [...(ctx.session.activeGatedRound?.values() ?? [])];
163
+ const pendingCalls = [
164
+ ...kept,
165
+ ...gated.map((gatedCall) => gatedCall.entry),
166
+ { call: nested.toolCall, status: "ready", approvalId: `nr_${nested.entry.runId}` },
167
+ ];
168
+ const keptIds = new Set(kept.map((entry) => entry.approvalId));
169
+ const decisions = [
170
+ ...(state?.interruption?.pendingDecisions ?? []).filter((pending) => keptIds.has(pending.approvalId)),
171
+ ...gated.map((gatedCall) => gatedCall.decision),
172
+ ...nested.pending,
173
+ ];
174
+ if (decisions.length > HARD_MAX_PENDING_DECISIONS) {
175
+ throw new AgentDecisionError("ERR_PRISM_DECISION_LIMIT", `Pending decisions exceed ${HARD_MAX_PENDING_DECISIONS} per run`);
176
+ }
177
+ const single = decisions.length === 1 ? decisions[0] : undefined;
178
+ const interruption = {
179
+ kind: single?.kind ?? "tool_approval",
180
+ reason: single ? single.reason : `${decisions.length} approval request(s) need a decision`,
181
+ ...(single?.toolCallId ? { toolCallId: single.toolCallId } : {}),
182
+ ...(single?.scope.toolName ? { toolName: single.scope.toolName } : {}),
183
+ pendingDecisions: decisions,
184
+ };
185
+ throw new AgentRunSuspended(await suspendDurable(ctx.session, {
186
+ runId: ctx.runId,
187
+ model: ctx.model,
188
+ limits: ctx.limits,
189
+ interruption,
190
+ pendingCalls,
191
+ nestedRuns: [...(state?.nestedRuns ?? []), nested.entry],
192
+ }), interruption);
193
+ }
194
+ export async function replayToolResult(ctx, result) {
195
+ await ctx.session.appendMessage({
196
+ role: "tool",
197
+ content: [
198
+ { type: "tool_result", toolCallId: result.toolCallId, name: result.name, result: result.value, error: result.error },
199
+ ...(result.content ?? []),
200
+ ],
201
+ metadata: result.metadata,
202
+ }, ctx.runId);
203
+ }
204
+ export async function handleNestedSignal(ctx, error) {
205
+ const durableOptions = ctx.session.activeDurable?.options;
206
+ if (!durableOptions || !error.toolCall) {
207
+ throw new AgentDecisionError("ERR_PRISM_DECISION_INVALID", "Nested-run suspension requires durable run state and a hosting tool call");
208
+ }
209
+ if (error.pendingDecisions.length === 0) {
210
+ throw new AgentDecisionError("ERR_PRISM_DECISION_INVALID", "Nested-run suspension carried no pending decisions");
211
+ }
212
+ const applied = await applyNestedRun(ctx.session, {
213
+ ref: error.ref,
214
+ toolCall: error.toolCall,
215
+ path: error.path ?? [],
216
+ pending: error.pendingDecisions,
217
+ hook: durableOptions.resumeNestedRun,
218
+ });
219
+ if ("toolResult" in applied) {
220
+ await replayToolResult(ctx, applied.toolResult);
221
+ return;
222
+ }
223
+ await suspendNested(ctx, { entry: applied.entry, toolCall: error.toolCall, pending: applied.pending });
224
+ }
225
+ export function bindChargeToolRound(ctx) {
226
+ return (calls) => {
227
+ if (calls.length > 0)
228
+ ctx.limits.charge("maxToolRounds");
229
+ const durable = ctx.session.activeDurable;
230
+ if (!durable?.options.interruptBeforeTool || calls.length === 0)
231
+ return;
232
+ for (const call of calls) {
233
+ if (matchStickyDecision(ctx.session, call, ctx.registry))
234
+ continue;
235
+ const approvalId = randomId("approval");
236
+ ctx.session.activeGatedRound ??= new Map();
237
+ ctx.session.activeGatedRound.set(call.id, {
238
+ entry: { call, status: "ready", approvalId },
239
+ decision: buildPendingDecision(ctx.session, call, approvalId, ctx.registry, ctx.runId, ctx.metadata, ctx.controller.signal),
240
+ });
241
+ }
242
+ if (ctx.session.activeGatedRound && ctx.session.activeGatedRound.size > DEFAULT_MAX_PENDING_DECISIONS) {
243
+ throw new AgentDecisionError("ERR_PRISM_DECISION_LIMIT", `Pending decisions exceed ${DEFAULT_MAX_PENDING_DECISIONS} per run`);
244
+ }
245
+ };
246
+ }
247
+ export function bindDispatchToolCall(ctx) {
248
+ return async (call) => {
249
+ const sticky = matchStickyDecision(ctx.session, call, ctx.registry);
250
+ if (sticky?.outcome === "reject_for_run") {
251
+ return {
252
+ toolCallId: call.id,
253
+ name: call.name,
254
+ error: { code: "approval_rejected", message: sticky.reason ?? "Rejected for this run" },
255
+ };
256
+ }
257
+ if (ctx.session.activeGatedRound?.has(call.id)) {
258
+ return { toolCallId: call.id, name: call.name, metadata: { approvalPending: true } };
259
+ }
260
+ try {
261
+ return await dispatchToolCall({
262
+ call,
263
+ registry: ctx.registry,
264
+ context: {
265
+ sessionId: ctx.session.id,
266
+ runId: ctx.runId,
267
+ toolCallId: call.id,
268
+ signal: ctx.controller.signal,
269
+ metadata: {
270
+ ...ctx.metadata,
271
+ loadedSkills: ctx.session.loadedSkills,
272
+ activeTools: ctx.tools,
273
+ activeSkillNames: ctx.activeSkills.map((skill) => skill.name),
274
+ },
275
+ identity: ctx.session.activeIdentity,
276
+ },
277
+ middleware: ctx.session.agent.config.middleware,
278
+ emit: (event) => ctx.session.emit(event),
279
+ permission: ctx.session.agent.config.permission,
280
+ trust: ctx.session.agent.config.trust,
281
+ redactor: ctx.session.activeRedactor,
282
+ ledger: ctx.session.activeLedger,
283
+ effectStore: ctx.session.activeEffectStore,
284
+ ownership: ctx.session.activeOwnership,
285
+ identity: ctx.session.activeIdentity,
286
+ guardrails: ctx.session.activeGuardrails,
287
+ limitTracker: ctx.limits,
288
+ beforeExecute: async (mediatedCall) => {
289
+ const durable = ctx.session.activeDurable;
290
+ if (!durable)
291
+ return;
292
+ const pendingCalls = durable.state?.pendingCalls;
293
+ const matched = pendingCalls?.find((entry) => entry.call.id === mediatedCall.id && entry.status === "ready");
294
+ if (matched) {
295
+ await persistDurable(ctx.session, {
296
+ ...durable.state,
297
+ status: "running",
298
+ pendingCalls: pendingCalls.map((entry) => (entry === matched ? { ...entry, status: "dispatched" } : entry)),
299
+ interruption: undefined,
300
+ });
301
+ return;
302
+ }
303
+ const pending = durable.state?.pending;
304
+ if (pending?.call.id === mediatedCall.id && pending.status === "ready") {
305
+ await persistDurable(ctx.session, {
306
+ ...durable.state,
307
+ status: "running",
308
+ pending: { ...pending, status: "dispatched" },
309
+ interruption: undefined,
310
+ });
311
+ return;
312
+ }
313
+ if (!durable.options.interruptBeforeTool)
314
+ return;
315
+ if (matchStickyDecision(ctx.session, mediatedCall, ctx.registry)?.outcome === "allow_for_run")
316
+ return;
317
+ const approvalId = randomId("approval");
318
+ const decision = buildPendingDecision(ctx.session, mediatedCall, approvalId, ctx.registry, ctx.runId, ctx.metadata, ctx.controller.signal);
319
+ const interruption = {
320
+ kind: "tool_approval",
321
+ reason: decision.reason,
322
+ toolCallId: mediatedCall.id,
323
+ toolName: mediatedCall.name,
324
+ pendingDecisions: [decision],
325
+ };
326
+ throw new AgentRunSuspended(await suspendDurable(ctx.session, {
327
+ runId: ctx.runId,
328
+ model: ctx.model,
329
+ limits: ctx.limits,
330
+ interruption,
331
+ pending: { call: mediatedCall, status: "ready" },
332
+ pendingCalls: [{ call: mediatedCall, status: "ready", approvalId }],
333
+ }), interruption);
334
+ },
335
+ validate: ctx.validate,
336
+ });
337
+ }
338
+ catch (error) {
339
+ if (error instanceof AgentDelegationSuspendedError && !error.toolCall)
340
+ error.toolCall = call;
341
+ throw error;
342
+ }
343
+ };
344
+ }
345
+ export async function replayDurableNestedAndPending(ctx) {
346
+ const { session, resumed } = ctx;
347
+ let resumePendingCalls = resumed?.state?.pendingCalls;
348
+ if (resumed?.state?.nestedRuns?.length) {
349
+ const nestedRuns = resumed.state.nestedRuns;
350
+ const hook = session.activeDurable?.options.resumeNestedRun;
351
+ const oldPending = resumed.state.interruption?.pendingDecisions ?? [];
352
+ const nestedApprovalIds = new Set(nestedRuns.flatMap((entry) => entry.approvals.map((approval) => approval.id)));
353
+ const remainingNested = [];
354
+ const surfacedPending = [];
355
+ const resolvedToolCallIds = new Set();
356
+ for (const entry of nestedRuns) {
357
+ const grouped = [];
358
+ for (const approval of entry.approvals) {
359
+ const decision = entry.decisions?.[approval.id] ?? resumed.decisions?.get(approval.id);
360
+ if (decision)
361
+ grouped.push({ ...decision, approvalId: approval.childApprovalId });
362
+ }
363
+ if (grouped.length === 0) {
364
+ remainingNested.push(entry);
365
+ surfacedPending.push(...oldPending.filter((pending) => entry.approvals.some((approval) => approval.id === pending.approvalId)));
366
+ continue;
367
+ }
368
+ if (!hook) {
369
+ throw new AgentDecisionError("ERR_PRISM_DECISION_INVALID", "Nested-run decisions require a resumeNestedRun hook");
370
+ }
371
+ const toolCall = resumePendingCalls?.find((pending) => pending.call.id === entry.toolCallId)?.call;
372
+ if (!toolCall)
373
+ throw new AgentRunStateError("Nested run link is missing its tool call");
374
+ const ref = { runId: entry.runId, ...(entry.sessionId ? { sessionId: entry.sessionId } : {}) };
375
+ const outcome = await hook({ ref, toolCallId: entry.toolCallId, path: entry.path }, grouped);
376
+ if (outcome.status !== "suspended") {
377
+ await replayToolResult(ctx, nestedOutcomeToolResult(outcome, entry.toolCallId, toolCall.name));
378
+ resolvedToolCallIds.add(entry.toolCallId);
379
+ continue;
380
+ }
381
+ const applied = await applyNestedRun(session, { ref, toolCall, path: entry.path, pending: outcome.pendingDecisions, hook });
382
+ if ("toolResult" in applied) {
383
+ await replayToolResult(ctx, applied.toolResult);
384
+ resolvedToolCallIds.add(entry.toolCallId);
385
+ }
386
+ else {
387
+ remainingNested.push(applied.entry);
388
+ surfacedPending.push(...applied.pending);
389
+ }
390
+ }
391
+ resumePendingCalls = resumePendingCalls
392
+ ?.filter((entry) => !resolvedToolCallIds.has(entry.call.id))
393
+ .map((entry) => {
394
+ const decision = resumed.decisions?.get(entry.approvalId);
395
+ return decision && !entry.decision ? { ...entry, decision } : entry;
396
+ });
397
+ if (session.activeDurable?.state) {
398
+ session.activeDurable.state = {
399
+ ...session.activeDurable.state,
400
+ pendingCalls: resumePendingCalls?.length ? resumePendingCalls : undefined,
401
+ nestedRuns: remainingNested.length ? remainingNested : undefined,
402
+ };
403
+ }
404
+ const remainingOwn = oldPending.filter((pending) => !nestedApprovalIds.has(pending.approvalId) &&
405
+ !resumed.decisions?.has(pending.approvalId) &&
406
+ !resumePendingCalls?.some((entry) => entry.approvalId === pending.approvalId && entry.decision !== undefined));
407
+ if (remainingOwn.length > 0 || surfacedPending.length > 0) {
408
+ const pendingDecisions = [...remainingOwn, ...surfacedPending];
409
+ const single = pendingDecisions.length === 1 ? pendingDecisions[0] : undefined;
410
+ const interruption = {
411
+ kind: single?.kind ?? "tool_approval",
412
+ reason: `${pendingDecisions.length} approval request(s) remain`,
413
+ ...(single?.toolCallId ? { toolCallId: single.toolCallId } : {}),
414
+ ...(single?.scope.toolName ? { toolName: single.scope.toolName } : {}),
415
+ pendingDecisions,
416
+ };
417
+ throw new AgentRunSuspended(await suspendDurable(session, {
418
+ runId: ctx.runId,
419
+ model: ctx.model,
420
+ limits: ctx.limits,
421
+ interruption,
422
+ pendingCalls: resumePendingCalls,
423
+ nestedRuns: remainingNested,
424
+ }), interruption);
425
+ }
426
+ }
427
+ if (resumePendingCalls?.length) {
428
+ for (const entry of resumePendingCalls) {
429
+ if (entry.status !== "ready")
430
+ continue;
431
+ const decision = entry.decision ?? resumed?.decisions?.get(entry.approvalId);
432
+ if (decision && (decision.outcome === "reject_once" || decision.outcome === "reject_for_run")) {
433
+ await replayToolResult(ctx, {
434
+ toolCallId: entry.call.id,
435
+ name: entry.call.name,
436
+ error: { code: "approval_rejected", message: decision.reason ?? "Approval rejected" },
437
+ });
438
+ continue;
439
+ }
440
+ if (decision?.elicitation !== undefined) {
441
+ await replayToolResult(ctx, { toolCallId: entry.call.id, name: entry.call.name, value: decision.elicitation });
442
+ continue;
443
+ }
444
+ const call = decision?.modifiedArguments ? { ...entry.call, arguments: decision.modifiedArguments } : entry.call;
445
+ try {
446
+ await replayToolResult(ctx, await ctx.loopCtx.dispatchToolCall(call));
447
+ }
448
+ catch (error) {
449
+ if (!(error instanceof AgentDelegationSuspendedError))
450
+ throw error;
451
+ await handleNestedSignal(ctx, error);
452
+ }
453
+ }
454
+ }
455
+ else if (resumed?.state?.pending?.status === "ready") {
456
+ await replayToolResult(ctx, await ctx.loopCtx.dispatchToolCall(resumed.state.pending.call));
457
+ }
458
+ }
459
+ export async function runLoopUntilSettled(ctx) {
460
+ while (true) {
461
+ try {
462
+ const loopUsage = await ctx.loop.run(ctx.loopCtx);
463
+ await suspendGatedRound(ctx);
464
+ return loopUsage;
465
+ }
466
+ catch (error) {
467
+ if (!(error instanceof AgentDelegationSuspendedError))
468
+ throw error;
469
+ await handleNestedSignal(ctx, error);
470
+ }
471
+ }
472
+ }
473
+ //# sourceMappingURL=tool-round.js.map
@@ -0,0 +1,115 @@
1
+ /** Shared host/round types for runInternal phase split (plan 059). Internal only. */
2
+ import type { ActiveDurableRun } from "../../agent-approval.js";
3
+ import type { PendingToolCall } from "../../agent-run-state.js";
4
+ import type { Agent, AgentEvent, AgentLoopStrategy, AgentRunResult, AIProvider, ErrorInfo, Guardrails, LoopContext, Message, ModelConfig, OwnershipScope, PendingDecision, PromptVersionRef, ProviderRequest, RunLedger, RunOptions, SessionEntry, SessionStore, Skill, ToolDefinition, ToolEffectStore, ToolRegistry, Usage } from "../../contracts.js";
5
+ import type { AgentIdentity } from "../../identity.js";
6
+ import type { AgentInput } from "../../input.js";
7
+ import type { SecretRedactor } from "../../redaction.js";
8
+ import type { RunLimitTracker } from "../../run-limits.js";
9
+ import type { SessionContextSnapshot } from "../../session-stores.js";
10
+ import type { LoadedSkillSet } from "../../skill-disclosure.js";
11
+ import type { LoadedSkillBodiesEntry } from "../../skill-load.js";
12
+ import type { ActiveToolSet } from "../../tool-search.js";
13
+ /** Live session bag phases mutate. Cast from RuntimeAgentSession (private fields). */
14
+ export type SessionHost = {
15
+ readonly id: string;
16
+ readonly agent: Agent;
17
+ readonly metadata?: Readonly<Record<string, unknown>>;
18
+ readonly store: SessionStore;
19
+ currentLeafId?: string;
20
+ history: Message[];
21
+ activeRun?: AbortController;
22
+ activeRunId?: string;
23
+ activeProviderTurnAbort?: AbortController;
24
+ pendingSoftInterrupt: boolean;
25
+ pendingSteers: Message[];
26
+ pendingSteerBytes: number;
27
+ activeRedactor?: SecretRedactor;
28
+ activeProvider?: AIProvider;
29
+ activeLedger?: RunLedger;
30
+ activeEffectStore?: ToolEffectStore;
31
+ activeOwnership?: OwnershipScope;
32
+ activeIdentity?: AgentIdentity;
33
+ activeIdempotencyKey?: string;
34
+ activeGuardrails?: Guardrails;
35
+ activeMetadata?: Readonly<Record<string, unknown>>;
36
+ activePromptVersion?: PromptVersionRef;
37
+ activeLimits?: RunLimitTracker;
38
+ activeLimitOutputBuffer: boolean;
39
+ activeDurable?: ActiveDurableRun;
40
+ activeLoop?: AgentLoopStrategy;
41
+ activeGatedRound?: Map<string, {
42
+ entry: PendingToolCall;
43
+ decision: PendingDecision;
44
+ }>;
45
+ activeLoopTurn: number;
46
+ readonly loadedSkills: LoadedSkillSet;
47
+ readonly activatedTools: ActiveToolSet;
48
+ restoredSkillBodies: readonly LoadedSkillBodiesEntry[];
49
+ activeRunSkills: readonly Skill[];
50
+ invalidateSnapshot(): void;
51
+ resolveRunProvider(options: RunOptions): void;
52
+ emit(event: AgentEvent): void;
53
+ rebuildHistory(): Promise<void>;
54
+ resolveRunSkills(options: RunOptions, tools: readonly ToolDefinition[]): readonly Skill[];
55
+ appendEntry(entry: SessionEntry): Promise<void>;
56
+ redact<T>(value: T): T;
57
+ appendMessage(message: Message, runId: string): Promise<void>;
58
+ autoCompact(runId: string, options: RunOptions, signal: AbortSignal, inputMessages: readonly Message[]): Promise<void>;
59
+ applyPendingSteers(runId: string, metadata: Readonly<Record<string, unknown>>, signal: AbortSignal): Promise<boolean>;
60
+ applyProviderRequestPolicies(request: ProviderRequest, runId: string, options: RunOptions, metadata: Readonly<Record<string, unknown>>, signal: AbortSignal): Promise<{
61
+ request: ProviderRequest;
62
+ secrets: readonly (string | undefined)[];
63
+ }>;
64
+ redactProviderRequest(request: ProviderRequest): ProviderRequest;
65
+ drainLedger(): Promise<void>;
66
+ buildRunResult(input: {
67
+ readonly runId: string;
68
+ readonly status: AgentRunResult["status"];
69
+ readonly usage?: Usage;
70
+ readonly limit?: import("../../contracts.js").RunLimitBreach;
71
+ readonly error?: ErrorInfo;
72
+ readonly abortReason?: string;
73
+ readonly runState?: import("../../contracts.js").AgentRunState;
74
+ readonly interruption?: import("../../contracts.js").AgentRunInterruption;
75
+ }): AgentRunResult;
76
+ closeSubscribers(): void;
77
+ snapshot(): Promise<SessionContextSnapshot>;
78
+ };
79
+ export declare function asSessionHost(session: unknown): SessionHost;
80
+ export type RoundContext = {
81
+ session: SessionHost;
82
+ input: AgentInput;
83
+ options: RunOptions;
84
+ runId: string;
85
+ resumed: ActiveDurableRun | undefined;
86
+ controller: AbortController;
87
+ model: ModelConfig;
88
+ metadata: Readonly<Record<string, unknown>>;
89
+ limits: RunLimitTracker;
90
+ registry: ToolRegistry;
91
+ tools: readonly ToolDefinition[];
92
+ activeSkills: readonly Skill[];
93
+ inputMessages: Message[];
94
+ maxToolRounds: number | undefined;
95
+ systemInstructions: string | undefined;
96
+ contextProviders: NonNullable<Agent["config"]["context"]>;
97
+ providerOptions: import("../../contracts.js").ProviderRequestOptions | undefined;
98
+ validate: RunOptions["validate"];
99
+ instructionInjectors: NonNullable<Agent["config"]["instructionInjectors"]>;
100
+ inputLayout: RunOptions["inputLayout"];
101
+ loop: AgentLoopStrategy;
102
+ toolConcurrency: number;
103
+ toolsDisclosure: import("../../tool-search.js").ToolsDisclosure;
104
+ assembledTurn: boolean;
105
+ artifactFinished: boolean;
106
+ artifactFailedInfo: {
107
+ message: string;
108
+ code?: string | number;
109
+ } | undefined;
110
+ runUsage: {
111
+ add(usage: Usage): void;
112
+ value(): Usage | undefined;
113
+ };
114
+ loopCtx: LoopContext;
115
+ };
@@ -0,0 +1,5 @@
1
+ /** Shared host/round types for runInternal phase split (plan 059). Internal only. */
2
+ export function asSessionHost(session) {
3
+ return session;
4
+ }
5
+ //# sourceMappingURL=types.js.map