@jini-ai/daemon 0.5.3 → 0.6.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 (77) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/agent-executor/contracts.d.ts +45 -14
  3. package/dist/agent-executor/contracts.js +0 -9
  4. package/dist/agent-executor/event-translation.d.ts +2 -2
  5. package/dist/agent-executor/event-translation.js +0 -2
  6. package/dist/agent-executor/events.d.ts +26 -6
  7. package/dist/agent-executor/events.js +198 -11
  8. package/dist/agent-executor/handoff.d.ts +1 -1
  9. package/dist/agent-executor/handoff.js +0 -1
  10. package/dist/agent-executor/index.d.ts +1 -1
  11. package/dist/agent-executor/launch.d.ts +1 -1
  12. package/dist/agent-executor/launch.js +47 -3
  13. package/dist/agent-executor/mcp-bridge.d.ts +2 -2
  14. package/dist/agent-executor/mcp-bridge.js +19 -8
  15. package/dist/agent-executor/model-receipts.d.ts +6 -0
  16. package/dist/agent-executor/model-receipts.js +26 -0
  17. package/dist/agent-executor.d.ts +7 -1665
  18. package/dist/agent-executor.js +7 -3007
  19. package/dist/core/read-only-tools.d.ts +130 -0
  20. package/dist/core/read-only-tools.js +115 -0
  21. package/dist/core/tool-audit.d.ts +131 -0
  22. package/dist/core/tool-audit.js +113 -0
  23. package/dist/http/active-context.d.ts +2 -2
  24. package/dist/http/agents.d.ts +8 -2
  25. package/dist/http/agents.js +6 -2
  26. package/dist/http/component-catalog.d.ts +2 -2
  27. package/dist/http/connectors.d.ts +17 -17
  28. package/dist/http/daemon-status.d.ts +2 -2
  29. package/dist/http/db-ops.d.ts +3 -3
  30. package/dist/http/delegated-tools.d.ts +1 -1
  31. package/dist/http/delegated-tools.js +1 -1
  32. package/dist/http/frontend-sessions.d.ts +1 -1
  33. package/dist/http/health.d.ts +6 -6
  34. package/dist/http/host-tools.d.ts +2 -2
  35. package/dist/http/index.d.ts +2 -2
  36. package/dist/http/index.js +1 -1
  37. package/dist/http/media.d.ts +4 -4
  38. package/dist/http/memory.d.ts +15 -15
  39. package/dist/http/remote-run-events.d.ts +2 -2
  40. package/dist/http/research.d.ts +1 -1
  41. package/dist/http/route-manifest.js +2 -2
  42. package/dist/http/routines.d.ts +7 -7
  43. package/dist/http/run-credentials.d.ts +2 -2
  44. package/dist/http/run-credentials.js +2 -2
  45. package/dist/http/runs.d.ts +31 -6
  46. package/dist/http/runs.js +36 -1
  47. package/dist/http/terminals.d.ts +6 -6
  48. package/dist/http/tool-catalog.d.ts +2 -2
  49. package/dist/http/xai.d.ts +6 -6
  50. package/dist/index.d.ts +75 -571
  51. package/dist/index.js +75 -571
  52. package/dist/read-only-tools.d.ts +1 -129
  53. package/dist/read-only-tools.js +2 -114
  54. package/dist/run-credentials.d.ts +47 -23
  55. package/dist/run-credentials.js +24 -8
  56. package/dist/scheduler.d.ts +5 -0
  57. package/dist/scheduler.js +13 -1
  58. package/dist/session-coordination.d.ts +58 -24
  59. package/dist/session-coordination.js +40 -12
  60. package/dist/store/event-log/sqlite.js +3 -3
  61. package/dist/surface-exchanges.d.ts +207 -61
  62. package/dist/surface-exchanges.js +167 -20
  63. package/dist/tool-audit.d.ts +1 -113
  64. package/dist/tool-audit.js +2 -94
  65. package/dist/tool-recovery/diagnostics.d.ts +132 -0
  66. package/dist/tool-recovery/diagnostics.js +110 -0
  67. package/dist/tool-recovery/federated-refusal.d.ts +92 -0
  68. package/dist/tool-recovery/federated-refusal.js +152 -0
  69. package/dist/tool-recovery/index.d.ts +6 -0
  70. package/dist/tool-recovery/index.js +6 -0
  71. package/dist/tool-recovery/recovery.d.ts +49 -0
  72. package/dist/tool-recovery/recovery.js +419 -0
  73. package/dist/tool-recovery/redaction.d.ts +166 -0
  74. package/dist/tool-recovery/redaction.js +192 -0
  75. package/dist/tool-recovery/stack.d.ts +69 -0
  76. package/dist/tool-recovery/stack.js +62 -0
  77. package/package.json +35 -12
package/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.5.4 — pending release
2
+
3
+ - Re-export the canonical core surface-exchange contracts and parameter constants. Store and ask/report behavior are unchanged; requires core 0.4.2.
4
+
1
5
  ## 0.5.3 — 2026-10-06
2
6
 
3
7
  - Message attachments reach every runtime: new `prepareMessageAttachments` / `messageContentWithImages` (+ `MessageAttachmentImage`, `MessageAttachmentSource`, `MessageAttachmentReader`) turn claimed refs into image pixels for providers and a file notice for non-images.
@@ -1,13 +1,13 @@
1
1
  import type { MessageAttachmentImage } from '../attachment-content.js';
