@jini-ai/agent-runtime 0.3.9 → 0.4.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 (200) hide show
  1. package/CHANGELOG.md +103 -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 +16 -2
  52. package/dist/defs/claude.js +22 -4
  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 +38 -3
  104. package/dist/detection.js +77 -37
  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 +89 -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 +252 -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 +37 -6
  200. package/package.json +43 -4
@@ -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)
@@ -14,7 +14,7 @@
14
14
  * dead, unreachable code would force either a contrived test or a
15
15
  * coverage-suppression comment to satisfy this package's >=99% bar, and this
16
16
  * repo's coverage-discipline convention calls for refactoring dead branches
17
- * away rather than either of those. See source-map.md.
17
+ * away rather than either of those. See archived provenance ledger.
18
18
  */
19
19
  /** A single streamed text delta event, as consumed by `emitWithTextSuppressor`. */
20
20
  type EventSink = (event: {
@@ -14,7 +14,7 @@
14
14
  * dead, unreachable code would force either a contrived test or a
15
15
  * coverage-suppression comment to satisfy this package's >=99% bar, and this
16
16
  * repo's coverage-discipline convention calls for refactoring dead branches
17
- * away rather than either of those. See source-map.md.
17
+ * away rather than either of those. See archived provenance ledger.
18
18
  */
19
19
  const ARTIFACT_OPEN_RE = /(?:<\s*\|?\s*DSML[\s,]+artifact\b[^>]*>|<\s*artifact\b[^>]*>)/i;
20
20
  const DSML_ARTIFACT_CLOSE_RE = /(?:<\/artifact>|<\/\s*\|?\s*DSML\s*>|<\s*\|?\s*\/\s*DSML\s*\|?\s*>)/i;
@@ -8,7 +8,8 @@ import type { ChildProcess } from 'node:child_process';
8
8
  /** A JSON-RPC 2.0 request or response identifier. May be a numeric integer or a string. */
9
9
  export type JsonRpcId = string | number;
10
10
  /** An arbitrary JSON object with string keys and unknown values. Used as a loose type for parsed RPC frames. */
11
- export type JsonObject = Record<string, unknown>;
11
+ /** ACP records may contain unvalidated provider values; this does not assert JSON safety. */
12
+ export type UnknownRecord = Record<string, unknown>;
12
13
  /** Minimal writable interface required to send JSON-RPC newline-delimited frames to an ACP agent's stdin. */
13
14
  export type RpcWritable = Pick<Writable, 'write' | 'end'>;
14
15
  /** The Node `ChildProcess` handle for a spawned ACP agent subprocess. */
@@ -21,7 +22,7 @@ export type TimerHandle = ReturnType<typeof setTimeout>;
21
22
  * (`'text_artifact'`). Inlined here rather than imported from an external
22
23
  * contracts package — the upstream type is a trivial two-value literal union
23
24
  * with no product-specific branding, so importing a whole package for it
24
- * would be the tail wagging the dog. See source-map.md.
25
+ * would be the tail wagging the dog. See archived provenance ledger.
25
26
  */
26
27
  export type ExecutionProfile = 'filesystem' | 'text_artifact';
27
28
  //# sourceMappingURL=types.d.ts.map
@@ -5,7 +5,7 @@
5
5
  * diagnostics. Depends on acp/types, acp/json, and acp/account-failure;
6
6
  * consumed exclusively by acp/session.ts.
7
7
  */
8
- import type { JsonObject } from './types.js';
8
+ import type { UnknownRecord } from './types.js';
9
9
  import { type AccountFailureClassifier } from './account-failure.js';
10
10
  /**
11
11
  * Produces a shallow diagnostic snapshot of an ACP update object for the
@@ -17,7 +17,7 @@ import { type AccountFailureClassifier } from './account-failure.js';
17
17
  * @param update - A parsed ACP `session/update` params object.
18
18
  * @returns A flat diagnostic object suitable for inclusion in an agent event payload.
19
19
  */
20
- export declare function acpRawEventShape(update: JsonObject): {
20
+ export declare function acpRawEventShape(update: UnknownRecord): {
21
21
  sessionUpdate: string | null;
22
22
  keys: string[];
23
23
  contentKind: string;
@@ -42,25 +42,25 @@ export declare function acpRawEventShape(update: JsonObject): {
42
42
  *
43
43
  * @param update - A parsed ACP `session/update` params object.
44
44
  */
45
- export declare function acpUpdateStatus(update: JsonObject): string;
45
+ export declare function acpUpdateStatus(update: UnknownRecord): string;
46
46
  /**
47
47
  * Returns `true` when the update's `status` field represents a successful
48
48
  * terminal state (`completed`, `complete`, `succeeded`, or `success`).
49
49
  * Used to decide when to emit deferred artifact-write tool events.
50
50
  */
51
- export declare function isAcpCompletedStatus(update: JsonObject): boolean;
51
+ export declare function isAcpCompletedStatus(update: UnknownRecord): boolean;
52
52
  /**
53
53
  * Returns `true` when the update's `status` field represents a terminal
54
54
  * failure state (`failed`, `failure`, `error`, `cancelled`, or `canceled`).
55
55
  * Used to clean up pending write-suppression state and emit failed tool events.
56
56
  */
57
- export declare function isAcpTerminalFailureStatus(update: JsonObject): boolean;
57
+ export declare function isAcpTerminalFailureStatus(update: UnknownRecord): boolean;
58
58
  /**
59
59
  * Returns `true` when the update's status is `'retry'`. Signals that the AMR
60
60
  * agent wants to restart the request; the session promoter maps this to a
61
61
  * structured error payload via `promotedAmrRetryStatusPayload`.
62
62
  */
63
- export declare function isAcpRetryStatus(update: JsonObject): boolean;
63
+ export declare function isAcpRetryStatus(update: UnknownRecord): boolean;
64
64
  /**
65
65
  * Recursively collects all text-bearing leaf values from an ACP update
66
66
  * (strings, numbers, booleans) up to 4 levels deep. Used to produce a flat
@@ -83,7 +83,7 @@ export declare function acpUpdateDiagnosticText(value: unknown, depth?: number):
83
83
  * `noopAccountFailureClassifier`, under which this always returns `null`.
84
84
  * @returns A structured error payload with `message` and `error`, or `null`.
85
85
  */
86
- export declare function promotedAmrRetryStatusPayload(update: JsonObject, classifier?: AccountFailureClassifier): {
86
+ export declare function promotedAmrRetryStatusPayload(update: UnknownRecord, classifier?: AccountFailureClassifier): {
87
87
  message: string;
88
88
  error: {
89
89
  code: string;
@@ -129,7 +129,7 @@ export declare function promotedAmrStderrPayload(chunk: string, classifier?: Acc
129
129
  *
130
130
  * @param update - A parsed ACP `session/update` params object.
131
131
  */
132
- export declare function acpToolCallId(update: JsonObject): string | null;
132
+ export declare function acpToolCallId(update: UnknownRecord): string | null;
133
133
  /**
134
134
  * Returns `true` when the update's `title` or `name` field contains a word
135
135
  * that indicates a file-write operation (`edit`, `write`, `create`, `update`,
@@ -138,7 +138,7 @@ export declare function acpToolCallId(update: JsonObject): string | null;
138
138
  *
139
139
  * @param update - A parsed ACP `session/update` params object.
140
140
  */
141
- export declare function isAcpArtifactWriteLabel(update: JsonObject): boolean;
141
+ export declare function isAcpArtifactWriteLabel(update: UnknownRecord): boolean;
142
142
  /**
143
143
  * Returns `true` when an ACP update represents the terminal completion of an
144
144
  * artifact-write tool call and should arm the DSML text suppressor. Requires
@@ -148,7 +148,7 @@ export declare function isAcpArtifactWriteLabel(update: JsonObject): boolean;
148
148
  * @param update - A parsed ACP `session/update` params object.
149
149
  * @param writeToolCallIds - The set of tool call ids identified as artifact writes so far.
150
150
  */
151
- export declare function isAcpArtifactWriteUpdate(update: JsonObject, writeToolCallIds: Set<string>): boolean;
151
+ export declare function isAcpArtifactWriteUpdate(update: UnknownRecord, writeToolCallIds: Set<string>): boolean;
152
152
  /**
153
153
  * Best-effort extraction of a concrete file path from an ACP artifact-write
154
154
  * tool call update, checking three sources in priority order:
@@ -162,5 +162,5 @@ export declare function isAcpArtifactWriteUpdate(update: JsonObject, writeToolCa
162
162
  * @param update - A parsed ACP `session/update` params object.
163
163
  * @returns An absolute or relative file path string, or `null` when absent.
164
164
  */
165
- export declare function acpArtifactWritePath(update: JsonObject): string | null;
165
+ export declare function acpArtifactWritePath(update: UnknownRecord): string | null;
166
166
  //# sourceMappingURL=updates.d.ts.map
@@ -130,7 +130,7 @@ export function promotedAmrRetryStatusPayload(update, classifier = noopAccountFa
130
130
  if (!isAcpRetryStatus(update))
131
131
  return null;
132
132
  const diagnosticText = acpUpdateDiagnosticText(update).join('\n');
133
- const failure = classifier.classify(diagnosticText);
133
+ const failure = classifier.classify({ text: diagnosticText });
134
134
  if (!failure)
135
135
  return null;
136
136
  return {
@@ -162,7 +162,7 @@ export function promotedAmrStderrPayload(chunk, classifier = noopAccountFailureC
162
162
  return null;
163
163
  if (!/\bretry\b/i.test(chunk))
164
164
  return null;
165
- const failure = classifier.classify(chunk);
165
+ const failure = classifier.classify({ text: chunk });
166
166
  if (!failure)
167
167
  return null;
168
168
  return {