@jini-ai/agent-runtime 0.3.10 → 0.4.1

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 (200) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/README.md +34 -17
  3. package/dist/acp-model-probe.d.ts +6 -4
  4. package/dist/acp-model-probe.js +3 -3
  5. package/dist/agent-protocol/acp/account-failure.d.ts +4 -2
  6. package/dist/agent-protocol/acp/account-failure.js +1 -1
  7. package/dist/agent-protocol/acp/json.d.ts +6 -6
  8. package/dist/agent-protocol/acp/json.js +3 -3
  9. package/dist/agent-protocol/acp/models.d.ts +10 -5
  10. package/dist/agent-protocol/acp/models.js +32 -31
  11. package/dist/agent-protocol/acp/session-params.d.ts +3 -1
  12. package/dist/agent-protocol/acp/session-params.js +2 -1
  13. package/dist/agent-protocol/acp/session.d.ts +28 -22
  14. package/dist/agent-protocol/acp/session.js +181 -147
  15. package/dist/agent-protocol/acp/text-suppression.d.ts +1 -1
  16. package/dist/agent-protocol/acp/text-suppression.js +1 -1
  17. package/dist/agent-protocol/acp/types.d.ts +3 -2
  18. package/dist/agent-protocol/acp/updates.d.ts +11 -11
  19. package/dist/agent-protocol/acp/updates.js +2 -2
  20. package/dist/agent-protocol/core/json-line-stream.d.ts +24 -8
  21. package/dist/agent-protocol/core/json-line-stream.js +8 -8
  22. package/dist/agent-protocol/pi-rpc/events.d.ts +5 -1
  23. package/dist/agent-protocol/pi-rpc/events.js +36 -36
  24. package/dist/agent-protocol/pi-rpc/internal.d.ts +4 -1
  25. package/dist/agent-protocol/pi-rpc/models.d.ts +3 -1
  26. package/dist/agent-protocol/pi-rpc/models.js +1 -1
  27. package/dist/agent-protocol/pi-rpc/session.d.ts +1 -1
  28. package/dist/agent-protocol/pi-rpc/session.js +33 -32
  29. package/dist/amr-model-cache.d.ts +12 -4
  30. package/dist/amr-model-cache.js +17 -21
  31. package/dist/amr-profile-resolver.d.ts +3 -1
  32. package/dist/anthropic-live-models.js +0 -0
  33. package/dist/artifact-taxonomy.d.ts +6 -2
  34. package/dist/auth.d.ts +52 -15
  35. package/dist/auth.js +36 -36
  36. package/dist/claude-code-models.js +2 -2
  37. package/dist/claude-stream.d.ts +22 -3
  38. package/dist/claude-stream.js +24 -8
  39. package/dist/copilot-stream.d.ts +7 -3
  40. package/dist/copilot-stream.js +3 -3
  41. package/dist/defs/aider.d.ts +8 -1
  42. package/dist/defs/aider.js +2 -2
  43. package/dist/defs/amp.d.ts +8 -1
  44. package/dist/defs/amp.js +2 -2
  45. package/dist/defs/amr.d.ts +22 -6
  46. package/dist/defs/amr.js +23 -23
  47. package/dist/defs/antigravity.d.ts +15 -4
  48. package/dist/defs/antigravity.js +9 -8
  49. package/dist/defs/auggie.d.ts +4 -1
  50. package/dist/defs/auggie.js +1 -1
  51. package/dist/defs/claude.d.ts +12 -2
  52. package/dist/defs/claude.js +5 -5
  53. package/dist/defs/cline.d.ts +4 -1
  54. package/dist/defs/cline.js +1 -1
  55. package/dist/defs/codebuddy.d.ts +8 -1
  56. package/dist/defs/codebuddy.js +2 -2
  57. package/dist/defs/codex.d.ts +18 -4
  58. package/dist/defs/codex.js +7 -7
  59. package/dist/defs/copilot.d.ts +8 -1
  60. package/dist/defs/copilot.js +2 -2
  61. package/dist/defs/crush.d.ts +8 -1
  62. package/dist/defs/crush.js +1 -1
  63. package/dist/defs/cursor-agent.d.ts +14 -3
  64. package/dist/defs/cursor-agent.js +5 -5
  65. package/dist/defs/deepseek.d.ts +8 -1
  66. package/dist/defs/deepseek.js +2 -2
  67. package/dist/defs/devin.d.ts +12 -2
  68. package/dist/defs/devin.js +3 -3
  69. package/dist/defs/droid.d.ts +8 -1
  70. package/dist/defs/droid.js +1 -1
  71. package/dist/defs/gemini.d.ts +8 -1
  72. package/dist/defs/gemini.js +1 -1
  73. package/dist/defs/goose.d.ts +4 -1
  74. package/dist/defs/goose.js +1 -1
  75. package/dist/defs/grok-build.d.ts +11 -2
  76. package/dist/defs/grok-build.js +3 -3
  77. package/dist/defs/hermes.d.ts +4 -1
  78. package/dist/defs/hermes.js +2 -2
  79. package/dist/defs/kilo.d.ts +4 -1
  80. package/dist/defs/kilo.js +2 -2
  81. package/dist/defs/kimi.d.ts +4 -1
  82. package/dist/defs/kimi.js +2 -2
  83. package/dist/defs/kiro.d.ts +4 -1
  84. package/dist/defs/kiro.js +2 -2
  85. package/dist/defs/mimo.d.ts +8 -1
  86. package/dist/defs/mimo.js +2 -2
  87. package/dist/defs/opencode.d.ts +9 -2
  88. package/dist/defs/opencode.js +2 -2
  89. package/dist/defs/pi.d.ts +12 -2
  90. package/dist/defs/pi.js +9 -9
  91. package/dist/defs/qoder.d.ts +8 -1
  92. package/dist/defs/qoder.js +2 -2
  93. package/dist/defs/qwen.d.ts +8 -1
  94. package/dist/defs/qwen.js +2 -2
  95. package/dist/defs/reasonix.d.ts +4 -1
  96. package/dist/defs/reasonix.js +3 -3
  97. package/dist/defs/shared.d.ts +4 -2
  98. package/dist/defs/shared.js +2 -2
  99. package/dist/defs/trae-cli.d.ts +12 -2
  100. package/dist/defs/trae-cli.js +3 -3
  101. package/dist/defs/vibe.d.ts +4 -1
  102. package/dist/defs/vibe.js +2 -2
  103. package/dist/detection.d.ts +19 -4
  104. package/dist/detection.js +33 -33
  105. package/dist/diagnostics.d.ts +14 -3
  106. package/dist/diagnostics.js +5 -5
  107. package/dist/env.d.ts +16 -4
  108. package/dist/env.js +7 -7
  109. package/dist/executables.d.ts +24 -8
  110. package/dist/executables.js +13 -13
  111. package/dist/index.d.ts +228 -4
  112. package/dist/index.js +230 -6
  113. package/dist/invocation.d.ts +6 -1
  114. package/dist/invocation.js +2 -2
  115. package/dist/json-event-stream.d.ts +7 -2
  116. package/dist/json-event-stream.js +3 -3
  117. package/dist/launch.d.ts +12 -2
  118. package/dist/launch.js +3 -3
  119. package/dist/log-file.d.ts +4 -1
  120. package/dist/log-file.js +2 -2
  121. package/dist/mcp.d.ts +6 -3
  122. package/dist/mcp.js +1 -1
  123. package/dist/metadata.d.ts +7 -3
  124. package/dist/metadata.js +1 -1
  125. package/dist/mmd-routes.d.ts +22 -6
  126. package/dist/mmd-routes.js +14 -14
  127. package/dist/model-catalog-cache.d.ts +50 -0
  128. package/dist/model-catalog-cache.js +61 -0
  129. package/dist/model-loading-state.d.ts +10 -0
  130. package/dist/model-loading-state.js +23 -0
  131. package/dist/model-registry.d.ts +28 -7
  132. package/dist/model-registry.js +11 -8
  133. package/dist/models.d.ts +31 -6
  134. package/dist/models.js +6 -6
  135. package/dist/opencode-log.d.ts +12 -4
  136. package/dist/opencode-log.js +8 -8
  137. package/dist/paths.d.ts +6 -2
  138. package/dist/paths.js +3 -3
  139. package/dist/pi-models.d.ts +3 -1
  140. package/dist/pi-models.js +2 -2
  141. package/dist/prompt-budget.d.ts +17 -4
  142. package/dist/prompt-budget.js +3 -3
  143. package/dist/prompt-file.d.ts +5 -1
  144. package/dist/prompt-file.js +1 -1
  145. package/dist/providers/aihubmix.d.ts +34 -9
  146. package/dist/providers/aihubmix.js +26 -26
  147. package/dist/providers/anthropic-messages.d.ts +77 -20
  148. package/dist/providers/anthropic-messages.js +53 -50
  149. package/dist/providers/azure-chat.d.ts +21 -7
  150. package/dist/providers/azure-chat.js +18 -16
  151. package/dist/providers/connection-guard.d.ts +45 -31
  152. package/dist/providers/connection-guard.js +40 -143
  153. package/dist/providers/connection-test.d.ts +1 -1
  154. package/dist/providers/connection-test.js +13 -30
  155. package/dist/providers/elevenlabs.d.ts +7 -2
  156. package/dist/providers/elevenlabs.js +4 -4
  157. package/dist/providers/google-messages.d.ts +44 -10
  158. package/dist/providers/google-messages.js +36 -34
  159. package/dist/providers/google-schema.d.ts +26 -0
  160. package/dist/providers/google-schema.js +253 -0
  161. package/dist/providers/google.d.ts +21 -6
  162. package/dist/providers/google.js +10 -10
  163. package/dist/providers/index.d.ts +3 -2
  164. package/dist/providers/index.js +3 -2
  165. package/dist/providers/model-catalog.d.ts +1 -1
  166. package/dist/providers/model-catalog.js +14 -27
  167. package/dist/providers/oauth-callback-server.d.ts +1 -1
  168. package/dist/providers/oauth-callback-server.js +3 -2
  169. package/dist/providers/oauth-credentials.d.ts +7 -1
  170. package/dist/providers/oauth-credentials.js +5 -5
  171. package/dist/providers/oauth-provider.d.ts +2 -2
  172. package/dist/providers/oauth-provider.js +20 -25
  173. package/dist/providers/oauth-tokens.d.ts +26 -6
  174. package/dist/providers/oauth-tokens.js +9 -9
  175. package/dist/providers/ollama-chat.d.ts +66 -31
  176. package/dist/providers/ollama-chat.js +64 -61
  177. package/dist/providers/openai-chat.d.ts +77 -22
  178. package/dist/providers/openai-chat.js +71 -67
  179. package/dist/providers/pkce.d.ts +33 -20
  180. package/dist/providers/pkce.js +75 -146
  181. package/dist/providers/sse-decode.d.ts +3 -1
  182. package/dist/providers/sse-decode.js +1 -1
  183. package/dist/providers/token-params.d.ts +16 -5
  184. package/dist/providers/token-params.js +6 -6
  185. package/dist/providers/tool-turn-types.d.ts +94 -0
  186. package/dist/providers/tool-turn-types.js +2 -0
  187. package/dist/providers/tool-turn.d.ts +10 -0
  188. package/dist/providers/tool-turn.js +258 -0
  189. package/dist/qoder-stream.d.ts +7 -3
  190. package/dist/qoder-stream.js +3 -3
  191. package/dist/registry.d.ts +6 -2
  192. package/dist/registry.js +4 -4
  193. package/dist/resolution.d.ts +5 -1
  194. package/dist/resolution.js +3 -3
  195. package/dist/role-marker-guard.d.ts +9 -5
  196. package/dist/role-marker-guard.js +5 -5
  197. package/dist/terminal-launch.d.ts +6 -1
  198. package/dist/terminal-launch.js +2 -2
  199. package/dist/types.d.ts +22 -6
  200. package/package.json +43 -4