2
- import { type spawn as nodeSpawn, type ChildProcess } from 'node:child_process';
3
- import { type Principal } from '@jini-ai/core';
4
- import { type RunAgentPayload, type RunErrorPayload } from '@jini-ai/protocol';
5
- import { type applyAgentLaunchEnv, type ensureAgentCapabilities, type getAgentDef, type resolveAgentLaunch, type attachAcpSession, type attachPiRpcSession, type prepareAgentLogFile, type preparePromptFileForAgent, type AcpMcpServerInput, type AcpPermissionHandler, type PromptAugmenter } from '@jini-ai/agent-runtime';
6
- import { type createCommandInvocation, type listProcessSnapshots, type ProcessSnapshot, type StopProcessesResult } from '@jini-ai/platform';
7
- import { type RunByteJournal } from '../continuation/journal.js';
8
- import { type RunRetrySideEffectState } from '../run/core/index.js';
9
- import { type ToolExecutor } from '../tool-executor.js';
10
- import { type RunLifecycle } from '../run-lifecycle.js';
2
+ import type { spawn as nodeSpawn, ChildProcess } from 'node:child_process';
3
+ import type { Principal } from '@jini-ai/core';
4
+ import type { RunAgentPayload, RunErrorPayload } from '@jini-ai/protocol';
5
+ import type { applyAgentLaunchEnv, ensureAgentCapabilities, resolveModelForLaunch, getAgentDef, resolveAgentLaunch, attachAcpSession, attachPiRpcSession, prepareAgentLogFile, preparePromptFileForAgent, AcpMcpServerInput, AcpPermissionHandler, PromptAugmenter } from '@jini-ai/agent-runtime';
6
+ import type { createCommandInvocation, listProcessSnapshots, ProcessSnapshot, StopProcessesResult } from '@jini-ai/platform';
7
+ import type { RunByteJournal } from '../continuation/journal.js';
8
+ import type { RunRetrySideEffectState } from '../run/core/index.js';
9
+ import type { ToolExecutor } from '../tool-executor.js';
10
+ import type { RunLifecycle } from '../run-lifecycle.js';
11
11
  /**
12
12
  * A parsed, loosely-typed event as produced by one of `@jini-ai/agent-runtime`'s
13
13
  * four stream-parser factories — each parser's `onEvent` callback receives
@@ -90,7 +90,7 @@ export type AgentRuntimeEventTranslation = {
90
90
  readonly kind: 'ignored';
91
91
  };
92
92
  /** Machine-readable failure reasons `run()` can reject with — every one is preceded by a `lifecycle.finish({status:'failed'})` call (see index.ts module doc's Invariant section). */
93
- export type AgentExecutorErrorCode = 'AGENT_PERMISSION_MODE_INVALID' | 'AGENT_NOT_FOUND' | 'AGENT_RUNTIME_UNSUPPORTED' | 'AGENT_BINARY_NOT_RESOLVED' | 'AGENT_SPAWN_FAILED' | 'AGENT_PROMPT_TOO_LARGE';
93
+ export type AgentExecutorErrorCode = 'AGENT_MODEL_UNRESOLVED' | 'AGENT_PERMISSION_MODE_INVALID' | 'AGENT_NOT_FOUND' | 'AGENT_RUNTIME_UNSUPPORTED' | 'AGENT_BINARY_NOT_RESOLVED' | 'AGENT_SPAWN_FAILED' | 'AGENT_PROMPT_TOO_LARGE';
94
94
  /** Thrown by `AgentExecutor.run()` on every failure path — never a bare `Error`, so callers can branch on `.code` instead of parsing `.message`. */
