@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.
- package/CHANGELOG.md +108 -0
- package/README.md +34 -17
- package/dist/acp-model-probe.d.ts +6 -4
- package/dist/acp-model-probe.js +3 -3
- package/dist/agent-protocol/acp/account-failure.d.ts +4 -2
- package/dist/agent-protocol/acp/account-failure.js +1 -1
- package/dist/agent-protocol/acp/json.d.ts +6 -6
- package/dist/agent-protocol/acp/json.js +3 -3
- package/dist/agent-protocol/acp/models.d.ts +10 -5
- package/dist/agent-protocol/acp/models.js +32 -31
- package/dist/agent-protocol/acp/session-params.d.ts +3 -1
- package/dist/agent-protocol/acp/session-params.js +2 -1
- package/dist/agent-protocol/acp/session.d.ts +28 -22
- package/dist/agent-protocol/acp/session.js +181 -147
- package/dist/agent-protocol/acp/text-suppression.d.ts +1 -1
- package/dist/agent-protocol/acp/text-suppression.js +1 -1
- package/dist/agent-protocol/acp/types.d.ts +3 -2
- package/dist/agent-protocol/acp/updates.d.ts +11 -11
- package/dist/agent-protocol/acp/updates.js +2 -2
- package/dist/agent-protocol/core/json-line-stream.d.ts +24 -8
- package/dist/agent-protocol/core/json-line-stream.js +8 -8
- package/dist/agent-protocol/pi-rpc/events.d.ts +5 -1
- package/dist/agent-protocol/pi-rpc/events.js +36 -36
- package/dist/agent-protocol/pi-rpc/internal.d.ts +4 -1
- package/dist/agent-protocol/pi-rpc/models.d.ts +3 -1
- package/dist/agent-protocol/pi-rpc/models.js +1 -1
- package/dist/agent-protocol/pi-rpc/session.d.ts +1 -1
- package/dist/agent-protocol/pi-rpc/session.js +33 -32
- package/dist/amr-model-cache.d.ts +12 -4
- package/dist/amr-model-cache.js +17 -21
- package/dist/amr-profile-resolver.d.ts +3 -1
- package/dist/anthropic-live-models.js +0 -0
- package/dist/artifact-taxonomy.d.ts +6 -2
- package/dist/auth.d.ts +52 -15
- package/dist/auth.js +36 -36
- package/dist/claude-code-models.js +2 -2
- package/dist/claude-stream.d.ts +22 -3
- package/dist/claude-stream.js +24 -8
- package/dist/copilot-stream.d.ts +7 -3
- package/dist/copilot-stream.js +3 -3
- package/dist/defs/aider.d.ts +8 -1
- package/dist/defs/aider.js +2 -2
- package/dist/defs/amp.d.ts +8 -1
- package/dist/defs/amp.js +2 -2
- package/dist/defs/amr.d.ts +22 -6
- package/dist/defs/amr.js +23 -23
- package/dist/defs/antigravity.d.ts +15 -4
- package/dist/defs/antigravity.js +9 -8
- package/dist/defs/auggie.d.ts +4 -1
- package/dist/defs/auggie.js +1 -1
- package/dist/defs/claude.d.ts +12 -2
- package/dist/defs/claude.js +5 -5
- package/dist/defs/cline.d.ts +4 -1
- package/dist/defs/cline.js +1 -1
- package/dist/defs/codebuddy.d.ts +8 -1
- package/dist/defs/codebuddy.js +2 -2
- package/dist/defs/codex.d.ts +18 -4
- package/dist/defs/codex.js +7 -7
- package/dist/defs/copilot.d.ts +8 -1
- package/dist/defs/copilot.js +2 -2
- package/dist/defs/crush.d.ts +8 -1
- package/dist/defs/crush.js +1 -1
- package/dist/defs/cursor-agent.d.ts +14 -3
- package/dist/defs/cursor-agent.js +5 -5
- package/dist/defs/deepseek.d.ts +8 -1
- package/dist/defs/deepseek.js +2 -2
- package/dist/defs/devin.d.ts +12 -2
- package/dist/defs/devin.js +3 -3
- package/dist/defs/droid.d.ts +8 -1
- package/dist/defs/droid.js +1 -1
- package/dist/defs/gemini.d.ts +8 -1
- package/dist/defs/gemini.js +1 -1
- package/dist/defs/goose.d.ts +4 -1
- package/dist/defs/goose.js +1 -1
- package/dist/defs/grok-build.d.ts +11 -2
- package/dist/defs/grok-build.js +3 -3
- package/dist/defs/hermes.d.ts +4 -1
- package/dist/defs/hermes.js +2 -2
- package/dist/defs/kilo.d.ts +4 -1
- package/dist/defs/kilo.js +2 -2
- package/dist/defs/kimi.d.ts +4 -1
- package/dist/defs/kimi.js +2 -2
- package/dist/defs/kiro.d.ts +4 -1
- package/dist/defs/kiro.js +2 -2
- package/dist/defs/mimo.d.ts +8 -1
- package/dist/defs/mimo.js +2 -2
- package/dist/defs/opencode.d.ts +9 -2
- package/dist/defs/opencode.js +2 -2
- package/dist/defs/pi.d.ts +12 -2
- package/dist/defs/pi.js +9 -9
- package/dist/defs/qoder.d.ts +8 -1
- package/dist/defs/qoder.js +2 -2
- package/dist/defs/qwen.d.ts +8 -1
- package/dist/defs/qwen.js +2 -2
- package/dist/defs/reasonix.d.ts +4 -1
- package/dist/defs/reasonix.js +3 -3
- package/dist/defs/shared.d.ts +4 -2
- package/dist/defs/shared.js +2 -2
- package/dist/defs/trae-cli.d.ts +12 -2
- package/dist/defs/trae-cli.js +3 -3
- package/dist/defs/vibe.d.ts +4 -1
- package/dist/defs/vibe.js +2 -2
- package/dist/detection.d.ts +19 -4
- package/dist/detection.js +33 -33
- package/dist/diagnostics.d.ts +14 -3
- package/dist/diagnostics.js +5 -5
- package/dist/env.d.ts +16 -4
- package/dist/env.js +7 -7
- package/dist/executables.d.ts +24 -8
- package/dist/executables.js +13 -13
- package/dist/index.d.ts +228 -4
- package/dist/index.js +230 -6
- package/dist/invocation.d.ts +6 -1
- package/dist/invocation.js +2 -2
- package/dist/json-event-stream.d.ts +7 -2
- package/dist/json-event-stream.js +3 -3
- package/dist/launch.d.ts +12 -2
- package/dist/launch.js +3 -3
- package/dist/log-file.d.ts +4 -1
- package/dist/log-file.js +2 -2
- package/dist/mcp.d.ts +6 -3
- package/dist/mcp.js +1 -1
- package/dist/metadata.d.ts +7 -3
- package/dist/metadata.js +1 -1
- package/dist/mmd-routes.d.ts +22 -6
- package/dist/mmd-routes.js +14 -14
- package/dist/model-catalog-cache.d.ts +50 -0
- package/dist/model-catalog-cache.js +61 -0
- package/dist/model-loading-state.d.ts +10 -0
- package/dist/model-loading-state.js +23 -0
- package/dist/model-registry.d.ts +28 -7
- package/dist/model-registry.js +11 -8
- package/dist/models.d.ts +31 -6
- package/dist/models.js +6 -6
- package/dist/opencode-log.d.ts +12 -4
- package/dist/opencode-log.js +8 -8
- package/dist/paths.d.ts +6 -2
- package/dist/paths.js +3 -3
- package/dist/pi-models.d.ts +3 -1
- package/dist/pi-models.js +2 -2
- package/dist/prompt-budget.d.ts +17 -4
- package/dist/prompt-budget.js +3 -3
- package/dist/prompt-file.d.ts +5 -1
- package/dist/prompt-file.js +1 -1
- package/dist/providers/aihubmix.d.ts +34 -9
- package/dist/providers/aihubmix.js +26 -26
- package/dist/providers/anthropic-messages.d.ts +77 -20
- package/dist/providers/anthropic-messages.js +53 -50
- package/dist/providers/azure-chat.d.ts +21 -7
- package/dist/providers/azure-chat.js +18 -16
- package/dist/providers/connection-guard.d.ts +45 -31
- package/dist/providers/connection-guard.js +40 -143
- package/dist/providers/connection-test.d.ts +1 -1
- package/dist/providers/connection-test.js +13 -30
- package/dist/providers/elevenlabs.d.ts +7 -2
- package/dist/providers/elevenlabs.js +4 -4
- package/dist/providers/google-messages.d.ts +44 -10
- package/dist/providers/google-messages.js +36 -34
- package/dist/providers/google-schema.d.ts +26 -0
- package/dist/providers/google-schema.js +253 -0
- package/dist/providers/google.d.ts +21 -6
- package/dist/providers/google.js +10 -10
- package/dist/providers/index.d.ts +3 -2
- package/dist/providers/index.js +3 -2
- package/dist/providers/model-catalog.d.ts +1 -1
- package/dist/providers/model-catalog.js +14 -27
- package/dist/providers/oauth-callback-server.d.ts +1 -1
- package/dist/providers/oauth-callback-server.js +3 -2
- package/dist/providers/oauth-credentials.d.ts +7 -1
- package/dist/providers/oauth-credentials.js +5 -5
- package/dist/providers/oauth-provider.d.ts +2 -2
- package/dist/providers/oauth-provider.js +20 -25
- package/dist/providers/oauth-tokens.d.ts +26 -6
- package/dist/providers/oauth-tokens.js +9 -9
- package/dist/providers/ollama-chat.d.ts +66 -31
- package/dist/providers/ollama-chat.js +64 -61
- package/dist/providers/openai-chat.d.ts +77 -22
- package/dist/providers/openai-chat.js +71 -67
- package/dist/providers/pkce.d.ts +33 -20
- package/dist/providers/pkce.js +75 -146
- package/dist/providers/sse-decode.d.ts +3 -1
- package/dist/providers/sse-decode.js +1 -1
- package/dist/providers/token-params.d.ts +16 -5
- package/dist/providers/token-params.js +6 -6
- package/dist/providers/tool-turn-types.d.ts +94 -0
- package/dist/providers/tool-turn-types.js +2 -0
- package/dist/providers/tool-turn.d.ts +10 -0
- package/dist/providers/tool-turn.js +258 -0
- package/dist/qoder-stream.d.ts +7 -3
- package/dist/qoder-stream.js +3 -3
- package/dist/registry.d.ts +6 -2
- package/dist/registry.js +4 -4
- package/dist/resolution.d.ts +5 -1
- package/dist/resolution.js +3 -3
- package/dist/role-marker-guard.d.ts +9 -5
- package/dist/role-marker-guard.js +5 -5
- package/dist/terminal-launch.d.ts +6 -1
- package/dist/terminal-launch.js +2 -2
- package/dist/types.d.ts +22 -6
- 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,
|
|
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:
|
|
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:
|
|
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: (
|
|
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: (
|
|
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:
|
|
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:
|
|
153
|
-
error:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
277
|
-
result:
|
|
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(
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
623
|
-
|
|
624
|
-
|
|
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: '
|
|
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
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
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
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
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
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
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
|
-
|
|
728
|
-
|
|
729
|
-
|
|
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
|
|
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
|
-
|
|
756
|
-
|
|
757
|
-
|
|
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
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
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)
|