@@ -1,6 +1,6 @@
1
1
  import type { ExecutionProfile } from './types.js';
2
2
  import { type ArtifactTextSuppressor } from './text-suppression.js';
3
- import type { JsonRpcId, JsonObject, AcpChildProcess } from './types.js';
3
+ import type { JsonRpcId, UnknownRecord, AcpChildProcess } from './types.js';
4
4
  import { type AcpMcpServerInput } from './session-params.js';
5
5
  import { type AccountFailureClassifier } from './account-failure.js';
6
6
  /** One option offered by an ACP agent for a pending tool permission request. */
@@ -19,10 +19,10 @@ export interface AcpPermissionOption {
19
19
  export interface AcpPermissionRequest {
20
20
  readonly requestId: JsonRpcId;
21
21
  readonly sessionId: string | null;
22
- readonly toolCall: JsonObject | null;
22
+ readonly toolCall: UnknownRecord | null;
23
23
  readonly options: readonly AcpPermissionOption[];
24
24
  /** Original ACP request parameters for forward-compatible audit storage. */
25
- readonly rawParams: JsonObject;
25
+ readonly rawParams: UnknownRecord;
26
26
  }
27
27
  /** A host's response to an {@link AcpPermissionRequest}. */
28
28
  export type AcpPermissionDecision = {
@@ -55,7 +55,10 @@ export interface AttachAcpSessionOptions {
55
55
  imagePaths?: string[];
56
56
  mcpServers?: AcpMcpServerInput[];
57
57
  envFormat?: 'array' | 'map';
58
- send: (event: string, payload: unknown) => void;
58
+ send: (requiredArgs: {
59
+ event: string;
60
+ payload: unknown;
61
+ }) => void;
59
62
  /**
60
63
  * Receives every native ACP tool-permission request before the agent can
61
64
  * execute it. Omit it only when the host intends the session to fail closed
@@ -113,7 +116,10 @@ export declare function createAcpSessionState(): AcpSessionState;
113
116
  * construct a fake one with `vi.fn()` stubs for the callbacks.
114
117
  */
115
118
  export interface AcpSessionEffects {
116
- send: (event: string, payload: unknown) => void;
119
+ send: (requiredArgs: {
120
+ event: string;
121
+ payload: unknown;
122
+ }) => void;
117
123
  fail: (message: string, options?: {
118
124
  forceModelUnavailable?: boolean;
119
125
  details?: unknown;
@@ -127,7 +133,7 @@ export interface AcpSessionEffects {
127
133
  emitVisibleTextDelta: (delta: string) => void;
128
134
  noteArtifactTextSuppression: (reason: string) => void;
129
135
  noteToolCallTextSuppression: (reason: string) => void;
130
- emitAcpRawShapeDiagnostic: (update: JsonObject) => void;
136
+ emitAcpRawShapeDiagnostic: (update: UnknownRecord) => void;
131
137
  toolCallTextSuppressor: ArtifactTextSuppressor;
132
138
  runStartedAt: number;
133
139
  modelUnavailableErrorCode?: 'AMR_MODEL_UNAVAILABLE' | undefined;
@@ -149,20 +155,20 @@ export interface AcpSessionEffects {
149
155
  export declare function handleRpcError({ state, effects, obj, error, rpcErr, }: {
150
156
  state: AcpSessionState;
151
157
  effects: AcpSessionEffects;
152
- obj: JsonObject;
153
- error: JsonObject | null;
158
+ obj: UnknownRecord;
159
+ error: UnknownRecord | null;
154
160
  rpcErr: string;
155
161
  }): void;
156
162
  /** Promotes an AMR retry-status `session/update` into a structured account-failure error, if the classifier matches. Returns `true` when it did (and thus failed the session). */
157
163
  export declare function tryPromoteAmrRetryStatus({ effects, update, }: {
158
164
  effects: AcpSessionEffects;
159
- update: JsonObject;
165
+ update: UnknownRecord;
160
166
  }): boolean;
161
167
  /** Translates an `agent_thought_chunk` update into `thinking_start` (once) / `thinking_delta` events. */
162
168
  export declare function handleThoughtChunkUpdate({ state, effects, update, }: {
163
169
  state: AcpSessionState;
164
170
  effects: AcpSessionEffects;
165
- update: JsonObject;
171
+ update: UnknownRecord;
166
172
  }): void;
167
173
  /** Strips tool-call XML from a raw message delta via the session's tool-call text suppressor, noting the suppression reason. */
168
174
  export declare function stripToolCallText({ effects, delta }: {
@@ -208,31 +214,31 @@ export declare function applyDsmlArtifactSuppression({ state, effects, suppresso
208
214
  export declare function handleMessageChunkUpdate({ state, effects, update, }: {
209
215
  state: AcpSessionState;
210
216
  effects: AcpSessionEffects;
211
- update: JsonObject;
217
+ update: UnknownRecord;
212
218
  }): void;
213
219
  export declare function mirrorArtifactWriteToolEvent({ state, effects, update, toolCallId, }: {
214
220
  state: AcpSessionState;
215
221
  effects: AcpSessionEffects;
216
- update: JsonObject;
222
+ update: UnknownRecord;
217
223
  toolCallId: string | null;
218
224
  }): void;
219
225
  /** Arms the DSML artifact suppressor when a tool_call/tool_call_update completes an artifact write, or disarms it once its owning write call terminally fails. */
220
226
  export declare function updateDsmlSuppressorForToolCall({ state, update, toolCallId, }: {
221
227
  state: AcpSessionState;
222
- update: JsonObject;
228
+ update: UnknownRecord;
223
229
  toolCallId: string | null;
224
230
  }): void;
225
231
  /** Translates a `tool_call` / `tool_call_update` update: tracks it as real turn output, mirrors artifact writes, and manages DSML suppressor arming. */
226
232
  export declare function handleToolCallUpdate({ state, effects, update, }: {
227
233
  state: AcpSessionState;
228
234
  effects: AcpSessionEffects;
229
- update: JsonObject;
235
+ update: UnknownRecord;
230
236
  }): void;
231
237
  /** Top-level `session/update` dispatcher: emits a generic status for non-streaming update kinds, then routes to the matching per-kind handler. */
232
238
  export declare function handleSessionUpdate({ state, effects, update, }: {
233
239
  state: AcpSessionState;
234
240
  effects: AcpSessionEffects;
235
- update: JsonObject;
241
+ update: UnknownRecord;
236
242
  }): void;
237
243
  /** Handles the `initialize` ack: sends `session/load` (resume) or `session/new` (fresh). */
238
244
  export declare function handleInitializeAck({ state, effects }: {
@@ -248,33 +254,33 @@ export declare function triggerSetModelIfNeeded({ state, effects, }: {
248
254
  export declare function handleSessionNewAck({ state, effects, result, rawLine, }: {
249
255
  state: AcpSessionState;
250
256
  effects: AcpSessionEffects;
251
- result: JsonObject;
257
+ result: UnknownRecord;
252
258
  rawLine: string;
253
259
  }): void;
254
260
  /** Handles the `session/prompt` ack: fails a turn that reported model activity but produced no visible output, otherwise flushes and finishes cleanly. */
255
261
  export declare function handlePromptResult({ state, effects, result, }: {
256
262
  state: AcpSessionState;
257
263
  effects: AcpSessionEffects;
258
- result: JsonObject;
264
+ result: UnknownRecord;
259
265
  }): void;
260
266
  /** Whether an incoming result frame is the ack for an in-flight `session/set_model` (or `set_config_option`) request. */
261
267
  export declare function isModelSetAckExpected({ state, effects, obj, }: {
262
268
  state: AcpSessionState;
263
269
  effects: AcpSessionEffects;
264
- obj: JsonObject;
270
+ obj: UnknownRecord;
265
271
  }): boolean;
266
272
  /** Handles the `session/set_model` (or `set_config_option`) ack: records the confirmed model and sends the prompt. */
267
273
  export declare function handleModelSetAck({ state, effects, result, }: {
268
274
  state: AcpSessionState;
269
275
  effects: AcpSessionEffects;
270
- result: JsonObject;
276
+ result: UnknownRecord;
271
277
  }): void;
272
278
  /** Top-level JSON-RPC result dispatcher, keyed by which request `expectedId`/`promptRequestId` is currently outstanding. */
273
279
  export declare function routeResultById({ state, effects, obj, result, rawLine, }: {
274
280
  state: AcpSessionState;
275
281
  effects: AcpSessionEffects;
276
- obj: JsonObject;
277
- result: JsonObject | null;
282
+ obj: UnknownRecord;
283
+ result: UnknownRecord | null;
278
284
  rawLine: string;
279
285
  }): void;
280
286
  /**
@@ -302,5 +308,5 @@ export declare function routeResultById({ state, effects, obj, result, rawLine,
302
308
  * @returns A controller with `hasFatalError`, `getDurableSessionId`,
303
309
  * `completedSuccessfully`, and `abort` methods.
304
310
  */
305
- export declare function attachAcpSession(options: AttachAcpSessionOptions): AcpSessionController;
311
+ export declare function attachAcpSession(requiredArgs: Pick<AttachAcpSessionOptions, "child" | "prompt" | "send">, optionalArgs?: Omit<AttachAcpSessionOptions, "child" | "prompt" | "send">): AcpSessionController;
306
312
  //# sourceMappingURL=session.d.ts.map
@@ -107,9 +107,9 @@ export function handleThoughtChunkUpdate({ state, effects, update, }) {
107
107
  return;
108
108
  if (!state.emittedThinkingStart) {
109
109
  state.emittedThinkingStart = true;
110
- effects.send('agent', { type: 'thinking_start' });
110
+ effects.send({ event: 'agent', payload: { type: 'thinking_start' } });
111
111
  }
112
- effects.send('agent', { type: 'thinking_delta', delta: text });
112
+ effects.send({ event: 'agent', payload: { type: 'thinking_delta', delta: text } });
113
113
  }
114
114
  /** Strips tool-call XML from a raw message delta via the session's tool-call text suppressor, noting the suppression reason. */
115
115
  export function stripToolCallText({ effects, delta }) {
@@ -251,13 +251,13 @@ export function mirrorArtifactWriteToolEvent({ state, effects, update, toolCallI
251
251
  if (!shouldEmit)
252
252
  return;
253
253
  st.emitted = true;
254
- effects.send('agent', {
255
- type: 'tool_use',
256
- id: toolCallId,
257
- name: 'Write',
258
- input: { file_path: st.path ?? toolCallId },
259
- });
260
- effects.send('agent', { type: 'tool_result', toolUseId: toolCallId, isError: failed });
254
+ effects.send({ event: 'agent', payload: {
255
+ type: 'tool_use',
256
+ id: toolCallId,
257
+ name: 'Write',
258
+ input: { file_path: st.path ?? toolCallId },
259
+ } });
260
+ effects.send({ event: 'agent', payload: { type: 'tool_result', toolUseId: toolCallId, isError: failed } });
261
261
  state.emittedConcreteToolEvent = true;
262
262
  }
263
263
  /** Arms the DSML artifact suppressor when a tool_call/tool_call_update completes an artifact write, or disarms it once its owning write call terminally fails. */
@@ -302,11 +302,11 @@ export function handleSessionUpdate({ state, effects, update, }) {
302
302
  if (effects.modelUnavailableErrorCode && tryPromoteAmrRetryStatus({ effects, update }))
303
303
  return;
304
304
  if (update.sessionUpdate !== 'agent_message_chunk' && update.sessionUpdate !== 'agent_thought_chunk') {
305
- effects.send('agent', {
306
- type: 'status',
307
- label: String(update.sessionUpdate || 'session_update'),
308
- elapsedMs: Date.now() - effects.runStartedAt,
309
- });
305
+ effects.send({ event: 'agent', payload: {
306
+ type: 'status',
307
+ label: String(update.sessionUpdate || 'session_update'),
308
+ elapsedMs: Date.now() - effects.runStartedAt,
309
+ } });
310
310
  effects.emitAcpRawShapeDiagnostic(update);
311
311
  }
312
312
  if (update.sessionUpdate === 'agent_thought_chunk') {
@@ -329,7 +329,7 @@ export function handleInitializeAck({ state, effects }) {
329
329
  effects.writeRpc(state.nextId, 'session/load', { sessionId: effects.resumeSessionId, cwd: effects.effectiveCwd }, 'session/load');
330
330
  }
331
331
  else {
332
- effects.writeRpc(state.nextId, 'session/new', buildAcpSessionNewParams(effects.effectiveCwd, effects.mcpServers ? { mcpServers: effects.mcpServers, envFormat: effects.envFormat } : { envFormat: effects.envFormat }), 'session/new');
332
+ effects.writeRpc(state.nextId, 'session/new', buildAcpSessionNewParams({ cwd: effects.effectiveCwd }, effects.mcpServers ? { mcpServers: effects.mcpServers, envFormat: effects.envFormat } : { envFormat: effects.envFormat }), 'session/new');
333
333
  }
334
334
  state.nextId += 1;
335
335
  }
@@ -358,7 +358,7 @@ export function handleSessionNewAck({ state, effects, result, rawLine, }) {
358
358
  state.modelConfigId = resolveModelConfigId(findModelConfigOption(result.configOptions));
359
359
  state.activeModel = currentModelFromSessionResult(result);
360
360
  if (state.sessionId && state.activeModel) {
361
- effects.send('agent', { type: 'status', label: 'model', model: state.activeModel });
361
+ effects.send({ event: 'agent', payload: { type: 'status', label: 'model', model: state.activeModel } });
362
362
  }
363
363
  if (triggerSetModelIfNeeded({ state, effects }))
364
364
  return;
@@ -403,7 +403,7 @@ export function handleModelSetAck({ state, effects, result, }) {
403
403
  // across the function boundary for the type checker (it doesn't survive
404
404
  // the call), not a behavior change.
405
405
  state.activeModel = currentModelFromSessionResult(result) ?? effects.model;
406
- effects.send('agent', { type: 'status', label: 'model', model: state.activeModel });
406
+ effects.send({ event: 'agent', payload: { type: 'status', label: 'model', model: state.activeModel } });
407
407
  effects.sendPrompt();
408
408
  }
409
409
  /** Top-level JSON-RPC result dispatcher, keyed by which request `expectedId`/`promptRequestId` is currently outstanding. */
@@ -462,13 +462,19 @@ function resolveAcpSessionOptionDefaults(options) {
462
462
  * @returns A controller with `hasFatalError`, `getDurableSessionId`,
463
463
  * `completedSuccessfully`, and `abort` methods.
464
464
  */
465
- export function attachAcpSession(options) {
465
+ export function attachAcpSession(requiredArgs, optionalArgs = {}) {
466
+ const options = { ...optionalArgs, ...requiredArgs };
466
467
  const { child, prompt, cwd, model, mcpServers, send, modelUnavailableErrorCode, resumeSessionId, onPermissionRequest, onCliReady, onSessionInit, } = options;
467
468
  const { imagePaths, envFormat, clientName, clientVersion, stageTimeoutMs, executionProfile, accountFailureClassifier, } = resolveAcpSessionOptionDefaults(options);
468
469
  const runStartedAt = Date.now();
469
470
  const effectiveCwd = path.resolve(cwd || process.cwd());
470
471
  if (!child.stdin || !child.stdout) {
471
- throw new Error('ACP child process must expose stdin and stdout streams');
472
+ const error = new Error('ACP child process must expose stdin and stdout streams');
473
+ if (resumeSessionId)
474
+ Object.assign(error, {
475
+ details: { kind: 'resume_failed', sessionId: resumeSessionId },
476
+ });
477
+ throw error;
472
478
  }
473
479
  const stdin = child.stdin;
474
480
  const stdout = child.stdout;
@@ -531,6 +537,34 @@ export function attachAcpSession(options) {
531
537
  value.includes('unknown model') ||
532
538
  value.includes('invalid model'));
533
539
  };
540
+ /** Emits a failure, tagging an unacknowledged resume without losing its cause.
541
+ * The session id is only set by a valid load acknowledgement, so the same
542
+ * boundary covers initialize/load RPC errors, watchdogs, and transport loss.
543
+ * Fresh sessions and errors after load keep their existing payloads.
544
+ * @complexity O(1), excluding the host's event sink.
545
+ */
546
+ const sendFailure = ({ payload }) => {
547
+ if (!resumeSessionId || state.sessionId) {
548
+ send({ event: 'error', payload });
549
+ return;
550
+ }
551
+ const original = asObject(payload) ?? {};
552
+ const originalError = asObject(original.error);
553
+ send({ event: 'error', payload: {
554
+ ...original,
555
+ error: {
556
+ code: 'AGENT_EXECUTION_FAILED',
557
+ message: original.message,
558
+ retryable: false,
559
+ ...originalError,
560
+ details: {
561
+ kind: 'resume_failed',
562
+ sessionId: resumeSessionId,
563
+ ...(originalError?.details === undefined ? {} : { cause: originalError.details }),
564
+ },
565
+ },
566
+ } });
567
+ };
534
568
  // Both call sites below (the session/update retry-status promotion and the
535
569
  // stderr promotion) are already nested inside a scope gated by `finished`
536
570
  // at their own entry point (the parser callback's own `if (aborted ||
@@ -540,12 +574,12 @@ export function attachAcpSession(options) {
540
574
  // second time once `finished` is true. The origin file carried its own
541
575
  // `if (finished) return;` re-entry guard here anyway; removed per this
542
576
  // package's coverage-driven dead-branch discipline (verified unreachable
543
- // by tracing every call site — see source-map.md), not suppressed.
577
+ // by tracing every call site — see archived provenance ledger), not suppressed.
544
578
  const failWithPayload = (payload) => {
545
579
  finished = true;
546
580
  fatal = true;
547
581
  clearStageTimer();
548
- send('error', payload);
582
+ sendFailure({ payload });
549
583
  if (!child.killed)
550
584
  child.kill('SIGTERM');
551
585
  };
@@ -557,29 +591,29 @@ export function attachAcpSession(options) {
557
591
  clearStageTimer();
558
592
  const useModelUnavailable = modelUnavailableErrorCode &&
559
593
  (options.forceModelUnavailable || isModelUnavailableError(message));
560
- send('error', useModelUnavailable
561
- ? amrModelUnavailablePayload(message)
562
- : options.details === undefined && options.retryable === undefined
563
- ? { message }
564
- : {
565
- message,
566
- error: {
567
- code: 'AGENT_EXECUTION_FAILED',
594
+ sendFailure({ payload: useModelUnavailable
595
+ ? amrModelUnavailablePayload(message)
596
+ : options.details === undefined && options.retryable === undefined
597
+ ? { message }
598
+ : {
568
599
  message,
569
- retryable: options.retryable ?? false,
570
- // Every real call site that reaches this branch (the
571
- // RPC-error path and the no-visible-output path) always
572
- // supplies `details` whenever it supplies `retryable`, and
573
- // `retryable` is itself derived from the same `details`
574
- // source at the RPC-error call site — so `options.details`
575
- // is never actually undefined here. Spreading it
576
- // unconditionally (rather than conditionally omitting the
577
- // key) is behaviorally identical for every real caller;
578
- // simplified per this package's coverage-driven
579
- // dead-branch discipline. See source-map.md.
580
- details: options.details,
581
- },
582
- });
600
+ error: {
601
+ code: 'AGENT_EXECUTION_FAILED',
602
+ message,
603
+ retryable: options.retryable ?? false,
604
+ // Every real call site that reaches this branch (the
605
+ // RPC-error path and the no-visible-output path) always
606
+ // supplies `details` whenever it supplies `retryable`, and
607
+ // `retryable` is itself derived from the same `details`
608
+ // source at the RPC-error call site — so `options.details`
609
+ // is never actually undefined here. Spreading it
610
+ // unconditionally (rather than conditionally omitting the
611
+ // key) is behaviorally identical for every real caller;
612
+ // simplified per this package's coverage-driven
613
+ // dead-branch discipline. See archived provenance ledger.
614
+ details: options.details,
615
+ },
616
+ } });
583
617
  if (!child.killed)
584
618
  child.kill('SIGTERM');
585
619
  };
@@ -598,13 +632,13 @@ export function attachAcpSession(options) {
598
632
  if (rawAcpShapeDiagnosticCount >= ACP_RAW_EVENT_SHAPE_DIAGNOSTIC_LIMIT)
599
633
  return;
600
634
  rawAcpShapeDiagnosticCount += 1;
601
- send('agent', {
602
- type: 'diagnostic',
603
- name: 'acp_raw_event_shape',
604
- source: 'acp-json-rpc',
605
- elapsedMs: Date.now() - runStartedAt,
606
- shape: acpRawEventShape(update),
607
- });
635
+ send({ event: 'agent', payload: {
636
+ type: 'diagnostic',
637
+ name: 'acp_raw_event_shape',
638
+ source: 'acp-json-rpc',
639
+ elapsedMs: Date.now() - runStartedAt,
640
+ shape: acpRawEventShape(update),
641
+ } });
608
642
  };
609
643
  // All three call sites guard against an empty `delta` before calling this
610
644
  // (the finishCleanPrompt flush, the `if (outputDelta)` branch, and the
@@ -613,18 +647,18 @@ export function attachAcpSession(options) {
613
647
  // origin file's own `if (!delta) return;` guard here can never actually
614
648
  // fire. Removed per this package's coverage-driven dead-branch
615
649
  // discipline (verified unreachable by tracing every call site — see
616
- // source-map.md), not suppressed.
650
+ // archived provenance ledger), not suppressed.
617
651
  const emitVisibleTextDelta = (delta) => {
618
652
  state.emittedVisibleTextChunk = true;
619
653
  if (!emittedFirstTokenStatus) {
620
654
  emittedFirstTokenStatus = true;
621
- send('agent', {
622
- type: 'status',
623
- label: 'streaming',
624
- ttftMs: Date.now() - runStartedAt,
625
- });
655
+ send({ event: 'agent', payload: {
656
+ type: 'status',
657
+ label: 'streaming',
658
+ ttftMs: Date.now() - runStartedAt,
659
+ } });
626
660
  }
627
- send('agent', { type: 'text_delta', delta });
661
+ send({ event: 'agent', payload: { type: 'text_delta', delta } });
628
662
  };
629
663
  const noteArtifactTextSuppression = (reason) => {
630
664
  if (!state.dsmlArtifactSuppressor)
@@ -641,42 +675,42 @@ export function attachAcpSession(options) {
641
675
  if (artifactSuppressionDiagnosticCount >= ACP_RAW_EVENT_SHAPE_DIAGNOSTIC_LIMIT)
642
676
  return;
643
677
  artifactSuppressionDiagnosticCount += 1;
644
- send('agent', {
645
- type: 'diagnostic',
646
- name: 'acp_artifact_text_suppression',
647
- source: 'acp-json-rpc',
648
- elapsedMs: Date.now() - runStartedAt,
649
- reason,
650
- suppressedChars: artifactTextSuppressionSummary.suppressedChars,
651
- suppressedChunks: artifactTextSuppressionSummary.suppressedChunks,
652
- openedBlocks: artifactTextSuppressionSummary.openedBlocks,
653
- closedBlocks: artifactTextSuppressionSummary.closedBlocks,
654
- pendingCandidateChars: stats.pendingCandidateChars,
655
- suppressing: stats.suppressing,
656
- });
657
- };
658
- const emitArtifactTextSuppressionSummary = () => {
659
- if (artifactTextSuppressionSummary.suppressedChars <= 0)
660
- return;
661
- if (executionProfile === 'filesystem') {
662
- send('agent', {
678
+ send({ event: 'agent', payload: {
663
679
  type: 'diagnostic',
664
- name: 'unexpected_text_artifact_in_filesystem_run',
680
+ name: 'acp_artifact_text_suppression',
665
681
  source: 'acp-json-rpc',
666
682
  elapsedMs: Date.now() - runStartedAt,
683
+ reason,
667
684
  suppressedChars: artifactTextSuppressionSummary.suppressedChars,
668
685
  suppressedChunks: artifactTextSuppressionSummary.suppressedChunks,
669
686
  openedBlocks: artifactTextSuppressionSummary.openedBlocks,
670
687
  closedBlocks: artifactTextSuppressionSummary.closedBlocks,
671
- });
688
+ pendingCandidateChars: stats.pendingCandidateChars,
689
+ suppressing: stats.suppressing,
690
+ } });
691
+ };
692
+ const emitArtifactTextSuppressionSummary = () => {
693
+ if (artifactTextSuppressionSummary.suppressedChars <= 0)
694
+ return;
695
+ if (executionProfile === 'filesystem') {
696
+ send({ event: 'agent', payload: {
697
+ type: 'diagnostic',
698
+ name: 'unexpected_text_artifact_in_filesystem_run',
699
+ source: 'acp-json-rpc',
700
+ elapsedMs: Date.now() - runStartedAt,
701
+ suppressedChars: artifactTextSuppressionSummary.suppressedChars,
702
+ suppressedChunks: artifactTextSuppressionSummary.suppressedChunks,
703
+ openedBlocks: artifactTextSuppressionSummary.openedBlocks,
704
+ closedBlocks: artifactTextSuppressionSummary.closedBlocks,
705
+ } });
672
706
  }
673
- send('agent', {
674
- type: 'diagnostic',
675
- name: 'acp_artifact_text_suppression_summary',
676
- source: 'acp-json-rpc',
677
- elapsedMs: Date.now() - runStartedAt,
678
- ...artifactTextSuppressionSummary,
679
- });
707
+ send({ event: 'agent', payload: {
708
+ type: 'diagnostic',
709
+ name: 'acp_artifact_text_suppression_summary',
710
+ source: 'acp-json-rpc',
711
+ elapsedMs: Date.now() - runStartedAt,
712
+ ...artifactTextSuppressionSummary,
713
+ } });
680
714
  };
681
715
  const noteToolCallTextSuppression = (reason) => {
682
716
  const stats = toolCallTextSuppressor.stats();
@@ -691,30 +725,30 @@ export function attachAcpSession(options) {
691
725
  if (artifactSuppressionDiagnosticCount >= ACP_RAW_EVENT_SHAPE_DIAGNOSTIC_LIMIT)
692
726
  return;
693
727
  artifactSuppressionDiagnosticCount += 1;
694
- send('agent', {
695
- type: 'diagnostic',
696
- name: 'acp_tool_call_text_suppression',
697
- source: 'acp-json-rpc',
698
- elapsedMs: Date.now() - runStartedAt,
699
- reason,
700
- suppressedChars: toolCallTextSuppressionSummary.suppressedChars,
701
- suppressedChunks: toolCallTextSuppressionSummary.suppressedChunks,
702
- openedBlocks: toolCallTextSuppressionSummary.openedBlocks,
703
- closedBlocks: toolCallTextSuppressionSummary.closedBlocks,
704
- pendingCandidateChars: stats.pendingCandidateChars,
705
- suppressing: stats.suppressing,
706
- });
728
+ send({ event: 'agent', payload: {
729
+ type: 'diagnostic',
730
+ name: 'acp_tool_call_text_suppression',
731
+ source: 'acp-json-rpc',
732
+ elapsedMs: Date.now() - runStartedAt,
733
+ reason,
734
+ suppressedChars: toolCallTextSuppressionSummary.suppressedChars,
735
+ suppressedChunks: toolCallTextSuppressionSummary.suppressedChunks,
736
+ openedBlocks: toolCallTextSuppressionSummary.openedBlocks,
737
+ closedBlocks: toolCallTextSuppressionSummary.closedBlocks,
738
+ pendingCandidateChars: stats.pendingCandidateChars,
739
+ suppressing: stats.suppressing,
740
+ } });
707
741
  };
708
742
  const emitToolCallTextSuppressionSummary = () => {
709
743
  if (toolCallTextSuppressionSummary.suppressedChars <= 0)
710
744
  return;
711
- send('agent', {
712
- type: 'diagnostic',
713
- name: 'acp_tool_call_text_suppression_summary',
714
- source: 'acp-json-rpc',
715
- elapsedMs: Date.now() - runStartedAt,
716
- ...toolCallTextSuppressionSummary,
717
- });
745
+ send({ event: 'agent', payload: {
746
+ type: 'diagnostic',
747
+ name: 'acp_tool_call_text_suppression_summary',
748
+ source: 'acp-json-rpc',
749
+ elapsedMs: Date.now() - runStartedAt,
750
+ ...toolCallTextSuppressionSummary,
751
+ } });
718
752
  };
719
753
  const sendPrompt = () => {
720
754
  state.promptRequestId = state.nextId;
@@ -723,11 +757,11 @@ export function attachAcpSession(options) {
723
757
  sessionId: state.sessionId,
724
758
  prompt: buildPromptBlocks(prompt, imagePaths),
725
759
  }, 'session/prompt');
726
- send('agent', {
727
- type: 'status',
728
- label: 'waiting_for_first_output',
729
- elapsedMs: Date.now() - runStartedAt,
730
- });
760
+ send({ event: 'agent', payload: {
761
+ type: 'status',
762
+ label: 'waiting_for_first_output',
763
+ elapsedMs: Date.now() - runStartedAt,
764
+ } });
731
765
  state.nextId += 1;
732
766
  };
733
767
  // `finishCleanPrompt` has exactly one call site (`handlePromptResult`,
@@ -736,7 +770,7 @@ export function attachAcpSession(options) {
736
770
  // (aborted || finished) return;` guard at entry — so this function can
737
771
  // never actually be invoked once `finished` is true. The origin file
738
772
  // carried its own re-entry guard here anyway; removed per this package's
739
- // coverage-driven dead-branch discipline. See source-map.md.
773
+ // coverage-driven dead-branch discipline. See archived provenance ledger.
740
774
  const finishCleanPrompt = (usageSource) => {
741
775
  const flushedToolText = toolCallTextSuppressor.flush();
742
776
  noteToolCallTextSuppression('tool_call_xml_flush');
@@ -751,11 +785,11 @@ export function attachAcpSession(options) {
751
785
  emitArtifactTextSuppressionSummary();
752
786
  const usage = formatUsage(usageSource);
753
787
  if (usage) {
754
- send('agent', {
755
- type: 'usage',
756
- usage,
757
- durationMs: Date.now() - runStartedAt,
758
- });
788
+ send({ event: 'agent', payload: {
789
+ type: 'usage',
790
+ usage,
791
+ durationMs: Date.now() - runStartedAt,
792
+ } });
759
793
  }
760
794
  finished = true;
761
795
  clearStageTimer();
@@ -827,7 +861,7 @@ export function attachAcpSession(options) {
827
861
  const recoverFromModelSelectionError = () => {
828
862
  state.setModelRequestId = null;
829
863
  state.activeModel = state.activeModel || 'default';
830
- send('agent', { type: 'status', label: 'model', model: state.activeModel });
864
+ send({ event: 'agent', payload: { type: 'status', label: 'model', model: state.activeModel } });
831
865
  sendPrompt();
832
866
  };
833
867
  const effects = {
@@ -853,36 +887,36 @@ export function attachAcpSession(options) {
853
887
  mcpServers,
854
888
  envFormat,
855
889
  };
856
- const parser = createJsonLineStream((raw, rawLine) => {
857
- if (aborted || finished)
858
- return;
859
- resetStageTimer('response');
860
- const obj = asObject(raw);
861
- if (!obj)
862
- return;
863
- // First well-formed ACP JSON-RPC message = CLI ready. Caller dedupes, so
864
- // re-notifying on later messages is harmless.
865
- onCliReady?.();
866
- const error = asObject(obj.error);
867
- const params = asObject(obj.params);
868
- const result = asObject(obj.result);
869
- const rpcErr = rpcErrorMessage(obj);
870
- if (rpcErr) {
871
- handleRpcError({ state, effects, obj, error, rpcErr });
872
- return;
873
- }
874
- if (obj.method === 'session/request_permission') {
875
- replyPermission(obj);
876
- return;
877
- }
878
- const update = asObject(params?.update);
879
- if (obj.method === 'session/update' && update) {
880
- handleSessionUpdate({ state, effects, update });
881
- return;
882
- }
883
- routeResultById({ state, effects, obj, result, rawLine });
884
- });
885
- stdout.on('data', (chunk) => parser.feed(chunk));
890
+ const parser = createJsonLineStream({ onMessage: ({ message: raw, rawLine }) => {
891
+ if (aborted || finished)
892
+ return;
893
+ resetStageTimer('response');
894
+ const obj = asObject(raw);
895
+ if (!obj)
896
+ return;
897
+ // First well-formed ACP JSON-RPC message = CLI ready. Caller dedupes, so
898
+ // re-notifying on later messages is harmless.
899
+ onCliReady?.();
900
+ const error = asObject(obj.error);
901
+ const params = asObject(obj.params);
902
+ const result = asObject(obj.result);
903
+ const rpcErr = rpcErrorMessage(obj);
904
+ if (rpcErr) {
905
+ handleRpcError({ state, effects, obj, error, rpcErr });
906
+ return;
907
+ }
908
+ if (obj.method === 'session/request_permission') {
909
+ replyPermission(obj);
910
+ return;
911
+ }
912
+ const update = asObject(params?.update);
913
+ if (obj.method === 'session/update' && update) {
914
+ handleSessionUpdate({ state, effects, update });
915
+ return;
916
+ }
917
+ routeResultById({ state, effects, obj, result, rawLine });
918
+ } });
919
+ stdout.on('data', (chunk) => parser.feed({ chunk: chunk }));
886
920
  child.stderr?.setEncoding('utf8');
887
921
  child.stderr?.on('data', (chunk) => {
888
922
  if (!modelUnavailableErrorCode || finished)