95
95
  export declare class AgentExecutorError extends Error {
96
96
  readonly code: AgentExecutorErrorCode;
@@ -194,7 +194,27 @@ export interface AgentExecutor {
194
194
  * `lifecycle.finish()` before this rejects (see index.ts module doc's Invariant).
195
195
  */
196
196
  run(requiredArgs: Pick<AgentExecutorRunInput, "runId" | "agentId" | "prompt" | "cwd">, optionalArgs?: Pick<AgentExecutorRunInput, "model" | "reasoning" | "permissionMode" | "imagePaths" | "imageContents" | "extraAllowedDirs" | "uploadRoot" | "credentialEnv" | "env" | "resumeSessionId" | "newSessionId" | "disallowedTools" | "allowedTools" | "settingSources" | "settings">): Promise<void>;
197
+ /**
198
+ * Delivers a message the human sent while `runId` is going straight into its live agent process
199
+ * (see {@link UserMessageDelivery} for each outcome). A stream-json agent is interrupted first and
200
+ * gets the message once the CLI acknowledges the interrupt, so this settles in milliseconds (at
201
+ * most `INTERRUPT_ACK_TIMEOUT_MS`); it never waits on the agent's reply.
202
+ */
203
+ sendUserMessage(requiredArgs: {
204
+ readonly runId: string;
205
+ readonly text: string;
206
+ }): Promise<UserMessageDelivery>;
197
207
  }
208
+ /**
209
+ * What {@link AgentExecutor.sendUserMessage} did with a mid-run message:
210
+ * - `'delivered'` — the running turn was interrupted and the message written into the live
211
+ * session; the agent answers it next, in the same process and session.
212
+ * - `'unsupported'` — the run is live, but its agent has no way to take input mid-run (its prompt
213
+ * is plain text or an RPC request). The host should interrupt and send it as the next turn.
214
+ * - `'not-running'` — no live process will accept it: an unknown run, a finished one, or one whose
215
+ * last turn already ended and closed stdin. The run is ending; the host should send it next.
216
+ */
217
+ export type UserMessageDelivery = 'delivered' | 'unsupported' | 'not-running';
198
218
  /** Process discovery is a getter; process collection and stopping accept explicit host inputs. */
199
219
  export type CollectProcessTreePidsPort = (args: {
200
220
  readonly processes: ProcessSnapshot[];
@@ -212,11 +232,11 @@ export interface TerminateChildTreeDeps {
212
232
  * Which step reported a contained failure through `onCleanupFailure` (SEC-007).
213
233
  *
214
234
  * The first three come from {@link terminateChildTreeBestEffort} (process-tree teardown). The last
215
- * two are the two fallible steps that sit between a child's `'close'` and `finish()` — staged-file
216
- * removal and the host's own `classifyFailure` — neither of which may prevent the terminal
217
- * transition, and neither of which may fail silently either. See each close handler.
235
+ * three cover tool cancellation, staged-file removal and the host's own `classifyFailure`.
236
+ * These fallible steps between a child's `'close'` and `finish()` must never prevent the terminal
237
+ * transition, and none may fail silently either. See each close handler.
218
238
  */
219
- export type AgentCleanupFailurePhase = 'cancel' | 'acp-attach-failure' | 'pi-rpc-attach-failure' | 'staged-file-cleanup' | 'failure-classification';
239
+ export type AgentCleanupFailurePhase = 'cancel' | 'acp-attach-failure' | 'pi-rpc-attach-failure' | 'tool-cancellation' | 'staged-file-cleanup' | 'failure-classification';
220
240
  export interface AgentCleanupFailureContext {
221
241
  readonly runId: string;
222
242
  readonly phase: AgentCleanupFailurePhase;
@@ -237,6 +257,8 @@ export interface StdinCloseHandle {
237
257
  closeStdinOnce(): void;
238
258
  /** Journals a sent-to-stdin byte chunk, queued through the same FIFO {@link wireChildLifecycle} already uses for emitted events. No-op when no journal was configured (see `CreateAgentExecutorOptions.journal`). */
239
259
  recordSentBytes(content: string): void;
260
+ /** Interrupts this child's turn, then writes a mid-run user message into its open stdin — see {@link AgentExecutor.sendUserMessage}. */
261
+ sendUserMessage(text: string): Promise<UserMessageDelivery>;
240
262
  }
241
263
  /**
242
264
  * Gap 3 (capability-routed continuation transport) — host-owned config for the
@@ -374,6 +396,13 @@ export interface McpJsonInjectionOptions {
374
396
  readonly removeDir?: ({ path }: {
375
397
  readonly path: string;
376
398
  }) => Promise<void>;
399
+ /** `'codex-toml'` only. Links the run home's sessions/archived_sessions to the operator's
400
+ * persistent rollout store. Config and auth remain isolated and are removed after the run.
401
+ * @default Native mkdir (0700) and directory symlinks; errors fail staging before spawn. */
402
+ readonly linkSessionStore?: (required: {
403
+ runHome: string;
404
+ sourceHome: string;
405
+ }, optional?: {}) => Promise<void>;
377
406
  }
378
407
  /** One `.mcp.json` `mcpServers` entry — the shape Claude Code's own config schema expects. */
379
408
  export interface McpJsonServerEntry {
@@ -543,6 +572,8 @@ export interface FailureClassificationContext {
543
572
  export type ClassifyFailure = (context: FailureClassificationContext) => boolean | Promise<boolean>;
544
573
  export interface CreateAgentExecutorOptions {
545
574
  readonly lifecycle: RunLifecycle;
575
+ /** Resolves/pins the concrete starting model against the actual launch scope. */
576
+ readonly resolveModelForLaunch?: typeof resolveModelForLaunch;
546
577
  /** @default the real `@jini-ai/agent-runtime` registry lookup */
547
578
  readonly getAgentDef?: typeof getAgentDef;
548
579
  /** @default the real `@jini-ai/agent-runtime` launch resolver */
@@ -1,12 +1,3 @@
1
- import {} from 'node:child_process';
2
- import {} from '@jini-ai/core';
3
- import {} from '@jini-ai/protocol';
4
- import {} from '@jini-ai/agent-runtime';
5
- import {} from '@jini-ai/platform';
6
- import {} from '../continuation/journal.js';
7
- import {} from '../run/core/index.js';
8
- import {} from '../tool-executor.js';
9
- import {} from '../run-lifecycle.js';
10
1
  export const SUPPORTED_STREAM_FORMATS = [
11
2
  'claude-stream-json',
12
3
  'json-event-stream',
@@ -1,5 +1,5 @@
1
- import { type RunAgentPayload, type RunErrorPayload } from '@jini-ai/protocol';
2
- import { type AgentRuntimeEventTranslation } from './contracts.js';
1
+ import type { RunAgentPayload, RunErrorPayload } from '@jini-ai/protocol';
2
+ import type { AgentRuntimeEventTranslation } from './contracts.js';
3
3
  /**
4
4
  * Narrows a parsed `usage` event's `usage` sub-object (`{input_tokens?, output_tokens?}`) — the one
5
5
  * piece of {@link translateUsagePayload} with real nested branching (an optional container holding
@@ -1,5 +1,3 @@
1
- import {} from '@jini-ai/protocol';
2
- import {} from './contracts.js';
3
1
  import { isRecord, asString, asOptionalString, } from './values.js';
4
2
  function asOptionalNumber(value) {
5
3
  return typeof value === 'number' ? value : undefined;
@@ -1,8 +1,9 @@
1
- import { type ChildProcess } from 'node:child_process';
1
+ import type { ModelCatalogSnapshot } from '@jini-ai/agent-runtime';
2
+ import type { ChildProcess } from 'node:child_process';
2
3
  import { attachAcpSession, attachPiRpcSession, type AcpPermissionHandler, type RuntimeAgentDef } from '@jini-ai/agent-runtime';
3
- import { type RunByteJournal } from '../continuation/journal.js';
4
- import { type RunLifecycle } from '../run-lifecycle.js';
5
- import { type ChildDrivenStreamFormat, type TerminateChildTreeDeps, type AgentCleanupFailureContext, type StdinCloseHandle, type ContinuationOptions, type McpBridgeDelivery, type ClassifyFailure, type FailBeforeSpawn } from './contracts.js';
4
+ import type { RunByteJournal } from '../continuation/journal.js';
5
+ import type { RunLifecycle } from '../run-lifecycle.js';
6
+ import type { ChildDrivenStreamFormat, TerminateChildTreeDeps, AgentCleanupFailureContext, StdinCloseHandle, ContinuationOptions, McpBridgeDelivery, ClassifyFailure, FailBeforeSpawn } from './contracts.js';
6
7
  /** Default sink when a host does not supply `onCleanupFailure`: still observable, never silent. Redacted per SEC-007 (a spawn/permission error can embed paths/host detail). */
7
8
  export declare function defaultCleanupFailureSink(context: AgentCleanupFailureContext): void;
8
9
  /**
@@ -22,6 +23,8 @@ export declare function defaultCleanupFailureSink(context: AgentCleanupFailureCo
22
23
  */
23
24
  export declare const DEFAULT_BUFFERED_STDOUT_MAX_BYTES: number;
24
25
  interface WireChildLifecycleContext extends TerminateChildTreeDeps {
26
+ readonly startingModel?: string;
27
+ readonly modelCatalog?: ModelCatalogSnapshot;
25
28
  readonly runId: string;
26
29
  readonly def: RuntimeAgentDef;
27
30
  readonly streamFormat: ChildDrivenStreamFormat;
@@ -115,6 +118,21 @@ interface WireChildLifecycleContext extends TerminateChildTreeDeps {
115
118
  * @overallScore 100/100
116
119
  */
117
120
  export declare function wireChildLifecycle(ctx: WireChildLifecycleContext): StdinCloseHandle;
121
+ /**
122
+ * Whether a def's live process can take another user message mid-run: only a stream-json stdin
123
+ * transport keeps stdin open for more JSONL user lines (see `RuntimeAgentDef.promptInputFormat`).
124
+ * Deliberately not `resolveContinuationTransport`: that answers how a tool result gets back, and
125
+ * reports `'mcp-callback'` for Claude even though its stdin takes user lines all the same.
126
+ */
127
+ export declare function acceptsMidRunUserMessages({ def }: {
128
+ readonly def: RuntimeAgentDef;
129
+ }): boolean;
130
+ /**
131
+ * How long a mid-run message waits for the CLI to acknowledge its interrupt before being written
132
+ * anyway. Claude Code acks within milliseconds (it answers before the aborted turn's own result);
133
+ * the ceiling only bounds an agent that ignores control requests.
134
+ */
135
+ export declare const INTERRUPT_ACK_TIMEOUT_MS = 5000;
118
136
  /**
119
137
  * Writes the initial user turn to the child's stdin per `def.promptInputFormat`
120
138
  * (both branches only run for `promptViaStdin: true` defs — the only shape
@@ -122,8 +140,8 @@ export declare function wireChildLifecycle(ctx: WireChildLifecycleContext): Stdi
122
140
  * - `'text'` (default): the raw prompt buffer, then stdin is closed —
123
141
  * matches `RuntimeAgentDef.promptInputFormat`'s own doc.
124
142
  * - `'stream-json'`: one JSONL line wrapping the prompt as an Anthropic
125
- * user message; stdin is deliberately left open (a real multi-turn
126
- * caller would inject further messages) — v1 has no such caller, so
143
+ * user message; stdin is deliberately left open so a message the human
144
+ * sends mid-run can follow it (`StdinCloseHandle.sendUserMessage`), and
127
145
  * {@link wireChildLifecycle}'s `turn_end` handling closes it once the
128
146
  * agent's own stream reports the turn ended.
129
147
  * @param def - The resolved agent def (only `.promptInputFormat` is read).
@@ -141,6 +159,7 @@ interface RunAcpDispatchInput {
141
159
  readonly prompt: string;
142
160
  readonly cwd: string;
143
161
  readonly model: string | undefined;
162
+ readonly modelCatalog?: ModelCatalogSnapshot;
144
163
  readonly imagePaths: readonly string[];
145
164
  readonly envFormat: 'array' | 'map' | undefined;
146
165
  readonly mcpBridge: McpBridgeDelivery | null;
@@ -168,6 +187,7 @@ interface RunPiRpcDispatchInput {
168
187
  readonly prompt: string;
169
188
  readonly cwd: string;
170
189
  readonly model: string | undefined;
190
+ readonly modelCatalog?: ModelCatalogSnapshot;
171
191
  readonly imagePaths: readonly string[];
172
192
  readonly uploadRoot: string | undefined;
173
193
  }
@@ -1,18 +1,15 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { createModelReceiptTracker } from './model-receipts.js';
1
3
  import { messageContentWithImages } from '../attachment-content.js';
2
- import {} from 'node:child_process';
3
4
  import { redactSecrets, } from '@jini-ai/core';
4
- import {} from '@jini-ai/protocol';
5
5
  import { createClaudeStreamHandler, createCopilotStreamHandler, createJsonEventStreamHandler, createQoderStreamHandler, attachAcpSession, attachPiRpcSession, } from '@jini-ai/agent-runtime';
6
6
  import { collectProcessTreePids, listProcessSnapshots, stopProcesses, } from '@jini-ai/platform';
7
7
  import { classifyRunCloseStatus, } from '../close-status.js';
8
8
  import { resolveContinuationTransport, } from '../continuation/continuation-transport.js';
9
- import {} from '../continuation/journal.js';
10
9
  import { resultContent, } from '../delegated-tool-bridge.js';
11
10
  import { extractResultMedia, } from '../tool-result-media.js';
12
- import {} from '../run-lifecycle.js';
13
- import {} from './contracts.js';
14
11
  import { translateAgentRuntimeEvent, translateAcpError, applyAgentTranslationSideEffects, } from './event-translation.js';
15
- import { errorMessage, } from './values.js';
12
+ import { errorMessage, isRecord, } from './values.js';
16
13
  import { MCP_BRIDGE_UNAVAILABLE, unavailableJiniBridgeStatus, bridgeUnavailableMessage, } from './mcp-bridge.js';
17
14
  /** Carry PID only on structured status so the host can record its OS start identity early.
18
15
  * A PID alone is never proof of death; hosts verify PID reuse before native continuation. */
@@ -72,7 +69,7 @@ export function defaultCleanupFailureSink(context) {
72
69
  /**
73
70
  * Reports a contained post-close failure through the host's sink, absorbing a throwing sink.
74
71
  *
75
- * A diagnostic sink is host code too, and the whole point of the two callers below is that nothing
72
+ * A diagnostic sink is host code too, and the whole point of these callers is that nothing
76
73
  * between `'close'` and `finish()` can strand the run — a sink that throws must not reintroduce
77
74
  * exactly that. Same reasoning `run-lifecycle.ts`'s `handleInactivityTimeout` already applies to its
78
75
  * own `onInternalError`.
@@ -260,6 +257,7 @@ function bufferedStdoutTruncationNotice(droppedBytes, maxBytes) {
260
257
  */
261
258
  export function wireChildLifecycle(ctx) {
262
259
  const { runId, def, streamFormat, child, lifecycle, journal, continuation, classifyFailure } = ctx;
260
+ const modelReceipt = createModelReceiptTracker(ctx.startingModel, ctx.modelCatalog?.models);
263
261
  let stdinClosed = false;
264
262
  let cancelRequested = false;
265
263
  let emitQueue = Promise.resolve();
@@ -276,6 +274,37 @@ export function wireChildLifecycle(ctx) {
276
274
  // consumed by a turn-end injection decision. See `ContinuationOptions`'s doc for why this is
277
275
  // only ever acted on when a host has explicitly allowlisted the tool's name.
278
276
  let pendingToolUse;
277
+ // Observe the canonical lifecycle, including delegated calls that bypass the CLI parser.
278
+ // A single latest tool_use cannot represent parallel tools at an interrupt boundary.
279
+ const unresolvedToolCalls = new Set();
280
+ const interruptedToolCalls = new Set();
281
+ const toolSubscription = lifecycle.stream({ runId, onEvent: (event) => {
282
+ if (event.kind !== 'agent')
283
+ return;
284
+ if (event.payload.type === 'tool_use')
285
+ unresolvedToolCalls.add(event.payload.id);
286
+ if (event.payload.type === 'tool_result')
287
+ unresolvedToolCalls.delete(event.payload.toolUseId);
288
+ } }).catch(error => {
289
+ // Observing tools is fallible I/O too: report it, but never strand close/finish on a replay
290
+ // failure or leave an unhandled rejection while the child is still running.
291
+ reportPostCloseFailure(ctx.onCleanupFailure, { runId, phase: 'tool-cancellation', pid: child.pid, error });
292
+ return null;
293
+ });
294
+ /** Run on the same FIFO as parsed results, after the aborted segment has drained.
295
+ * O(t) in unresolved calls; completed results are never replaced with cancellation. */
296
+ async function cancelUnresolvedToolCalls(ids) {
297
+ await toolSubscription;
298
+ for (const toolUseId of ids ?? [...unresolvedToolCalls]) {
299
+ if (!unresolvedToolCalls.has(toolUseId))
300
+ continue;
301
+ await lifecycle.emit({ runId, input: { event: 'agent', data: {
302
+ type: 'tool_result', toolUseId, content: 'Tool execution cancelled.', isError: true,
303
+ } } }).catch(error => {
304
+ reportPostCloseFailure(ctx.onCleanupFailure, { runId, phase: 'tool-cancellation', pid: child.pid, error });
305
+ });
306
+ }
307
+ }
279
308
  // Bridge guard (see `expectsJiniBridge`): checked on the first init frame only. Once the bridge is
280
309
  // found unavailable, the child is being stopped and nothing it still prints is forwarded.
281
310
  let bridgeChecked = !ctx.expectsJiniBridge;
@@ -306,12 +335,43 @@ export function wireChildLifecycle(ctx) {
306
335
  }
307
336
  });
308
337
  }
338
+ if (ctx.startingModel)
339
+ enqueueEmit(() => lifecycle.emit({ runId, input: { event: 'agent', data: { type: 'status', label: 'starting_model', model: ctx.startingModel } } }));
309
340
  function closeStdinOnce() {
310
341
  if (stdinClosed)
311
342
  return;
312
343
  stdinClosed = true;
313
344
  child.stdin?.end();
314
345
  }
346
+ // Mid-run interrupts (see `sendUserMessage` below): each sent `interrupt` control request waits
347
+ // here, by request id, for the CLI's `control_response`. Settled with `true` on the ack, `false`
348
+ // when the process exits first. Bounded by the mid-run messages in flight on this one run.
349
+ const pendingInterruptAcks = new Map();
350
+ // Set once this run has interrupted its agent: from then on a turn the interrupt aborted
351
+ // (`aborted_*` stop reason) is not the run's last — the message that follows starts a new one.
352
+ let interruptSent = false;
353
+ // stdout carry-over for spotting acks; only filled while an interrupt waits for one.
354
+ let ackScanBuffer = '';
355
+ /** Resolves every waiting interrupt whose `control_response` is in this stdout chunk. @complexity O(chunk length). */
356
+ function scanForInterruptAcks(text) {
357
+ if (pendingInterruptAcks.size === 0)
358
+ return;
359
+ ackScanBuffer += text;
360
+ const lines = ackScanBuffer.split('\n');
361
+ ackScanBuffer = lines.pop() ?? '';
362
+ for (const line of lines) {
363
+ if (!line.includes('"control_response"'))
364
+ continue;
365
+ const requestId = controlResponseRequestId({ line: line });
366
+ const settle = requestId === undefined ? undefined : pendingInterruptAcks.get(requestId);
367
+ if (!settle)
368
+ continue;
369
+ pendingInterruptAcks.delete(requestId);
370
+ settle(true);
371
+ }
372
+ if (pendingInterruptAcks.size === 0)
373
+ ackScanBuffer = '';
374
+ }
315
375
  /**
316
376
  * Writes a structured (never string-concatenated — see `ContinuationOptions`'s doc on the
317
377
  * prompt-injection stakes here) tool_result JSONL line, mirroring the shape
@@ -331,6 +391,21 @@ export function wireChildLifecycle(ctx) {
331
391
  if (journal)
332
392
  enqueueEmit(() => journal.record({ runId: runId, entry: sentJournalEntry(content) }));
333
393
  }
394
+ /**
395
+ * Whether a `turn_end` leaves stdin open for a live agent that takes mid-run messages, because
396
+ * the run is not over: a `tool_use` stop (the CLI runs the tool itself and carries on, and a
397
+ * message sent while the tool runs must still reach it), any end while an interrupt still waits
398
+ * for its ack (the message is about to be written), and the end of a turn an interrupt aborted
399
+ * (the message written after it starts the next turn). Every other end closes stdin, so the CLI
400
+ * exits once it has answered everything written before EOF.
401
+ */
402
+ function keepsStdinOpenAfterTurnEnd(stopReason) {
403
+ if (!acceptsMidRunUserMessages({ def: def }))
404
+ return false;
405
+ if (stopReason === 'tool_use' || pendingInterruptAcks.size > 0)
406
+ return true;
407
+ return interruptSent && stopReason !== undefined && stopReason.startsWith('aborted');
408
+ }
334
409
  /**
335
410
  * Decides, per `turn_end`, whether to auto-resolve a pending tool_use through the injected
336
411
  * `ToolExecutor` and keep stdin open (gap 3), or close stdin exactly as every version of this
@@ -338,6 +413,11 @@ export function wireChildLifecycle(ctx) {
338
413
  * unconfigured or the pending tool isn't allowlisted).
339
414
  */
340
415
  function handleTurnEnd(stopReason) {
416
+ if (stopReason?.startsWith('aborted'))
417
+ enqueueEmit(async () => {
418
+ await cancelUnresolvedToolCalls([...interruptedToolCalls]);
419
+ interruptedToolCalls.clear();
420
+ });
341
421
  const toolUse = pendingToolUse;
342
422
  const shouldInject = stopReason === 'tool_use' &&
343
423
  toolUse !== undefined &&
@@ -345,7 +425,8 @@ export function wireChildLifecycle(ctx) {
345
425
  resolveContinuationTransport({ def: def }) === 'stdin-injection' &&
346
426
  continuation.autonomousToolNames.has(toolUse.name);
347
427
  if (!shouldInject) {
348
- closeStdinOnce();
428
+ if (!keepsStdinOpenAfterTurnEnd(stopReason))
429
+ closeStdinOnce();
349
430
  return;
350
431
  }
351
432
  pendingToolUse = undefined;
@@ -399,6 +480,9 @@ export function wireChildLifecycle(ctx) {
399
480
  return;
400
481
  const translation = translateAgentRuntimeEvent({ rawEvent: rawEvent });
401
482
  if (translation.kind === 'agent') {
483
+ const receipt = modelReceipt(translation.payload);
484
+ if (receipt)
485
+ enqueueEmit(() => lifecycle.emit({ runId, input: { event: 'agent', data: receipt } }));
402
486
  if (translation.sessionId !== undefined)
403
487
  capturedSessionId = translation.sessionId;
404
488
  if (!bridgeChecked && translation.payload.type === 'status' && translation.payload.label === 'initializing') {
@@ -495,6 +579,9 @@ export function wireChildLifecycle(ctx) {
495
579
  return;
496
580
  }
497
581
  enqueueEmit(() => lifecycle.emit({ runId: runId, input: { event: 'stdout', data: { chunk: text } } }));
582
+ // Before the parser: an ack and the aborted turn's end can share a chunk, and the turn end must
583
+ // see that no interrupt is still waiting (see `keepsStdinOpenAfterTurnEnd`).
584
+ scanForInterruptAcks(text);
498
585
  // Non-null: `streamHandler` is only ever null when `streamFormat === 'plain'` (see its
499
586
  // construction above), the branch this statement is provably unreachable from.
500
587
  streamHandler.feed({ chunk: text });
@@ -520,6 +607,9 @@ export function wireChildLifecycle(ctx) {
520
607
  void terminateChildTreeBestEffort(ctx, child, runId, 'cancel', ctx.onCleanupFailure);
521
608
  } });
522
609
  child.on('close', (code, signal) => {
610
+ for (const settle of pendingInterruptAcks.values())
611
+ settle(false);
612
+ pendingInterruptAcks.clear();
523
613
  void (async () => {
524
614
  // Not wrapped in try/catch: all 4 supported parser factories'
525
615
  // flush() implementations already internally guard their own
@@ -534,6 +624,11 @@ export function wireChildLifecycle(ctx) {
534
624
  // `finish()`'s `'end'` event, exactly like every live-path emit already is.
535
625
  flushBufferedStdout();
536
626
  await emitQueue;
627
+ if (cancelRequested)
628
+ await cancelUnresolvedToolCalls();
629
+ const subscribedTools = await toolSubscription;
630
+ if (subscribedTools?.kind === 'ok')
631
+ subscribedTools.unsubscribe();
537
632
  unsubscribeCancel();
538
633
  // Both of the next two steps are guarded: neither a failed cleanup nor a rejecting host
539
634
  // classifier may prevent the terminal transition below — see each helper's own doc.
@@ -552,14 +647,92 @@ export function wireChildLifecycle(ctx) {
552
647
  await lifecycle.finish({ runId, status, code, signal: signal ?? null, resumable }, { ...(capturedSessionId !== undefined ? { sessionRef: capturedSessionId } : {}) });
553
648
  })();
554
649
  });
650
+ /**
651
+ * A mid-run message: first an `interrupt` control request on the still-open stdin, so the agent
652
+ * stops generating (or abandons the tool it is running) at once instead of finishing its whole
653
+ * reply; then, once the CLI acknowledges, the message as the next user line, which the CLI
654
+ * answers straight away in the same session; then the run's own `user_message` event through the
655
+ * same FIFO as the agent's output, so the message sits in the stream where it took effect.
656
+ * An ack that never comes (an agent that ignores control requests) only delays the write by
657
+ * {@link INTERRUPT_ACK_TIMEOUT_MS}: the CLI then folds the line in at its next step. Once
658
+ * `turn_end` has closed stdin, or the process exits before the ack, there is no session left to
659
+ * reach — `'not-running'`.
660
+ */
661
+ async function sendUserMessage(text) {
662
+ if (!acceptsMidRunUserMessages({ def: def }))
663
+ return 'unsupported';
664
+ const stdin = child.stdin;
665
+ if (stdinClosed || !stdin)
666
+ return 'not-running';
667
+ const requestId = `jini-interrupt-${randomUUID()}`;
668
+ interruptSent = true;
669
+ // Capture before requesting the interrupt. Its result can precede the acknowledgement, or
670
+ // arrive after the next segment starts: neither ordering may cancel that segment's new tools.
671
+ enqueueEmit(async () => {
672
+ await toolSubscription;
673
+ for (const id of interruptedToolCalls)
674
+ if (!unresolvedToolCalls.has(id))
675
+ interruptedToolCalls.delete(id);
676
+ for (const id of unresolvedToolCalls)
677
+ interruptedToolCalls.add(id);
678
+ });
679
+ const acked = new Promise((resolve) => pendingInterruptAcks.set(requestId, resolve));
680
+ stdin.write(`${JSON.stringify({ type: 'control_request', request_id: requestId, request: { subtype: 'interrupt' } })}\n`, 'utf8');
681
+ let timer;
682
+ const timedOut = new Promise((resolve) => {
683
+ timer = setTimeout(() => resolve(true), INTERRUPT_ACK_TIMEOUT_MS);
684
+ });
685
+ const processAlive = await Promise.race([acked, timedOut]);
686
+ clearTimeout(timer);
687
+ pendingInterruptAcks.delete(requestId);
688
+ if (!processAlive || stdinClosed)
689
+ return 'not-running';
690
+ stdin.write(`${JSON.stringify(streamJsonUserMessage({ content: [{ type: 'text', text: text }] }))}\n`, 'utf8');
691
+ if (journal)
692
+ enqueueEmit(() => journal.record({ runId: runId, entry: sentJournalEntry(text) }));
693
+ enqueueEmit(() => lifecycle.emit({ runId: runId, input: { event: 'agent', data: { type: 'user_message', id: randomUUID(), text: text } } }));
694
+ return 'delivered';
695
+ }
555
696
  return {
556
697
  closeStdinOnce,
557
698
  recordSentBytes(content) {
558
699
  if (journal)
559
700
  enqueueEmit(() => journal.record({ runId: runId, entry: sentJournalEntry(content) }));
560
701
  },
702
+ sendUserMessage,
561
703
  };
562
704
  }
705
+ /**
706
+ * Whether a def's live process can take another user message mid-run: only a stream-json stdin
707
+ * transport keeps stdin open for more JSONL user lines (see `RuntimeAgentDef.promptInputFormat`).
708
+ * Deliberately not `resolveContinuationTransport`: that answers how a tool result gets back, and
709
+ * reports `'mcp-callback'` for Claude even though its stdin takes user lines all the same.
710
+ */
711
+ export function acceptsMidRunUserMessages({ def }) {
712
+ return def.promptViaStdin === true && def.promptInputFormat === 'stream-json';
713
+ }
714
+ /**
715
+ * How long a mid-run message waits for the CLI to acknowledge its interrupt before being written
716
+ * anyway. Claude Code acks within milliseconds (it answers before the aborted turn's own result);
717
+ * the ceiling only bounds an agent that ignores control requests.
718
+ */
719
+ export const INTERRUPT_ACK_TIMEOUT_MS = 5_000;
720
+ /** The `request_id` of a stream-json `control_response` line, or `undefined` for anything else. */
721
+ function controlResponseRequestId({ line }) {
722
+ try {
723
+ const parsed = JSON.parse(line);
724
+ if (!isRecord(parsed) || parsed.type !== 'control_response' || !isRecord(parsed.response))
725
+ return undefined;
726
+ return typeof parsed.response.request_id === 'string' ? parsed.response.request_id : undefined;
727
+ }
728
+ catch {
729
+ return undefined;
730
+ }
731
+ }
732
+ /** The stream-json stdin envelope for one user message — the initial prompt and every mid-run one. */
733
+ function streamJsonUserMessage({ content }) {
734
+ return { type: 'user', message: { role: 'user', content: content } };
735
+ }
563
736
  /**
564
737
  * Wires an ACP child to a run. Unlike the JSON-stream path, ACP owns the
565
738
  * prompt protocol and reports its parsed events through `attachAcpSession`'s
@@ -570,6 +743,7 @@ export function wireChildLifecycle(ctx) {
570
743
  * success.
571
744
  */
572
745
  function wireAcpLifecycle(ctx) {
746
+ const modelReceipt = createModelReceiptTracker(ctx.model, ctx.modelCatalog?.models);
573
747
  const { runId, agentId, child, lifecycle, journal, classifyFailure } = ctx;
574
748
  let cancelRequested = false;
575
749
  let emitQueue = Promise.resolve();
@@ -589,6 +763,8 @@ function wireAcpLifecycle(ctx) {
589
763
  }
590
764
  });
591
765
  }
766
+ if (ctx.model)
767
+ enqueueEmit(() => lifecycle.emit({ runId, input: { event: 'agent', data: { type: 'status', label: 'starting_model', model: ctx.model } } }));
592
768
  child.stdout?.on('data', (chunk) => {
593
769
  const text = chunk.toString('utf8');
594
770
  if (journal)
@@ -632,6 +808,9 @@ function wireAcpLifecycle(ctx) {
632
808
  if (event === 'agent') {
633
809
  const translation = translateAgentRuntimeEvent({ rawEvent: payload });
634
810
  if (translation.kind === 'agent') {
811
+ const receipt = modelReceipt(translation.payload);
812
+ if (receipt)
813
+ enqueueEmit(() => lifecycle.emit({ runId, input: { event: 'agent', data: receipt } }));
635
814
  applyAgentTranslationSideEffects({ payload: translation.payload, sessionId: translation.sessionId, sink: {
636
815
  onSessionId: ({ sessionId }) => { capturedSessionId = sessionId; },
637
816
  onToolCall: () => { toolCallSeen = true; },
@@ -674,6 +853,7 @@ function wireAcpLifecycle(ctx) {
674
853
  * multi-turn tool continuation, resumable session ids, etc.).
675
854
  */
676
855
  function wirePiRpcLifecycle(ctx) {
856
+ const modelReceipt = createModelReceiptTracker(ctx.model, ctx.modelCatalog?.models);
677
857
  const { runId, agentId, child, lifecycle, journal, classifyFailure } = ctx;
678
858
  let cancelRequested = false;
679
859
  let emitQueue = Promise.resolve();
@@ -693,6 +873,8 @@ function wirePiRpcLifecycle(ctx) {
693
873
  }
694
874
  });
695
875
  }
876
+ if (ctx.model)
877
+ enqueueEmit(() => lifecycle.emit({ runId, input: { event: 'agent', data: { type: 'status', label: 'starting_model', model: ctx.model } } }));
696
878
  child.stdout?.on('data', (chunk) => {
697
879
  const text = chunk.toString('utf8');
698
880
  if (journal)
@@ -735,6 +917,9 @@ function wirePiRpcLifecycle(ctx) {
735
917
  session = ctx.attachPiRpcSession({ child: ctx.child, prompt: ctx.prompt, send({ payload }) {
736
918
  const translation = translateAgentRuntimeEvent({ rawEvent: payload });
737
919
  if (translation.kind === 'agent') {
920
+ const receipt = modelReceipt(translation.payload);
921
+ if (receipt)
922
+ enqueueEmit(() => lifecycle.emit({ runId, input: { event: 'agent', data: receipt } }));
738
923
  if (translation.sessionId !== undefined)
739
924
  capturedSessionId = translation.sessionId;
740
925
  if (translation.payload.type === 'tool_use') {
@@ -759,8 +944,8 @@ function wirePiRpcLifecycle(ctx) {
759
944
  * - `'text'` (default): the raw prompt buffer, then stdin is closed —
760
945
  * matches `RuntimeAgentDef.promptInputFormat`'s own doc.
761
946
  * - `'stream-json'`: one JSONL line wrapping the prompt as an Anthropic
762
- * user message; stdin is deliberately left open (a real multi-turn
763
- * caller would inject further messages) — v1 has no such caller, so
947
+ * user message; stdin is deliberately left open so a message the human
948
+ * sends mid-run can follow it (`StdinCloseHandle.sendUserMessage`), and
764
949
  * {@link wireChildLifecycle}'s `turn_end` handling closes it once the
765
950
  * agent's own stream reports the turn ended.
766
951
  * @param def - The resolved agent def (only `.promptInputFormat` is read).
@@ -775,7 +960,7 @@ export function writePromptToStdin(def, child, prompt, handle) {
775
960
  if (!stdin)
776
961
  return;
777
962
  if (def.promptInputFormat === 'stream-json') {
778
- const line = JSON.stringify({ type: 'user', message: { role: 'user', content: messageContentWithImages({ prompt, images: handle.imageContents ?? [] }, {}) } });
963
+ const line = JSON.stringify(streamJsonUserMessage({ content: messageContentWithImages({ prompt, images: handle.imageContents ?? [] }, {}) }));
779
964
  stdin.write(`${line}\n`, 'utf8');
780
965
  handle.recordSentBytes(prompt);
781
966
  return;
@@ -795,6 +980,7 @@ export async function runAcpDispatch({ input, deps }) {
795
980
  prompt: input.prompt,
796
981
  cwd: input.cwd,
797
982
  model: input.model,
983
+ ...(input.modelCatalog ? { modelCatalog: input.modelCatalog } : {}),
798
984
  imagePaths: input.imagePaths,
799
985
  envFormat: input.envFormat,
800
986
  // Mechanism 2 of 5 — see `WireAcpLifecycleContext.mcpServers`. `undefined` for any def that
@@ -831,6 +1017,7 @@ export async function runPiRpcDispatch({ input, deps }) {
831
1017
  prompt: input.prompt,
832
1018
  cwd: input.cwd,
833
1019
  model: input.model,
1020
+ ...(input.modelCatalog ? { modelCatalog: input.modelCatalog } : {}),
834
1021
  imagePaths: input.imagePaths,
835
1022
  uploadRoot: input.uploadRoot,
836
1023
  attachPiRpcSession: deps.attachPiRpcSession,
@@ -1,4 +1,4 @@
1
- import { type RuntimeAgentDef, type RuntimeLockHold } from '@jini-ai/agent-runtime';
1
+ import type { RuntimeAgentDef, RuntimeLockHold } from '@jini-ai/agent-runtime';
2
2
  /**
3
3
  * Phase 7: acquires a `runtimeLock` def's process-global mutex before `buildArgs` runs — see
4
4
  * `RuntimeLock`'s own doc for the concrete race. A no-op (`undefined`) for the 23 of 24 defs with no
@@ -1,4 +1,3 @@
1
- import {} from '@jini-ai/agent-runtime';
2
1
  /**
3
2
  * Phase 7: acquires a `runtimeLock` def's process-global mutex before `buildArgs` runs — see
4
3
  * `RuntimeLock`'s own doc for the concrete race. A no-op (`undefined`) for the 23 of 24 defs with no
@@ -89,7 +89,7 @@
89
89
  * a second, independently-built (and rejected) classifier that once lived in
90
90
  * this module.
91
91
  */
92
- export { type SupportedStreamFormat, type AgentExecutorCompatibility, type AgentRuntimeEventTranslation, type AgentExecutorErrorCode, AgentExecutorError, type AgentExecutorRunInput, type AgentExecutor, type CollectProcessTreePidsPort, type StopProcessesPort, type AgentCleanupFailurePhase, type AgentCleanupFailureContext, type ContinuationOptions, type McpJsonInjectionOptions, type McpBridgeDelivery, type PreparedCodexHome, type PreparedClaudeConfigDir, type ClaudeConfigDirSeams, type ClaudeConfigDirIsolationOptions, type FailureClassificationContext, type ClassifyFailure, type CreateAgentExecutorOptions, type SystemPromptOverlayDelivery, type PreparedSystemPromptOverlayFile, type SpawnAgentChildProcessResult, } from './contracts.js';
92
+ export { type SupportedStreamFormat, type AgentExecutorCompatibility, type AgentRuntimeEventTranslation, type AgentExecutorErrorCode, AgentExecutorError, type AgentExecutorRunInput, type AgentExecutor, type UserMessageDelivery, type CollectProcessTreePidsPort, type StopProcessesPort, type AgentCleanupFailurePhase, type AgentCleanupFailureContext, type ContinuationOptions, type McpJsonInjectionOptions, type McpBridgeDelivery, type PreparedCodexHome, type PreparedClaudeConfigDir, type ClaudeConfigDirSeams, type ClaudeConfigDirIsolationOptions, type FailureClassificationContext, type ClassifyFailure, type CreateAgentExecutorOptions, type SystemPromptOverlayDelivery, type PreparedSystemPromptOverlayFile, type SpawnAgentChildProcessResult, } from './contracts.js';
93
93
  export { isSupportedStreamFormat, assessAgentExecutorCompatibility, isAgentExecutorSupported, resolveDefAndStreamFormat, resolveImageDeliveryAndArgvBudget, resolveRunEnv, resolveLaunch, stagePromptFile, stageLogFile, computeChildEnv, computeRuntimeContext, buildAgentBuildArgsOptions, resolveSystemPromptOverlayDelivery, buildRunArgs, prepareSystemPromptOverlayFileIfNeeded, guardWindowsCommandLineBudget, spawnAgentChildProcess, isStdinDrivenFormat, confirmChildSpawned, createAgentExecutor, } from './launch.js';
94
94
  export { extractUsageTokens, translateStatusEvent, translateToolResultEvent, translateErrorEvent, translateTurnEndEvent, translateAgentRuntimeEvent, applyAgentTranslationSideEffects, } from './event-translation.js';
95
95
  export { MCP_BRIDGE_UNAVAILABLE, unavailableJiniBridgeStatus, buildMcpJsonServerEntry, mergeMcpJsonContent, buildAcpMcpBridgeServers, mergeEnvContentMcpConfig, mergeEnvContentInstructions, buildCodexMcpServerToml, buildCodexHomeConfigToml, resolveSourceCodexHomeDir, buildMcpBridgeDelivery, resolveSourceClaudeConfigDir, resolveMcpBridgeForRun, writeMcpJsonIfNeeded, prepareCodexHomeIfNeeded, prepareClaudeConfigDirIfNeeded, } from './mcp-bridge.js';
@@ -370,6 +370,6 @@ export declare function confirmChildSpawned({ input, deps }: {
370
370
  * @complexity `run()`'s own setup is O(1); steady-state cost is the chosen stream parser's.
371
371
  * @overallScore 100/100
372
372
  */
373
- export declare function createAgentExecutor(requiredArgs: Pick<CreateAgentExecutorOptions, "lifecycle">, optionalArgs?: Pick<CreateAgentExecutorOptions, "getAgentDef" | "resolveAgentLaunch" | "ensureAgentCapabilities" | "applyAgentLaunchEnv" | "createCommandInvocation" | "spawn" | "attachAcpSession" | "acpPermissionHandler" | "attachPiRpcSession" | "preparePromptFileForAgent" | "prepareAgentLogFile" | "listProcessSnapshots" | "collectProcessTreePids" | "stopProcesses" | "onCleanupFailure" | "journal" | "continuation" | "classifyFailure" | "mcpJsonInjection" | "claudeConfigDirIsolation" | "claudeConfigDirIsolationEnabled" | "bufferedStdoutMaxBytes" | "promptAugmenter">): AgentExecutor;
373
+ export declare function createAgentExecutor(requiredArgs: Pick<CreateAgentExecutorOptions, "lifecycle">, optionalArgs?: Pick<CreateAgentExecutorOptions, "getAgentDef" | "resolveAgentLaunch" | "resolveModelForLaunch" | "ensureAgentCapabilities" | "applyAgentLaunchEnv" | "createCommandInvocation" | "spawn" | "attachAcpSession" | "acpPermissionHandler" | "attachPiRpcSession" | "preparePromptFileForAgent" | "prepareAgentLogFile" | "listProcessSnapshots" | "collectProcessTreePids" | "stopProcesses" | "onCleanupFailure" | "journal" | "continuation" | "classifyFailure" | "mcpJsonInjection" | "claudeConfigDirIsolation" | "claudeConfigDirIsolationEnabled" | "bufferedStdoutMaxBytes" | "promptAugmenter">): AgentExecutor;
374
374
  export {};
375
375
  //# sourceMappingURL=launch.d.ts.map