@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
@@ -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 {
@@ -16,10 +16,19 @@ export interface JsonLineAccumulatorState {
16
16
  pendingJson: string;
17
17
  pendingJsonLineCount: number;
18
18
  }
19
- export declare function emitJsonLine(candidate: string, onMessage: (message: unknown, rawLine: string) => void): boolean;
20
- export declare function continuePendingJsonLine(state: JsonLineAccumulatorState, trimmed: string, onMessage: (message: unknown, rawLine: string) => void): void;
19
+ export declare function emitJsonLine(candidate: string, onMessage: (requiredArgs: {
20
+ message: unknown;
21
+ rawLine: string;
22
+ }) => void): boolean;
23
+ export declare function continuePendingJsonLine(state: JsonLineAccumulatorState, trimmed: string, onMessage: (requiredArgs: {
24
+ message: unknown;
25
+ rawLine: string;
26
+ }) => void): void;
21
27
  export declare function tryStartPendingJsonLine(state: JsonLineAccumulatorState, trimmed: string): void;
22
- export declare function handleJsonLine(state: JsonLineAccumulatorState, line: string, onMessage: (message: unknown, rawLine: string) => void): void;
28
+ export declare function handleJsonLine(state: JsonLineAccumulatorState, line: string, onMessage: (requiredArgs: {
29
+ message: unknown;
30
+ rawLine: string;
31
+ }) => void): void;
23
32
  /**
24
33
  * Creates a streaming JSON-line parser over a raw byte/string stream.
25
34
  * Buffers incoming chunks, splits on newline boundaries, and attempts
@@ -30,13 +39,20 @@ export declare function handleJsonLine(state: JsonLineAccumulatorState, line: st
30
39
  * Used as the shared ACP transport: both the acp/ and pi-rpc/ adapters call
31
40
  * this to decode JSON-RPC frames from a subprocess's stdout.
32
41
  *
33
- * @param onMessage - Called for each successfully parsed JSON value along with
34
- * the raw reassembled line string as a second argument.
35
- * @returns An object with `feed(chunk)` for incremental input and `flush()` to
42
+ * @param requiredArgs.onMessage - Called with `{ message, rawLine }` for each
43
+ * successfully parsed JSON value and its raw reassembled line string.
44
+ * @returns An object with `feed({ chunk })` for incremental input and `flush()` to
36
45
  * drain any residual buffered content at stream end.
37
46
  */
38
- export declare function createJsonLineStream(onMessage: (message: unknown, rawLine: string) => void): {
39
- feed(chunk: string): void;
47
+ export declare function createJsonLineStream({ onMessage }: {
48
+ onMessage: (requiredArgs: {
49
+ message: unknown;
50
+ rawLine: string;
51
+ }) => void;
52
+ }): {
53
+ feed({ chunk }: {
54
+ chunk: string;
55
+ }): void;
40
56
  flush(): void;
41
57
  };
42
58
  export type JsonObjectScanFrame = {
@@ -22,7 +22,7 @@ function resetPendingJson(state) {
22
22
  }
23
23
  export function emitJsonLine(candidate, onMessage) {
24
24
  try {
25
- onMessage(JSON.parse(candidate), candidate);
25
+ onMessage({ message: JSON.parse(candidate), rawLine: candidate });
26
26
  return true;
27
27
  }
28
28
  catch {
@@ -81,16 +81,16 @@ export function handleJsonLine(state, line, onMessage) {
81
81
  * Used as the shared ACP transport: both the acp/ and pi-rpc/ adapters call
82
82
  * this to decode JSON-RPC frames from a subprocess's stdout.
83
83
  *
84
- * @param onMessage - Called for each successfully parsed JSON value along with
85
- * the raw reassembled line string as a second argument.
86
- * @returns An object with `feed(chunk)` for incremental input and `flush()` to
84
+ * @param requiredArgs.onMessage - Called with `{ message, rawLine }` for each
85
+ * successfully parsed JSON value and its raw reassembled line string.
86
+ * @returns An object with `feed({ chunk })` for incremental input and `flush()` to
87
87
  * drain any residual buffered content at stream end.
88
88
  */
89
- export function createJsonLineStream(onMessage) {
89
+ export function createJsonLineStream({ onMessage }) {
90
90
  let buffer = '';
91
91
  const state = { pendingJson: '', pendingJsonLineCount: 0 };
92
92
  return {
93
- feed(chunk) {
93
+ feed({ chunk }) {
94
94
  buffer += chunk;
95
95
  const lines = buffer.split('\n');
96
96
  buffer = lines.pop() || '';
@@ -114,7 +114,7 @@ export function createJsonLineStream(onMessage) {
114
114
  // a defensive `if (pendingJson && emit(pendingJson)) { pendingJson =
115
115
  // '' }` here that was provably dead code; removed per this package's
116
116
  // coverage-driven dead-branch discipline rather than left uncovered or
117
- // suppressed. See source-map.md.
117
+ // suppressed. See archived provenance ledger.
118
118
  // Ignore trailing non-JSON log lines on stdout.
119
119
  },
120
120
  };
@@ -136,7 +136,7 @@ export function afterValue(state) {
136
136
  // guard was provably dead (verified by a 2M-trial adversarial fuzz,
137
137
  // including deliberately malformed bracket sequences, finding zero
138
138
  // mismatches) and is removed here per this package's coverage-driven
139
- // dead-branch discipline. See source-map.md.
139
+ // dead-branch discipline. See archived provenance ledger.
140
140
  function closeFrame(state) {
141
141
  state.stack.pop();
142
142
  afterValue(state);
@@ -43,5 +43,9 @@ export declare function handleAutoRetryEnd(raw: JsonRecord, send: SendAgentEvent
43
43
  * @param ctx - Per-run context: start timestamp and first-token sentinel.
44
44
  * @returns `'agent_end'` on run completion, `null` otherwise.
45
45
  */
46
- export declare function mapPiRpcEvent(raw: JsonRecord, send: SendAgentEvent, ctx: PiRpcContext): 'agent_end' | null;
46
+ export declare function mapPiRpcEvent({ raw, send, ctx }: {
47
+ raw: JsonRecord;
48
+ send: SendAgentEvent;
49
+ ctx: PiRpcContext;
50
+ }): 'agent_end' | null;
47
51
  //# sourceMappingURL=events.d.ts.map
@@ -1,13 +1,13 @@
1
1
  import { getRecord } from './internal.js';
2
2
  export function handleAgentStart(_raw, send) {
3
- send('agent', { type: 'status', label: 'working' });
3
+ send({ event: 'agent', payload: { type: 'status', label: 'working' } });
4
4
  return null;
5
5
  }
6
6
  export function handleAgentEnd() {
7
7
  return 'agent_end';
8
8
  }
9
9
  export function handleTurnStart(_raw, send) {
10
- send('agent', { type: 'status', label: 'thinking' });
10
+ send({ event: 'agent', payload: { type: 'status', label: 'thinking' } });
11
11
  return null;
12
12
  }
13
13
  /** Extracts the subset of pi's `message.usage` fields the daemon reports as token usage. */
@@ -30,12 +30,12 @@ export function emitTurnEndUsage(u, send, ctx) {
30
30
  if (Object.keys(usage).length === 0)
31
31
  return;
32
32
  const cost = getRecord(u.cost);
33
- send('agent', {
34
- type: 'usage',
35
- usage,
36
- costUsd: cost?.total ?? cost?.totalCost ?? null,
37
- durationMs: Date.now() - ctx.runStartedAt,
38
- });
33
+ send({ event: 'agent', payload: {
34
+ type: 'usage',
35
+ usage,
36
+ costUsd: cost?.total ?? cost?.totalCost ?? null,
37
+ durationMs: Date.now() - ctx.runStartedAt,
38
+ } });
39
39
  }
40
40
  export function emitTurnEndErrorIfPresent(message, raw, send) {
41
41
  if (message?.stopReason !== 'error')
@@ -43,7 +43,7 @@ export function emitTurnEndErrorIfPresent(message, raw, send) {
43
43
  const messageText = typeof message.errorMessage === 'string' && message.errorMessage.length > 0
44
44
  ? message.errorMessage
45
45
  : 'Pi agent error';
46
- send('agent', { type: 'error', message: messageText, raw });
46
+ send({ event: 'agent', payload: { type: 'error', message: messageText, raw } });
47
47
  }
48
48
  export function handleTurnEnd(raw, send, ctx) {
49
49
  const message = getRecord(raw.message);
@@ -58,24 +58,24 @@ export function handleAssistantTextDelta(ev, send, ctx) {
58
58
  return;
59
59
  if (!ctx.sentFirstToken.value) {
60
60
  ctx.sentFirstToken.value = true;
61
- send('agent', {
62
- type: 'status',
63
- label: 'streaming',
64
- ttftMs: Date.now() - ctx.runStartedAt,
65
- });
61
+ send({ event: 'agent', payload: {
62
+ type: 'status',
63
+ label: 'streaming',
64
+ ttftMs: Date.now() - ctx.runStartedAt,
65
+ } });
66
66
  }
67
- send('agent', { type: 'text_delta', delta: ev.delta });
67
+ send({ event: 'agent', payload: { type: 'text_delta', delta: ev.delta } });
68
68
  }
69
69
  export function handleAssistantThinkingDelta(ev, send) {
70
70
  if (typeof ev.delta !== 'string')
71
71
  return;
72
- send('agent', { type: 'thinking_delta', delta: ev.delta });
72
+ send({ event: 'agent', payload: { type: 'thinking_delta', delta: ev.delta } });
73
73
  }
74
74
  export function handleAssistantThinkingStart(_ev, send) {
75
- send('agent', { type: 'thinking_start' });
75
+ send({ event: 'agent', payload: { type: 'thinking_start' } });
76
76
  }
77
77
  export function handleAssistantThinkingEnd(_ev, send) {
78
- send('agent', { type: 'thinking_end' });
78
+ send({ event: 'agent', payload: { type: 'thinking_end' } });
79
79
  }
80
80
  // pi's RPC protocol emits a message_update with error delta when
81
81
  // the model returns an error (e.g. aborted, context overflow).
@@ -87,7 +87,7 @@ export function handleAssistantError(ev, send, _ctx, raw) {
87
87
  : typeof ev.delta === 'string' && ev.delta.length > 0
88
88
  ? ev.delta
89
89
  : 'Agent error';
90
- send('agent', { type: 'error', message, raw });
90
+ send({ event: 'agent', payload: { type: 'error', message, raw } });
91
91
  }
92
92
  const ASSISTANT_MESSAGE_EVENT_HANDLERS = {
93
93
  text_delta: handleAssistantTextDelta,
@@ -112,12 +112,12 @@ export function handleMessageEnd() {
112
112
  return null;
113
113
  }
114
114
  export function handleToolExecutionStart(raw, send) {
115
- send('agent', {
116
- type: 'tool_use',
117
- id: raw.toolCallId ?? null,
118
- name: raw.toolName ?? null,
119
- input: raw.args ?? null,
120
- });
115
+ send({ event: 'agent', payload: {
116
+ type: 'tool_use',
117
+ id: raw.toolCallId ?? null,
118
+ name: raw.toolName ?? null,
119
+ input: raw.args ?? null,
120
+ } });
121
121
  return null;
122
122
  }
123
123
  export function handleToolExecutionEnd(raw, send) {
@@ -133,12 +133,12 @@ export function handleToolExecutionEnd(raw, send) {
133
133
  : typeof content === 'string'
134
134
  ? content
135
135
  : '';
136
- send('agent', {
137
- type: 'tool_result',
138
- toolUseId: raw.toolCallId ?? null,
139
- content: text,
140
- isError: raw.isError === true,
141
- });
136
+ send({ event: 'agent', payload: {
137
+ type: 'tool_result',
138
+ toolUseId: raw.toolCallId ?? null,
139
+ content: text,
140
+ isError: raw.isError === true,
141
+ } });
142
142
  return null;
143
143
  }
144
144
  // pi's RPC protocol can emit `extension_error` when an extension
@@ -149,15 +149,15 @@ export function handleExtensionError(raw, send) {
149
149
  const message = typeof raw.error === 'string' && raw.error.length > 0
150
150
  ? raw.error
151
151
  : 'Extension error';
152
- send('agent', { type: 'error', message, raw });
152
+ send({ event: 'agent', payload: { type: 'error', message, raw } });
153
153
  return null;
154
154
  }
155
155
  export function handleCompactionStart(_raw, send) {
156
- send('agent', { type: 'status', label: 'compacting' });
156
+ send({ event: 'agent', payload: { type: 'status', label: 'compacting' } });
157
157
  return null;
158
158
  }
159
159
  export function handleAutoRetryStart(_raw, send) {
160
- send('agent', { type: 'status', label: 'retrying' });
160
+ send({ event: 'agent', payload: { type: 'status', label: 'retrying' } });
161
161
  return null;
162
162
  }
163
163
  export function handleAutoRetryEnd(raw, send) {
@@ -169,7 +169,7 @@ export function handleAutoRetryEnd(raw, send) {
169
169
  const message = typeof raw.finalError === 'string' && raw.finalError.length > 0
170
170
  ? raw.finalError
171
171
  : 'Auto-retry exhausted';
172
- send('agent', { type: 'error', message, raw });
172
+ send({ event: 'agent', payload: { type: 'error', message, raw } });
173
173
  return null;
174
174
  }
175
175
  const PI_RPC_EVENT_HANDLERS = {
@@ -196,7 +196,7 @@ const PI_RPC_EVENT_HANDLERS = {
196
196
  * @param ctx - Per-run context: start timestamp and first-token sentinel.
197
197
  * @returns `'agent_end'` on run completion, `null` otherwise.
198
198
  */
199
- export function mapPiRpcEvent(raw, send, ctx) {
199
+ export function mapPiRpcEvent({ raw, send, ctx }) {
200
200
  const handler = typeof raw.type === 'string' ? PI_RPC_EVENT_HANDLERS[raw.type] : undefined;
201
201
  return handler ? handler(raw, send, ctx) : null;
202
202
  }
@@ -6,7 +6,10 @@
6
6
  /** A plain JSON object — the base payload type for pi RPC messages. */
7
7
  export type JsonRecord = Record<string, unknown>;
8
8
  /** Callback signature for forwarding a typed event to the daemon's SSE layer. */
9
- export type SendAgentEvent = (channel: string, payload: JsonRecord) => void;
9
+ export type SendAgentEvent = (requiredArgs: {
10
+ event: string;
11
+ payload: JsonRecord;
12
+ }) => void;
10
13
  /** Optional token-count fields surfaced by pi's RPC protocol on turn completion. */
11
14
  export type TokenUsage = {
12
15
  input_tokens?: number;
@@ -17,5 +17,7 @@ export type PiModelOption = {
17
17
  * @param stdout - Raw stdout captured from `pi --list-models`, as a string or Buffer.
18
18
  * @returns Parsed model array (at least 2 entries), or `null` when nothing is found.
19
19
  */
20
- export declare function parsePiModels(stdout: unknown): PiModelOption[] | null;
20
+ export declare function parsePiModels({ stdout }: {
21
+ stdout: unknown;
22
+ }): PiModelOption[] | null;
21
23
  //# sourceMappingURL=models.d.ts.map
@@ -12,7 +12,7 @@
12
12
  * @param stdout - Raw stdout captured from `pi --list-models`, as a string or Buffer.
13
13
  * @returns Parsed model array (at least 2 entries), or `null` when nothing is found.
14
14
  */
15
- export function parsePiModels(stdout) {
15
+ export function parsePiModels({ stdout }) {
16
16
  const lines = String(stdout || '')
17
17
  .split('\n')
18
18
  .map((l) => l.trim())
@@ -141,5 +141,5 @@ export declare function handlePiResponseMessage({ raw, parentSessionRpcId, promp
141
141
  * parent session path for conversation resume.
142
142
  * @returns A `PiRpcSession` handle with `hasFatalError`, `getLastSessionPath`, and `abort`.
143
143
  */
144
- export declare function attachPiRpcSession({ child, prompt, cwd, model, send, imagePaths, uploadRoot, parentSession, }: PiRpcSessionOptions): PiRpcSession;
144
+ export declare function attachPiRpcSession(requiredArgs: Pick<PiRpcSessionOptions, "child" | "prompt" | "send">, optionalArgs?: Omit<PiRpcSessionOptions, "child" | "prompt" | "send">): PiRpcSession;
145
145
  //# sourceMappingURL=session.d.ts.map
@@ -247,7 +247,8 @@ export function handlePiResponseMessage({ raw, parentSessionRpcId, promptRpcId,
247
247
  * parent session path for conversation resume.
248
248
  * @returns A `PiRpcSession` handle with `hasFatalError`, `getLastSessionPath`, and `abort`.
249
249
  */
250
- export function attachPiRpcSession({ child, prompt, cwd, model, send, imagePaths, uploadRoot, parentSession, }) {
250
+ export function attachPiRpcSession(requiredArgs, optionalArgs = {}) {
251
+ const { child, prompt, cwd, model, send, imagePaths, uploadRoot, parentSession, } = { ...optionalArgs, ...requiredArgs };
251
252
  const stdin = child.stdin;
252
253
  const stdout = child.stdout;
253
254
  if (stdin === null) {
@@ -281,17 +282,17 @@ export function attachPiRpcSession({ child, prompt, cwd, model, send, imagePaths
281
282
  return;
282
283
  finished = true;
283
284
  fatal = true;
284
- send('error', { message, ...(code ? { code } : {}) });
285
+ send({ event: 'error', payload: { message, ...(code ? { code } : {}) } });
285
286
  if (!child.killed)
286
287
  child.kill('SIGTERM');
287
288
  };
288
289
  // Emit initial status with model name immediately — before pi even
289
290
  // responds — so the UI header shows the model name at session start.
290
- send('agent', {
291
- type: 'status',
292
- label: 'initializing',
293
- model: typeof model === 'string' && model ? model : null,
294
- });
291
+ send({ event: 'agent', payload: {
292
+ type: 'status',
293
+ label: 'initializing',
294
+ model: typeof model === 'string' && model ? model : null,
295
+ } });
295
296
  // ---- Outbound: send new_session (if parentSession provided) then prompt via RPC ----
296
297
  stdin.on('error', (err) => {
297
298
  if (errorCode(err) !== 'EPIPE') {
@@ -351,33 +352,33 @@ export function attachPiRpcSession({ child, prompt, cwd, model, send, imagePaths
351
352
  }, shutdownMs);
352
353
  };
353
354
  // ---- Inbound: parse stdout events ----
354
- const parser = createJsonLineStream((raw) => {
355
- if (!isRecord(raw))
356
- return;
357
- // Once finished (agent_end or abort), stop processing — the run is
358
- // over, so no more agent events should be emitted. We still drain
359
- // stdout via parser.feed() so the pipe doesn't break; we just skip
360
- // acting on the parsed objects.
361
- if (finished)
362
- return;
363
- // Extension UI requests: auto-resolve to keep pi unblocked.
364
- if (raw.type === 'extension_ui_request') {
365
- replyExtensionUi(stdin, raw);
366
- return;
367
- }
368
- if (raw.type === 'response') {
369
- handlePiResponseMessage({ raw, parentSessionRpcId, promptRpcId, fail, sendPromptCommand });
370
- return;
371
- }
372
- // Agent events: delegate to the pure mapper.
373
- const result = mapPiRpcEvent(raw, send, { runStartedAt, sentFirstToken });
374
- if (result === 'agent_end') {
375
- handlePiAgentEnd();
376
- }
377
- });
355
+ const parser = createJsonLineStream({ onMessage: ({ message: raw }) => {
356
+ if (!isRecord(raw))
357
+ return;
358
+ // Once finished (agent_end or abort), stop processing — the run is
359
+ // over, so no more agent events should be emitted. We still drain
360
+ // stdout via parser.feed() so the pipe doesn't break; we just skip
361
+ // acting on the parsed objects.
362
+ if (finished)
363
+ return;
364
+ // Extension UI requests: auto-resolve to keep pi unblocked.
365
+ if (raw.type === 'extension_ui_request') {
366
+ replyExtensionUi(stdin, raw);
367
+ return;
368
+ }
369
+ if (raw.type === 'response') {
370
+ handlePiResponseMessage({ raw, parentSessionRpcId, promptRpcId, fail, sendPromptCommand });
371
+ return;
372
+ }
373
+ // Agent events: delegate to the pure mapper.
374
+ const result = mapPiRpcEvent({ raw: raw, send: send, ctx: { runStartedAt, sentFirstToken } });
375
+ if (result === 'agent_end') {
376
+ handlePiAgentEnd();
377
+ }
378
+ } });
378
379
  stdout.on('data', (chunk) => {
379
380
  try {
380
- parser.feed(typeof chunk === 'string' ? chunk : chunk.toString('utf8'));
381
+ parser.feed({ chunk: typeof chunk === 'string' ? chunk : chunk.toString('utf8') });
381
382
  }
382
383
  catch (err) {
383
384
  fail(`parser: ${errorMessage(err)}`);
@@ -26,11 +26,19 @@ type Fetchers = {
26
26
  fetchRemote: () => Promise<RuntimeModelOption[]>;
27
27
  };
28
28
  export declare class AmrModelLoadingCache {
29
- private readonly refreshIntervalMs;
30
29
  private readonly states;
31
- constructor(refreshIntervalMs?: number);
32
- get(cacheKey: string, fetchers: Fetchers): Promise<AmrModelsResponse>;
33
- warm(cacheKey: string, fetchRemote: () => Promise<RuntimeModelOption[]>): void;
30
+ private readonly refreshIntervalMs;
31
+ constructor(_requiredArgs?: Record<string, never>, { refreshIntervalMs }?: {
32
+ refreshIntervalMs?: number;
33
+ });
34
+ get({ cacheKey, fetchers }: {
35
+ cacheKey: string;
36
+ fetchers: Fetchers;
37
+ }): Promise<AmrModelsResponse>;
38
+ warm({ cacheKey, fetchRemote }: {
39
+ cacheKey: string;
40
+ fetchRemote: () => Promise<RuntimeModelOption[]>;
41
+ }): void;
34
42
  resetForTests(): void;
35
43
  private stateFor;
36
44
  private startRefresh;
@@ -1,3 +1,4 @@
1
+ import { coalesceModelLoad } from './model-loading-state.js';
1
2
  // The AMR model catalog changes rarely (new models land on the order of
2
3
  // days), and a cached remote list is returned immediately while a refresh
3
4
  // runs in the background — `get()` never blocks on the network when a
@@ -11,12 +12,12 @@ function errorMessage(error) {
11
12
  return error instanceof Error ? error.message : String(error ?? 'unknown error');
12
13
  }
13
14
  export class AmrModelLoadingCache {
14
- refreshIntervalMs;
15
15
  states = new Map();
16
- constructor(refreshIntervalMs = DEFAULT_REMOTE_REFRESH_INTERVAL_MS) {
16
+ refreshIntervalMs;
17
+ constructor(_requiredArgs = {}, { refreshIntervalMs = DEFAULT_REMOTE_REFRESH_INTERVAL_MS } = {}) {
17
18
  this.refreshIntervalMs = refreshIntervalMs;
18
19
  }
19
- async get(cacheKey, fetchers) {
20
+ async get({ cacheKey, fetchers }) {
20
21
  const state = this.stateFor(cacheKey);
21
22
  const now = Date.now();
22
23
  if (state.remote) {
@@ -40,7 +41,7 @@ export class AmrModelLoadingCache {
40
41
  ...(state.lastRemoteError ? { remoteError: state.lastRemoteError } : {}),
41
42
  };
42
43
  }
43
- warm(cacheKey, fetchRemote) {
44
+ warm({ cacheKey, fetchRemote }) {
44
45
  this.startRefresh(this.stateFor(cacheKey), fetchRemote);
45
46
  }
46
47
  resetForTests() {
@@ -55,24 +56,19 @@ export class AmrModelLoadingCache {
55
56
  return created;
56
57
  }
57
58
  startRefresh(state, fetchRemote) {
58
- if (state.inFlight)
59
- return;
60
- state.inFlight = (async () => {
61
- try {
62
- const models = await fetchRemote();
63
- if (models.length === 0) {
64
- throw new Error('AMR remote model list returned no chat models');
59
+ void coalesceModelLoad({ state, load: async () => {
60
+ try {
61
+ const models = await fetchRemote();
62
+ if (models.length === 0) {
63
+ throw new Error('AMR remote model list returned no chat models');
64
+ }
65
+ state.remote = { models, fetchedAt: Date.now() };
66
+ state.lastRemoteError = null;
67
+ }
68
+ catch (error) {
69
+ state.lastRemoteError = errorMessage(error);
65
70
  }
66
- state.remote = { models, fetchedAt: Date.now() };
67
- state.lastRemoteError = null;
68
- }
69
- catch (error) {
70
- state.lastRemoteError = errorMessage(error);
71
- }
72
- finally {
73
- state.inFlight = null;
74
- }
75
- })();
71
+ } });
76
72
  }
77
73
  }
78
74
  export const amrModelLoadingCache = new AmrModelLoadingCache();