@robota-sdk/agent-subagent-runner 3.0.0-beta.100

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.
@@ -0,0 +1,543 @@
1
+ import { IConnectionEnvironmentCheck, ISubagentJobHandle, ISubagentJobStart, ISubagentRunner, ISubagentWorktreeAdapter } from "@robota-sdk/agent-executor";
2
+ import { IHookTypeExecutor, IProviderDefinition, IProviderDefinitionConfig, ISessionUsageTotals, IToolWithEventService, TPermissionMode, TToolArgs } from "@robota-sdk/agent-core";
3
+ import { IAgentDefinition, IInProcessSubagentRunnerDeps, IResolvedConfig, ISubagentOptions, ISubagentParentContext, TSubagentRunnerFactory, restoreSessionRecordIntoSession } from "@robota-sdk/agent-framework";
4
+ import { ISerializableProviderProfile, ISubagentSpawnRequest } from "@robota-sdk/agent-interface-execution";
5
+ //#region src/worker-entry.d.ts
6
+ /**
7
+ * DIST-006: how a subagent worker process is STARTED, stated by the composition root.
8
+ *
9
+ * The seam this replaces asked a library "where is my worker file on disk?" — a question it cannot
10
+ * answer, because the answer is a property of the packaging step, not of the library. It was wrong
11
+ * twice for the same reason: once when the worker had no bundle entry at all, and again when a
12
+ * downstream bundler inlined this package into another artifact and moved the resolver's notion of
13
+ * "next to me" one package along.
14
+ *
15
+ * The only party that knows how a process is packaged is that process. So the composition root
16
+ * states how to start a copy of itself, and this package owns nothing but the IPC contract.
17
+ */
18
+ /**
19
+ * The argv flag that puts a composition root's own entry into subagent-worker mode.
20
+ *
21
+ * Deliberately not a plausible user flag: it is part of an internal process contract, and a user
22
+ * who types it gets a loud refusal rather than a half-started worker.
23
+ */
24
+ declare const SUBAGENT_WORKER_MODE_FLAG = "--__agent-subagent-worker";
25
+ /**
26
+ * How to spawn a copy of the running artifact in subagent-worker mode.
27
+ *
28
+ * `execPath` + `args` is the whole contract, and it is satisfiable by every artifact shape:
29
+ * - a bundled Node build names the file it is currently executing;
30
+ * - a `tsx` source run names the same thing and adds `--import tsx` to `execArgv`;
31
+ * - a single-file compiled binary names NOTHING — `process.execPath` is the binary, and
32
+ * re-executing it re-enters its embedded entry.
33
+ */
34
+ interface ISubagentWorkerEntry {
35
+ /** The executable to run. `process.execPath` for every artifact this repository ships. */
36
+ readonly execPath: string;
37
+ /** Arguments before the worker-mode flag — the entry module, or nothing when it is embedded. */
38
+ readonly args: readonly string[];
39
+ /** Extra runtime flags, e.g. `--import tsx` when the entry is TypeScript source. */
40
+ readonly execArgv?: readonly string[];
41
+ }
42
+ /** True when this process was started as a subagent worker. */
43
+ declare function isSubagentWorkerModeArgv(argv: readonly string[]): boolean;
44
+ //#endregion
45
+ //#region src/worker-composition.d.ts
46
+ /**
47
+ * The session record store a fork job's `resumeSessionId` names a record in, typed FROM the one
48
+ * function that reads it (`restoreSessionRecordIntoSession`, agent-framework) rather than from the
49
+ * interface package that declares the port — this package does not depend on that package, and a
50
+ * type derived from the consumer cannot drift from what the consumer accepts.
51
+ */
52
+ type TResumeSessionStore = Parameters<typeof restoreSessionRecordIntoSession>[0];
53
+ /**
54
+ * ARCH-021: what the product composes, stated by the composition root.
55
+ *
56
+ * This is the sibling of {@link ISubagentWorkerEntry} one level up. That seam answers "how is this
57
+ * artifact started"; this one answers "what does this product compose" — and the same rule decides
58
+ * both: **the only party that knows is the product itself.**
59
+ *
60
+ * The seam this replaces had a neutral package importing `createDefaultTools()` and
61
+ * `createDefaultProviderDefinitions()` and building the child's surface from them, while the
62
+ * composition root had already handed the runner the fully composed surface. So a product's custom
63
+ * providers and pack-owned tools reached an in-process subagent and not a child-process one, and
64
+ * ARCH-006's invariant — every tool agent runs comes from a pack — was false in the child.
65
+ *
66
+ * **Why a recipe rather than the instances.** A composition cannot be projected across a process
67
+ * boundary, because it is code: `createProvider` is a function and a tool carries `execute`. The two
68
+ * structurally sound answers are to proxy the instances or to stop expressing the contract as
69
+ * instances. Proxying loses on containment — a proxied tool executes in the PARENT, bound to the
70
+ * parent's checkout, while a worktree-isolated child's execution root is a different directory. So
71
+ * the recipe crosses and the child builds an equivalent surface at its own root, which is what every
72
+ * comparable product does.
73
+ */
74
+ interface ISubagentWorkerComposition {
75
+ /** Product-selected hooks receive the same execution boundary as the child tools. */
76
+ createHookTypeExecutors?: (context: {
77
+ readonly cwd: string;
78
+ readonly sandboxClient?: TProjectedSandboxClient;
79
+ }) => IHookTypeExecutor[];
80
+ /**
81
+ * The product's tool surface for THIS subagent's execution root.
82
+ *
83
+ * `cwd` is a required argument for the same reason `ICreateDefaultToolsOptions.cwd` is (ARCH-010):
84
+ * a tool set built without its root carries a disarmed path guard, and the measured consequence
85
+ * was a subagent `Read` returning `/etc/hostname`. Passing the root through the call rather than
86
+ * capturing it in the factory is what stops a child from inheriting the parent's.
87
+ */
88
+ createTools(context: {
89
+ readonly cwd: string;
90
+ /**
91
+ * ARCH-034: the tiers session assembly adds ON TOP of the product's tool set.
92
+ *
93
+ * The two runners of `ISubagentRunner` were handing a subagent different surfaces, and the
94
+ * difference was silent because both paths succeed. In-process passes the parent's fully
95
+ * ASSEMBLED tools; this path rebuilds the product's set at the child's root. For a product whose
96
+ * packs own the tool surface those agree — but what session assembly adds AFTER the packs did
97
+ * not cross: the goal tool (`includeGoalTool`) and edit-checkpoint wrapping.
98
+ *
99
+ * Choosing a runner is an isolation and packaging decision. It is not supposed to be a capability
100
+ * decision, so the composition root states which of those tiers the child should also receive and
101
+ * the recipe carries the answer rather than the parent's live wrappers.
102
+ */
103
+ readonly sessionTiers?: {
104
+ /** Whether the parent's session included the goal-status tool. */
105
+ readonly includeGoalTool?: boolean;
106
+ };
107
+ /**
108
+ * ARCH-033: the sandbox the child restored, when the parent projected one.
109
+ *
110
+ * Threaded rather than captured, for the same reason `cwd` is: a tool surface built without the
111
+ * sandbox it is supposed to act in would run on the HOST while the parent runs sandboxed, which
112
+ * is the divergence the composition root's refusal exists to prevent. Absent ⇒ no sandbox, and
113
+ * the child's tools act on its own confined root.
114
+ */
115
+ readonly sandboxClient?: TProjectedSandboxClient;
116
+ }): IToolWithEventService[];
117
+ /**
118
+ * The product's provider registry. Carried as definitions rather than a constructed provider
119
+ * because `createProvider` is code — the child builds its own provider from the serialized profile
120
+ * against THIS registry, so a custom provider type resolves instead of throwing `Unknown provider`.
121
+ */
122
+ readonly providerDefinitions: readonly IProviderDefinition[];
123
+ /**
124
+ * How the child rebuilds a SANDBOX that the parent is running in (ARCH-033).
125
+ *
126
+ * The same shape as `providerDefinitions`, and for the same reason. A live `ISandboxClient` is an
127
+ * open session against a remote machine; it cannot cross a process boundary. What CAN cross is the
128
+ * pair (which client type, which snapshot) — `ISandboxClient.snapshot()` returns a
129
+ * provider-owned reference and `restore(id)` hydrates a fresh client from it, and a reference is
130
+ * just a string.
131
+ *
132
+ * So the composition root registers the constructor by type name, exactly as it registers provider
133
+ * definitions, and the recipe carries `{ type, snapshotId }`. The child looks the type up here and
134
+ * restores. Neither half works alone: a snapshot with no registry is a reference nothing can open,
135
+ * and a registry with no snapshot rebuilds an EMPTY sandbox, which is worse than refusing because
136
+ * the child would look sandboxed while sharing none of the parent's state.
137
+ *
138
+ * Absent ⇒ the product composes no sandbox, and `assertChildProcessSubagentsCanReproduce` in the
139
+ * composition root refuses to start a sandboxed parent that cannot project. That refusal remains
140
+ * the correct behaviour for a product that has not registered a factory; this seam is what lets one
141
+ * stop refusing.
142
+ */
143
+ readonly sandboxFactories?: Readonly<Record<string, TSandboxClientFactory>>;
144
+ /**
145
+ * CLI-1994: how the child opens the session store a fork job's record was written to.
146
+ *
147
+ * The same shape as `providerDefinitions` and `sandboxFactories`, and for the same reason: where a
148
+ * product keeps its session records is the composition root's knowledge, and it cannot be
149
+ * projected onto the wire without also projecting the records — which is exactly what ARCH-044
150
+ * keeps off it. So the parent sends the id, and the child asks the composition to open the store
151
+ * FOR THE PARENT'S cwd (`request.cwd`, not the execution root — a worktree-isolated child runs in
152
+ * a directory that has no session records of its own).
153
+ *
154
+ * Absent ⇒ the product composes no store, and a job that names a record to resume fails, stated
155
+ * as such, rather than starting with an empty conversation that looks like a fork.
156
+ */
157
+ readonly openSessionStore?: (context: {
158
+ readonly cwd: string;
159
+ }) => TResumeSessionStore;
160
+ /**
161
+ * A sandbox the child composes itself at its execution root, rather than restoring one from a
162
+ * snapshot: the OS sandbox is a function of the root and its settings, and the settings cross as
163
+ * data (`parentSettings`, the parent's as they stood at spawn) where a live handle cannot.
164
+ *
165
+ * The worker builds it once and hands the SAME instance to `createTools` (as `sandboxClient`) and to
166
+ * the session (as `commandSandbox`), so the approval a confined command gets comes from the instance
167
+ * the command runs under. Absent ⇒ the child's session approves no command on a sandbox's say-so.
168
+ */
169
+ readonly createSandbox?: (context: {
170
+ readonly cwd: string;
171
+ /** The parent's sandbox settings at spawn, when its composition root sent them. */
172
+ readonly parentSettings?: TParentSandboxSettings;
173
+ }) => ISubagentComposedSandbox | undefined;
174
+ }
175
+ /** A composition root's sandbox settings, as plain data that crosses the process boundary. */
176
+ type TParentSandboxSettings = Readonly<Record<string, unknown>>;
177
+ /** A sandbox the child composed itself: the client its tools run under and the approval it gives. */
178
+ interface ISubagentComposedSandbox {
179
+ readonly client: TProjectedSandboxClient;
180
+ readonly commandSandbox?: ISubagentOptions['commandSandbox'];
181
+ /**
182
+ * Take the parent's settings after a change it made while this child runs (`/sandbox`), so the
183
+ * next command is confined and approved as the parent's would be. Throws on settings it cannot
184
+ * read: the worker then aborts the run and ends it with that error, rather than run on settings the
185
+ * user has replaced.
186
+ */
187
+ readonly applyParentSettings?: (settings: TParentSandboxSettings) => void;
188
+ }
189
+ /**
190
+ * Rebuilds a sandbox client of ONE type from a snapshot reference the parent produced.
191
+ *
192
+ * Deliberately not `() => ISandboxClient`: a factory that cannot receive the reference can only make
193
+ * an empty sandbox, which is the failure mode this seam exists to avoid.
194
+ */
195
+ type TSandboxClientFactory = (snapshotId: string) => Promise<TProjectedSandboxClient>;
196
+ /**
197
+ * What the factory hands back, expressed STRUCTURALLY rather than as `ISandboxClient`.
198
+ *
199
+ * This package is the neutral runner: it depends on `agent-core`, `agent-executor`,
200
+ * `agent-framework`, `agent-interface-execution` and `agent-process` — deliberately not on
201
+ * `agent-tools`, where `ISandboxClient` lives. Importing that type to describe a value this package
202
+ * only ever passes through would add a dependency edge for a pass-through, which is the shape
203
+ * ARCH-021 removed from here on the provider axis.
204
+ *
205
+ * So the seam names the minimum it needs to be honest about — the object is opaque to the runner and
206
+ * meaningful only to the composition root that registered the factory and the tools that receive it.
207
+ */
208
+ type TProjectedSandboxClient = object;
209
+ /**
210
+ * The serializable half — what the parent puts in the recipe.
211
+ *
212
+ * Both fields are required. `type` selects the factory; `snapshotId` is what the parent's
213
+ * `snapshot()` returned. Carrying one without the other is the empty-sandbox failure above.
214
+ */
215
+ interface ISandboxProjection {
216
+ readonly type: string;
217
+ readonly snapshotId: string;
218
+ }
219
+ //#endregion
220
+ //#region src/child-process-subagent-runner.d.ts
221
+ interface IChildProcessSubagentRunnerOptions {
222
+ /**
223
+ * DIST-006: how to start a copy of the running artifact in subagent-worker mode, stated by the
224
+ * composition root. It replaced `workerPath`, which asked this package to locate a file whose
225
+ * location is a property of the packaging step — a question no library can answer, and one that
226
+ * was answered wrongly twice.
227
+ */
228
+ workerEntry: ISubagentWorkerEntry;
229
+ providerConfig?: IProviderDefinitionConfig;
230
+ /**
231
+ * The parent's provider registry. Its defaults complete the connection the child is given, and
232
+ * each definition names the environment its client reads. Required: a job whose provider has no
233
+ * definition here is refused, because its connection cannot be checked.
234
+ */
235
+ providerDefinitions: readonly IProviderDefinition[];
236
+ killGraceMs?: number;
237
+ /**
238
+ * How long a spawned worker may take to signal `ready` before the runner gives up. Injectable so
239
+ * the branch is reachable in a test; without that it is a fix that ships untested.
240
+ */
241
+ handshakeBudgetMs?: number;
242
+ env?: NodeJS.ProcessEnv;
243
+ /** Explicit host snapshot used to resolve this provider; defaults to the live process environment. */
244
+ expectedEnvironment?: NodeJS.ProcessEnv;
245
+ /** A host can supply a curated child environment without inheriting unrelated process secrets. */
246
+ inheritEnvironment?: boolean;
247
+ worktreeIsolation?: boolean;
248
+ worktreeAdapter: ISubagentWorktreeAdapter;
249
+ logsDir?: string;
250
+ /**
251
+ * The owner lets commands see the provider credential variable, so the worker keeps it in its own
252
+ * environment. Absent ⇒ the worker removes it once its provider is built.
253
+ */
254
+ keepProviderCredential?: boolean;
255
+ /**
256
+ * The parent's sandbox settings as they stand now, read at EACH spawn: a setting the user changed
257
+ * this session (`/sandbox`) lives on the parent's live client, not in the files a child would read.
258
+ * The child's `createSandbox` receives the value. Absent ⇒ the child reads its root's settings.
259
+ */
260
+ parentSandboxSettings?: () => TParentSandboxSettings | undefined;
261
+ /**
262
+ * Be told the parent's sandbox settings after each change (`/sandbox`); returns the way to stop.
263
+ * The runner forwards each change to every running child, so a child started before the change
264
+ * follows it too.
265
+ */
266
+ watchParentSandboxSettings?: (listener: (settings: TParentSandboxSettings) => void) => () => void;
267
+ }
268
+ declare function createChildProcessSubagentRunnerFactory(options: IChildProcessSubagentRunnerOptions): TSubagentRunnerFactory;
269
+ declare class ChildProcessSubagentRunner implements ISubagentRunner {
270
+ private readonly deps;
271
+ private readonly workerEntry;
272
+ private readonly killGraceMs;
273
+ private readonly handshakeBudgetMs?;
274
+ private readonly providerConfig?;
275
+ private readonly providerDefinitions;
276
+ private readonly env?;
277
+ private readonly expectedEnvironment?;
278
+ private readonly inheritEnvironment;
279
+ private readonly logsDir?;
280
+ private readonly keepProviderCredential;
281
+ private readonly parentSandboxSettings?;
282
+ private readonly watchParentSandboxSettings?;
283
+ constructor(deps: IInProcessSubagentRunnerDeps, options: IChildProcessSubagentRunnerOptions);
284
+ start(job: ISubagentJobStart): ISubagentJobHandle;
285
+ /**
286
+ * The payload the child is started with. The builder lives in
287
+ * `child-process-subagent-projection.ts` (CLI-1994 moved it there so the ARCH-044 key-set test
288
+ * pins the code that produces it); review of ARCH-033/ARCH-034 is the reason it is a named
289
+ * producer at all — both fields were declared on the wire type, read by the worker, and set by
290
+ * nothing, because this was the only production site that constructs a payload and no test
291
+ * reached it.
292
+ */
293
+ private createStartPayload;
294
+ private forwardSandboxSettingChanges;
295
+ private resolveTranscriptPath;
296
+ /**
297
+ * Issue #3288 §1: how this job's child gets a human's yes/no — the parent session's own approver
298
+ * (`deps.permissionHandler`, the exact function the in-process runner already hands its own child
299
+ * session), bound to a `requester` identity that names this task so a surface can say "Background
300
+ * agent X wants to …" instead of an unattributed prompt. `undefined` when the parent session has no
301
+ * approver at all (print mode / a truly headless run): the result controller then denies every
302
+ * request from the child immediately, the same fail-closed default the enforcer applies with none.
303
+ */
304
+ private permissionApprover;
305
+ }
306
+ //#endregion
307
+ //#region src/subagent-worker-start-dto.d.ts
308
+ /**
309
+ * The parent's loaded-context RUNTIME model (`ILoadedContext`, which the framework barrel does not
310
+ * export). Named here for the encoder/restore signatures only — the wire DTO below never references it.
311
+ */
312
+ type TParentContextModel = IInProcessSubagentRunnerDeps['context'];
313
+ interface ISubagentWorkerAgentDefinitionDto {
314
+ readonly name: string;
315
+ readonly description: string;
316
+ readonly systemPrompt: string;
317
+ readonly model?: string;
318
+ readonly effort?: IAgentDefinition['effort'];
319
+ readonly role?: string;
320
+ readonly maxTurns?: number;
321
+ readonly tools?: readonly string[];
322
+ readonly disallowedTools?: readonly string[];
323
+ }
324
+ interface ISubagentWorkerContextFileEntryDto {
325
+ readonly filePath: string;
326
+ readonly content: string;
327
+ readonly contentHash: string;
328
+ }
329
+ interface ISubagentWorkerParentContextDto {
330
+ readonly agentsMd: string;
331
+ readonly projectNotesMd: string;
332
+ readonly memoryMd?: string;
333
+ readonly taskContext?: string;
334
+ readonly compactInstructions?: string;
335
+ readonly agentsFileEntries?: readonly ISubagentWorkerContextFileEntryDto[];
336
+ readonly projectNotesFileEntries?: readonly ISubagentWorkerContextFileEntryDto[];
337
+ }
338
+ type TDtoDecodeResult<TDto> = {
339
+ readonly ok: true;
340
+ readonly value: TDto;
341
+ } | {
342
+ readonly ok: false;
343
+ readonly reason: string;
344
+ };
345
+ declare function encodeAgentDefinition(definition: IAgentDefinition): ISubagentWorkerAgentDefinitionDto;
346
+ declare function decodeAgentDefinitionDto(value: unknown): TDtoDecodeResult<ISubagentWorkerAgentDefinitionDto>;
347
+ /** Explicit restore in the worker: the DTO's fields are the runtime model's, copied, not aliased. */
348
+ declare function restoreAgentDefinition(dto: ISubagentWorkerAgentDefinitionDto): IAgentDefinition;
349
+ /** Accepts the issue #2317 projection (or anything wider, structurally); only declared fields cross. */
350
+ declare function encodeParentContext(context: ISubagentParentContext): ISubagentWorkerParentContextDto;
351
+ declare function decodeParentContextDto(value: unknown): TDtoDecodeResult<ISubagentWorkerParentContextDto>;
352
+ declare function restoreParentContext(dto: ISubagentWorkerParentContextDto): TParentContextModel;
353
+ //#endregion
354
+ //#region src/child-process-subagent-ipc.d.ts
355
+ type TSubagentWorkerWireValue = string | number | boolean | null | undefined | object;
356
+ /** ARCH-044: the four config members the child reads. See `projectParentConfig`. */
357
+ interface ISubagentWorkerParentConfig {
358
+ readonly provider: {
359
+ readonly model: string;
360
+ };
361
+ readonly permissions: IResolvedConfig['permissions'];
362
+ readonly defaultTrustLevel: IResolvedConfig['defaultTrustLevel'];
363
+ readonly hooks?: IResolvedConfig['hooks'];
364
+ }
365
+ interface ISubagentWorkerStartPayload {
366
+ taskId: string;
367
+ request: ISubagentSpawnRequest;
368
+ /**
369
+ * ARCH-031: the worktree the parent's runner prepared, carried across the fork so the child can
370
+ * answer `subagentExecutionRoot` the same way the parent would. Runner-produced, so it rides beside
371
+ * the request rather than on it.
372
+ *
373
+ * `branch` crosses the fork too, even though nothing reads it here yet: dropping it at the IPC
374
+ * boundary would make the child's view of its own isolated run poorer than the parent's, for no
375
+ * reason other than the absence of a present-day consumer.
376
+ */
377
+ worktree?: {
378
+ readonly path: string;
379
+ readonly branch?: string;
380
+ };
381
+ /** ARCH-044 (issue #2047): a JSON-safe DTO owned here, projected from `IAgentDefinition` by the parent. */
382
+ agentDefinition: ISubagentWorkerAgentDefinitionDto;
383
+ /**
384
+ * ARCH-044 (issue #2047): the config members the child reads, declared here rather than indexed
385
+ * out of the runtime type.
386
+ *
387
+ * It was `IInProcessSubagentRunnerDeps['config']`, so the wire shape was the in-process shape and
388
+ * grew with it — which put the parent's resolved `provider.apiKey` and its `env` map into a second
389
+ * process where nothing read either. Declaring the members means a new field on `IResolvedConfig`
390
+ * does not reach the child by default; `projectParentConfig` is what enforces it at runtime,
391
+ * because structural typing would accept the whole config here.
392
+ */
393
+ parentConfig: ISubagentWorkerParentConfig;
394
+ /**
395
+ * ARCH-044 (issue #2047): a JSON-safe DTO owned here, decoded totally on the child side. The parent
396
+ * fills it from `projectParentContext` (issue #2317): the two context members the child reads —
397
+ * `agentsMd` and `projectNotesMd` — and never the parent's whole `ILoadedContext`, whose file
398
+ * entries carry the full text of every AGENTS.md and CLAUDE.md the parent loaded.
399
+ */
400
+ parentContext: ISubagentWorkerParentContextDto;
401
+ providerProfile: ISerializableProviderProfile;
402
+ /**
403
+ * The destination-deciding environment the parent checked before spawning, sealed so the child can
404
+ * repeat the check before it builds a provider. Values never travel; only a keyed digest does.
405
+ */
406
+ connectionCheck: IConnectionEnvironmentCheck;
407
+ /**
408
+ * ARCH-033: how the child rebuilds the parent's sandbox, as `(type, snapshotId)`.
409
+ *
410
+ * The live client cannot cross a process boundary — it is an open session against a remote machine.
411
+ * This pair can: the type selects a factory the composition root registered, and the snapshot is a
412
+ * provider-owned reference the parent's `snapshot()` returned. Both halves are required, because a
413
+ * snapshot with no registry is a reference nothing opens and a registry with no snapshot rebuilds
414
+ * an EMPTY sandbox — a child that looks sandboxed while sharing none of the parent's state.
415
+ *
416
+ * Absent ⇒ the parent holds no sandbox, which is every product that has not registered one.
417
+ */
418
+ sandboxProjection?: ISandboxProjection;
419
+ /**
420
+ * ARCH-034: which session-assembly tiers the parent's surface carried.
421
+ *
422
+ * A property of the parent's SESSION rather than of the child's root, so it rides on the payload
423
+ * beside the request instead of being derived at the child. Absent ⇒ the parent had none.
424
+ */
425
+ sessionTiers?: {
426
+ readonly includeGoalTool?: boolean;
427
+ };
428
+ /**
429
+ * The parent's sandbox settings as they stand at spawn, including a change made this session.
430
+ * Opaque here: the composition root produced it and its `createSandbox` reads it, so the child
431
+ * confines and approves as the parent does now, not as its root's settings files say.
432
+ */
433
+ parentSandboxSettings?: TParentSandboxSettings;
434
+ permissionMode?: TPermissionMode;
435
+ logsDir?: string;
436
+ /** The owner opted the provider credential variable in for commands; the worker keeps it. */
437
+ keepProviderCredential?: true;
438
+ }
439
+ interface ISubagentWorkerStartMessage {
440
+ type: 'start';
441
+ payload: ISubagentWorkerStartPayload;
442
+ }
443
+ interface ISubagentWorkerSendMessage {
444
+ type: 'send';
445
+ prompt: string;
446
+ }
447
+ interface ISubagentWorkerCancelMessage {
448
+ type: 'cancel';
449
+ reason?: string;
450
+ }
451
+ /** The parent's sandbox settings changed while the child runs (`/sandbox`); the child follows. */
452
+ interface ISubagentWorkerSandboxSettingsMessage {
453
+ type: 'sandbox_settings';
454
+ settings: TParentSandboxSettings;
455
+ }
456
+ /**
457
+ * Issue #3288 §1: the three-way answer a permission ask can receive — the same shape
458
+ * `@robota-sdk/agent-session`'s `TPermissionResult` allows, inlined here so this wire module does not
459
+ * need that package as a dependency for one literal union.
460
+ */
461
+ type TSubagentWorkerPermissionResult = boolean | 'allow-session' | 'allow-project';
462
+ /**
463
+ * Issue #3288 §1: the parent's answer to a `permission_request` the child raised, correlated by
464
+ * `requestId`. `false` when the parent has no approver attached (print mode) — the fail-closed
465
+ * default, not a special case the child has to know about.
466
+ */
467
+ interface ISubagentWorkerPermissionResponseMessage {
468
+ type: 'permission_response';
469
+ requestId: string;
470
+ result: TSubagentWorkerPermissionResult;
471
+ }
472
+ type TSubagentWorkerParentMessage = ISubagentWorkerStartMessage | ISubagentWorkerSendMessage | ISubagentWorkerCancelMessage | ISubagentWorkerSandboxSettingsMessage | ISubagentWorkerPermissionResponseMessage;
473
+ interface ISubagentWorkerReadyMessage {
474
+ type: 'ready';
475
+ /**
476
+ * ARCH-021: the tool names the child actually composed, so "the child has the product's surface"
477
+ * is VERIFIED per run rather than assumed by construction. Names only — the tools themselves are
478
+ * code and do not cross this boundary; that is the whole point of the composition port.
479
+ *
480
+ * Enumerated at the worker's own cwd before any job arrives, which is sound because a pack's tool
481
+ * NAMES do not depend on the root (the root binds the path guard, not the name set).
482
+ */
483
+ composedToolNames?: readonly string[];
484
+ }
485
+ interface ISubagentWorkerTextDeltaMessage {
486
+ type: 'text_delta';
487
+ delta: string;
488
+ }
489
+ interface ISubagentWorkerToolStartMessage {
490
+ type: 'tool_start';
491
+ toolName: string;
492
+ toolArgs?: TToolArgs;
493
+ }
494
+ interface ISubagentWorkerToolEndMessage {
495
+ type: 'tool_end';
496
+ toolName: string;
497
+ success: boolean;
498
+ }
499
+ /**
500
+ * Issue #3288 §1: a tool call inside the child needs a human's approval. `requestId` correlates this
501
+ * with the parent's eventual `permission_response`; the child parks the call until it arrives (or
502
+ * until its own turn is cancelled, which the enforcer's existing signal race already denies).
503
+ */
504
+ interface ISubagentWorkerPermissionRequestMessage {
505
+ type: 'permission_request';
506
+ requestId: string;
507
+ toolName: string;
508
+ toolArgs?: TToolArgs;
509
+ }
510
+ interface ISubagentWorkerResultMessage {
511
+ type: 'result';
512
+ output: string;
513
+ /** ANALYTICS-001 (Phase 2): total token usage of the subagent run, forwarded to the parent. */
514
+ usage?: ISessionUsageTotals;
515
+ }
516
+ interface ISubagentWorkerErrorMessage {
517
+ type: 'error';
518
+ message: string;
519
+ }
520
+ interface ISubagentWorkerCancelledMessage {
521
+ type: 'cancelled';
522
+ reason?: string;
523
+ }
524
+ type TSubagentWorkerChildMessage = ISubagentWorkerReadyMessage | ISubagentWorkerTextDeltaMessage | ISubagentWorkerToolStartMessage | ISubagentWorkerToolEndMessage | ISubagentWorkerPermissionRequestMessage | ISubagentWorkerResultMessage | ISubagentWorkerErrorMessage | ISubagentWorkerCancelledMessage;
525
+ declare function isSubagentWorkerParentMessage(value: TSubagentWorkerWireValue): value is TSubagentWorkerParentMessage;
526
+ declare function isSubagentWorkerChildMessage(value: TSubagentWorkerWireValue): value is TSubagentWorkerChildMessage;
527
+ //#endregion
528
+ //#region src/child-process-subagent-worker.d.ts
529
+ /**
530
+ * DIST-006: worker mode is ENTERED, not implied by loading this module.
531
+ *
532
+ * These handlers used to run as module top-level side effects, which is what forced the worker to
533
+ * be a separate file that something had to locate on disk. As a function, the composition root's
534
+ * own entry can become the worker — so there is no second artifact and no path to get wrong.
535
+ *
536
+ * ARCH-021: `composition` is REQUIRED, deliberately. An optional parameter falling back to imported
537
+ * defaults would reinstate the exact defect this seam removes — and at this line conventions have a
538
+ * measured failure rate of 100% (ARCH-010 and ARCH-006 are both findings here).
539
+ */
540
+ declare function runSubagentWorkerMain(composition: ISubagentWorkerComposition): void;
541
+ //#endregion
542
+ export { ChildProcessSubagentRunner, type IChildProcessSubagentRunnerOptions, type ISubagentComposedSandbox, type ISubagentWorkerAgentDefinitionDto, type ISubagentWorkerComposition, type ISubagentWorkerContextFileEntryDto, type ISubagentWorkerEntry, type ISubagentWorkerParentContextDto, type ISubagentWorkerStartPayload, SUBAGENT_WORKER_MODE_FLAG, type TParentSandboxSettings, type TResumeSessionStore, type TSubagentWorkerChildMessage, type TSubagentWorkerParentMessage, type TSubagentWorkerWireValue, createChildProcessSubagentRunnerFactory, decodeAgentDefinitionDto, decodeParentContextDto, encodeAgentDefinition, encodeParentContext, isSubagentWorkerChildMessage, isSubagentWorkerModeArgv, isSubagentWorkerParentMessage, restoreAgentDefinition, restoreParentContext, runSubagentWorkerMain };
543
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/worker-entry.ts","../../src/worker-composition.ts","../../src/child-process-subagent-runner.ts","../../src/subagent-worker-start-dto.ts","../../src/child-process-subagent-ipc.ts","../../src/child-process-subagent-worker.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;cAmBa;;;;;;;;;;UAWI;;WAEN;;WAEA;;WAEA;;;iBAIK,yBAAyB;;;;;;;;;KC3B7B,sBAAsB,kBAAkB;;;;;;;;;;;;;;;;;;;;;;UAuBnC;;EAEf,2BAA2B;aAChB;aACA,gBAAgB;QACrB;;;;;;;;;EASN,YAAY;aACD;;;;;;;;;;;;;;aAcA;;eAEE;;;;;;;;;;aAUF,gBAAgB;MACvB;;;;;;WAOK,8BAA8B;;;;;;;;;;;;;;;;;;;;;WAsB9B,mBAAmB,SAAS,eAAe;;;;;;;;;;;;;;WAe3C,oBAAoB;aAAoB;QAAkB;;;;;;;;;;WAW1D,iBAAiB;aACf;;aAEA,iBAAiB;QACtB;;;KAII,yBAAyB,SAAS;;UAG7B;WACN,QAAQ;WACR,iBAAiB;;;;;;;WAOjB,uBAAuB,UAAU;;;;;;;;KAShC,yBAAyB,uBAAuB,QAAQ;;;;;;;;;;;;;KAcxD;;;;;;;UAQK;WACN;WACA;;;;UC5HM;;;;;;;EAOf,aAAa;EACb,iBAAiB;;;;;;EAMjB,8BAA8B;EAC9B;;;;;EAKA;EACA,MAAM,OAAO;;EAEb,sBAAsB,OAAO;;EAE7B;EACA;EACA,iBAAiB;EACjB;;;;;EAKA;;;;;;EAMA,8BAA8B;;;;;;EAM9B,8BAA8B,WAAW,UAAU;;iBAGrC,wCACd,SAAS,qCACR;cAaU,sCAAsC;mBAe9B;mBAdF;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;EAGE,YAAA,MAAM,8BACvB,SAAS;EAmBX,MAAM,KAAK,oBAAoB;;;;;;;;;UAoGvB;UAcA;UAYA;;;;;;;;;UAaA;;;;;;;;KC9QL,sBAAsB;UAEV;WACN;WACA;WACA;WACA;WACA,SAAS;WACT;WACA;WACA;WACA;;UAGM;WACN;WACA;WACA;;UAGM;WACN;WACA;WACA;WACA;WACA;WACA,6BAA6B;WAC7B,mCAAmC;;KAmClC,iBAAiB;WAChB;WAAmB,OAAO;;WAAoB;WAAoB;;iBAmE/D,sBACd,YAAY,mBACX;iBAOa,yBACd,iBACC,iBAAiB;;iBASJ,uBAAuB,KAAK,oCAAoC;;iBAgBhE,oBACd,SAAS,yBACR;iBAOa,uBACd,iBACC,iBAAiB;iBAIJ,qBAAqB,KAAK,kCAAkC;;;KClMhE;;UAKK;WACN;aAAqB;;WACrB,aAAa;WACb,mBAAmB;WACnB,QAAQ;;UAGF;EACf;EACA,SAAS;;;;;;;;;;EAUT;aAAsB;aAAuB;;;EAE7C,iBAAiB;;;;;;;;;;;EAWjB,cAAc;;;;;;;EAOd,eAAe;EACf,iBAAiB;;;;;EAKjB,iBAAiB;;;;;;;;;;;;EAYjB,oBAAoB;;;;;;;EAOpB;aAA0B;;;;;;;EAM1B,wBAAwB;EACxB,iBAAiB;EACjB;;EAEA;;UAGe;EACf;EACA,SAAS;;UAGM;EACf;EACA;;UAGe;EACf;EACA;;;UAIe;EACf;EACA,UAAU;;;;;;;KAQA;;;;;;UAOK;EACf;EACA;EACA,QAAQ;;KAGE,+BACR,8BACA,6BACA,+BACA,wCACA;UAEa;EACf;;;;;;;;;EASA;;UAGe;EACf;EACA;;UAGe;EACf;EACA;EACA,WAAW;;UAGI;EACf;EACA;EACA;;;;;;;UAQe;EACf;EACA;EACA;EACA,WAAW;;UAGI;EACf;EACA;;EAEA,QAAQ;;UAGO;EACf;EACA;;UAGe;EACf;EACA;;KAGU,8BACR,8BACA,kCACA,kCACA,gCACA,0CACA,+BACA,8BACA;iBAkHY,8BACd,OAAO,2BACN,SAAS;iBAuBI,6BACd,OAAO,2BACN,SAAS;;;;;;;;;;;;;;iBCDI,sBAAsB,aAAa"}
@@ -0,0 +1,3 @@
1
+ import{spawn as e}from"node:child_process";import{existsSync as t,readFileSync as n}from"node:fs";import{join as r}from"node:path";import{BackgroundTaskError as i,connectionEnvironmentNames as a,createBackgroundTaskLogPage as o,createProviderFromExactProfile as s,createWorktreeSubagentRunner as c,findConnectionEnvironmentDivergence as l,sealConnectionEnvironment as u,subagentExecutionRoot as d,verifyConnectionEnvironment as f}from"@robota-sdk/agent-executor";import{DEFAULT_KILL_GRACE_MS as p,killProcessTree as m}from"@robota-sdk/agent-process";import{createBoundedOutput as ee,findProviderDefinition as h,isModelEffort as te,sumHistoryUsage as ne}from"@robota-sdk/agent-core";import{createSubagentLogger as g,createSubagentSession as re,restoreSessionRecordIntoSession as ie}from"@robota-sdk/agent-framework";function ae(e){return{provider:{model:e.provider.model},permissions:e.permissions,defaultTrustLevel:e.defaultTrustLevel,...e.hooks===void 0?{}:{hooks:e.hooks}}}function oe(e){return{agentsMd:e.agentsMd,projectNotesMd:e.projectNotesMd}}const _={name:{kind:`string`,required:!0},description:{kind:`string`,required:!0},systemPrompt:{kind:`string`,required:!0},model:{kind:`string`,required:!1},effort:{kind:`effort`,required:!1},role:{kind:`string`,required:!1},maxTurns:{kind:`number`,required:!1},tools:{kind:`string[]`,required:!1},disallowedTools:{kind:`string[]`,required:!1}},v={agentsMd:{kind:`string`,required:!0},projectNotesMd:{kind:`string`,required:!0},memoryMd:{kind:`string`,required:!1},taskContext:{kind:`string`,required:!1},compactInstructions:{kind:`string`,required:!1},agentsFileEntries:{kind:`file-entry[]`,required:!1},projectNotesFileEntries:{kind:`file-entry[]`,required:!1}};function y(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function se(e){return Array.isArray(e)&&e.every(e=>typeof e==`string`)}function ce(e){return y(e)&&typeof e.filePath==`string`&&typeof e.content==`string`&&typeof e.contentHash==`string`}function le(e,t){switch(e){case`string`:return typeof t==`string`;case`number`:return typeof t==`number`&&Number.isFinite(t);case`effort`:return typeof t==`string`&&te(t);case`string[]`:return se(t);case`file-entry[]`:return Array.isArray(t)&&t.every(ce)}}function b(e,t){let n={};for(let r of Object.keys(t)){let t=e[r];t!==void 0&&(n[r]=t)}return n}function x(e,t,n){if(!y(t))return{ok:!1,reason:`${e}: expected an object`};for(let[r,i]of Object.entries(n)){let n=t[r];if(n===void 0){if(i.required)return{ok:!1,reason:`${e}.${r}: required`};continue}if(!le(i.kind,n))return{ok:!1,reason:`${e}.${r}: expected ${i.kind}`}}return{ok:!0,value:b(t,n)}}function S(e){return b(e,_)}function C(e){return x(`agentDefinition`,e,_)}function w(e){let t={name:e.name,description:e.description,systemPrompt:e.systemPrompt};return e.model!==void 0&&(t.model=e.model),e.effort!==void 0&&(t.effort=e.effort),e.role!==void 0&&(t.role=e.role),e.maxTurns!==void 0&&(t.maxTurns=e.maxTurns),e.tools!==void 0&&(t.tools=[...e.tools]),e.disallowedTools!==void 0&&(t.disallowedTools=[...e.disallowedTools]),t}function T(e){return b(e,v)}function E(e){return x(`parentContext`,e,v)}function D(e){let t={agentsMd:e.agentsMd,projectNotesMd:e.projectNotesMd};return e.memoryMd!==void 0&&(t.memoryMd=e.memoryMd),e.taskContext!==void 0&&(t.taskContext=e.taskContext),e.compactInstructions!==void 0&&(t.compactInstructions=e.compactInstructions),e.agentsFileEntries!==void 0&&(t.agentsFileEntries=e.agentsFileEntries.map(e=>({...e}))),e.projectNotesFileEntries!==void 0&&(t.projectNotesFileEntries=e.projectNotesFileEntries.map(e=>({...e}))),t}function ue(e){return e.sessionTiers===void 0?{}:{sessionTiers:e.sessionTiers}}async function de(e){let{sandboxClient:t,sandboxType:n}=e;return t?.snapshot===void 0||n===void 0?{}:{sandboxProjection:{type:n,snapshotId:await t.snapshot()}}}function O(e,t,n,r,o){let s=n.providerDefinitions,c=(n.providerConfig??t.config.provider).name;if(h(s,c)===void 0)throw new i(`validation`,`No provider definition for "${c}" was given to the subagent runner, so the connection a child would make cannot be checked; the subagent was not started.`);let d=ge(n.providerConfig,t,e,s),f=a(d,s),p=l(f,r,o);if(p!==void 0)throw new i(`validation`,`The subagent's environment sets ${p} differently from this session, which would change where its provider connects or which credential it sends; the subagent was not started.`);return{providerProfile:d,connectionCheck:u(f,o)}}function fe(e,t,n){let r=pe(e.request.agentType,t.customAgentRegistry,t.builtInAgents,t.agentDefinitions),i={taskId:e.taskId,request:e.request,...e.worktree?{worktree:e.worktree}:{},agentDefinition:S(me(r,e)),parentConfig:ae(t.getParentPermissionRules===void 0?t.config:{...t.config,permissions:t.getParentPermissionRules()}),parentContext:T(oe(t.context)),...n.connection??O(e,t,n,process.env,process.env),permissionMode:t.getParentPermissionMode?.()??t.permissionMode,...ue(t),...n.parentSandboxSettings===void 0?{}:{parentSandboxSettings:n.parentSandboxSettings},...n.logsDir?{logsDir:n.logsDir}:{},...n.keepProviderCredential?{keepProviderCredential:!0}:{}};return de(t).then(e=>({...i,...e}))}function pe(e,t,n,r){let a=t?.(e)??n?.find(t=>t.name===e)??r?.find(t=>t.name===e);if(!a)throw new i(`validation`,`Unknown agent type: ${e}`);return a}function me(e,t){return{...e,...t.request.model?{model:t.request.model}:{},...t.request.effort===void 0?{}:{effort:t.request.effort},...t.request.allowedTools?{tools:t.request.allowedTools}:{},...t.request.disallowedTools?{disallowedTools:t.request.disallowedTools}:{}}}function he(e,t){return e.apiKeyRef===void 0?e.apiKeyEnv===void 0?e.apiKey===void 0?t===void 0?{}:t.startsWith(`$ENV:`)?{apiKeyEnv:t.slice(5)}:{apiKey:t}:{apiKey:e.apiKey}:{apiKeyEnv:e.apiKeyEnv}:{apiKeyRef:e.apiKeyRef}}function ge(e,t,n,r){let i=e??t.config.provider,a=h(r,i.name)?.defaults??{},o=i.baseURL??a.baseURL,s=i.options??a.options,c=he(i,a.apiKey);return{...e===void 0&&t.config.currentProvider!==void 0?{profileName:t.config.currentProvider}:{},type:i.name,model:n.request.model??i.model,...c,...o===void 0?{}:{baseURL:o},...i.timeout===void 0?{}:{timeout:i.timeout},...s===void 0?{}:{options:s}}}function k(e){return typeof e==`object`&&!!e}function _e(e){if(!k(e)||!A(e,`nonce`)||!A(e,`digest`))return!1;let t=e.names;return Array.isArray(t)&&t.every(e=>typeof e==`string`)}function A(e,t){return typeof e[t]==`string`}function j(e,t){return A(e,t)}function M(e,t){return A(e,t)}function N(e,t){return e[t]===void 0||typeof e[t]==`string`}function P(e){if(e.usage===void 0)return!0;let t=e.usage;return k(t)?typeof t.promptTokens==`number`&&typeof t.completionTokens==`number`&&typeof t.totalTokens==`number`:!1}function F(e){if(e.composedToolNames===void 0)return!0;let t=e.composedToolNames;return Array.isArray(t)?t.every(e=>typeof e==`string`):!1}function I(e){return!k(e)||!M(e,`taskId`)||!k(e.request)||!j(e.request,`agentType`)||!j(e.request,`prompt`)||!j(e.request,`permissionPolicy`)||!j(e.request,`cwd`)||!N(e.request,`resumeSessionId`)||e.worktree!==void 0&&(!k(e.worktree)||!A(e.worktree,`path`))||!C(e.agentDefinition).ok||!k(e.parentConfig)||!E(e.parentContext).ok||!k(e.providerProfile)||!A(e.providerProfile,`type`)||!A(e.providerProfile,`model`)||e.parentSandboxSettings!==void 0&&!k(e.parentSandboxSettings)?!1:_e(e.connectionCheck)}function L(e){if(!k(e)||!A(e,`type`))return!1;switch(e.type){case`start`:return I(e.payload);case`send`:return A(e,`prompt`);case`cancel`:return e.reason===void 0||typeof e.reason==`string`;case`sandbox_settings`:return k(e.settings);case`permission_response`:return A(e,`requestId`)&&ve(e.result);default:return!1}}function ve(e){return typeof e==`boolean`||e===`allow-session`||e===`allow-project`}function R(e){if(!k(e)||!A(e,`type`))return!1;switch(e.type){case`ready`:return F(e);case`text_delta`:return A(e,`delta`);case`tool_start`:return A(e,`toolName`);case`tool_end`:return A(e,`toolName`)&&typeof e.success==`boolean`;case`permission_request`:return A(e,`requestId`)&&A(e,`toolName`);case`result`:return A(e,`output`)&&P(e);case`error`:return A(e,`message`);case`cancelled`:return e.reason===void 0||typeof e.reason==`string`;default:return!1}}const ye=process.platform!==`win32`;function be(e,t){return new Promise(n=>{if(e.exitCode!==null||e.signalCode!==null){n();return}let r=setTimeout(()=>{e.removeListener(`exit`,i),n()},t);r.unref?.();let i=()=>{clearTimeout(r),n()};e.once(`exit`,i)})}const z=new WeakMap;function xe(e){let t=e.stderr;if(!t)return;let n=ee({maxBytes:4096,retain:`tail`,truncationMarker:()=>``});z.set(e,n),t.on(`error`,()=>{}),t.on(`data`,e=>n.append(e))}function Se(e){return(z.get(e)?.toString()??``).trim()}function Ce(e,t,n,r,a,o){switch(e.type){case`ready`:t();break;case`result`:n(e);break;case`error`:r(new i(`runner`,e.message));break;case`cancelled`:r(new i(`runner`,e.reason??`Subagent worker cancelled`));break;case`text_delta`:a?.({type:`background_task_text_delta`,delta:e.delta});break;case`tool_start`:a?.({type:`background_task_tool_start`,toolName:e.toolName,firstArg:we(e.toolArgs)});break;case`tool_end`:a?.({type:`background_task_tool_end`,toolName:e.toolName,success:e.success});break;case`permission_request`:o?.(e);break;default:r(new i(`runner`,`Unhandled subagent worker message`))}}function we(e){if(!e)return;let t=Object.values(e)[0];if(t!==void 0)return typeof t==`object`?JSON.stringify(t):String(t)}function B(e,t){return new Promise((n,r)=>{if(!e.connected){r(new i(`crash`,`Subagent worker IPC channel is closed`));return}e.send(t,e=>{if(e){r(e);return}n()})})}async function V(e,t){await m(e.child,{graceMs:e.killGraceMs,processGroup:ye,preKill:async()=>{e.child.connected&&(await B(e.child,{type:`cancel`,reason:t}).catch(()=>void 0),await be(e.child,e.killGraceMs))}})}function Te(e){return new Promise((t,n)=>{new Ee(e,t,n).start()})}var Ee=class{options;resolve;reject;settled=!1;started=!1;ready=!1;timeoutTimer;handshakeTimer;handshakeBudgetMs;payload;deniedToolCallCounts={"no-approver":0,"denied-by-person":0,"approver-error":0,cancelled:0};pendingPermissionRequests=new Map;constructor(e,t,n){this.options=e,this.resolve=t,this.reject=n;let r=e.handshakeBudgetMs;this.handshakeBudgetMs=r!==void 0&&r>0?r:3e4,this.payload=e.payload.catch(e=>{this.rejectOnce(e instanceof Error?e:Error(String(e)))}),this.timeoutTimer=Oe(this.options.runtime,e=>this.rejectOnce(e)),this.handshakeTimer=setTimeout(()=>{this.ready||this.settled||(V(this.options.runtime,`Subagent worker never signalled ready`),this.rejectOnce(new i(`runner`,`Subagent worker never signalled ready within ${this.handshakeBudgetMs}ms. Its entry must dispatch worker mode before starting the host application.`)))},this.handshakeBudgetMs),this.handshakeTimer.unref?.()}start(){let{child:e}=this.options.runtime;e.on(`message`,this.onMessage),e.on(`error`,this.onError),e.on(`exit`,this.onExit),e.once(`spawn`,()=>{setImmediate(this.startWorker)})}startWorker=()=>{if(this.started)return;this.started=!0;let{child:e}=this.options.runtime;this.payload.then(t=>t===void 0?void 0:B(e,{type:`start`,payload:t})).catch(e=>{this.rejectOnce(e instanceof Error?e:Error(String(e)))})};onMessage=e=>{if(!R(e)){this.rejectOnce(new i(`runner`,`Received malformed subagent worker message`));return}this.ready=!0,clearTimeout(this.handshakeTimer);let{job:t}=this.options.runtime;Ce(e,this.startWorker,this.resolveOnce,this.rejectOnce,t.emit,this.onPermissionRequest)};onPermissionRequest=e=>{let t=this.answerPermissionRequest(e).finally(()=>{this.pendingPermissionRequests.delete(e.requestId)});this.pendingPermissionRequests.set(e.requestId,t)};async answerPermissionRequest(e){let{reason:t,result:n}=await this.askApprover(e);t!==void 0&&(this.deniedToolCallCounts[t]+=1);let{child:r}=this.options.runtime;r.connected&&await B(r,{type:`permission_response`,requestId:e.requestId,result:n}).catch(()=>void 0)}async askApprover(e){let t=this.options.permissionApprover;if(t===void 0)return{reason:`no-approver`,result:!1};try{let n=await t(e.toolName,e.toolArgs??{},this.options.permissionAbort.signal);return n===!1?this.options.permissionAbort.signal.aborted?{reason:`cancelled`,result:!1}:{reason:`denied-by-person`,result:!1}:{result:n}}catch{return{reason:`approver-error`,result:!1}}}onError=e=>{this.rejectOnce(new i(`crash`,e.message))};onExit=(e,t)=>{this.settled||this.rejectOnce(new i(`crash`,je(e,t,Se(this.options.runtime.child))))};resolveOnce=e=>{this.settled||(this.settled=!0,this.settleAfterPendingPermissionRequests(()=>{let{runtime:t,resolveTranscriptPath:n}=this.options;this.resolve(ke(t.job,e,n,this.deniedToolCallCounts))}))};rejectOnce=e=>{this.settled||(this.settled=!0,this.settleAfterPendingPermissionRequests(()=>this.reject(e)))};async settleAfterPendingPermissionRequests(e){this.options.permissionAbort.abort(),this.clearTimers(),this.cleanup(),await Promise.all(this.pendingPermissionRequests.values()),e()}clearTimers(){this.timeoutTimer&&clearTimeout(this.timeoutTimer),clearTimeout(this.handshakeTimer)}cleanup(){let{child:e}=this.options.runtime;e.off(`message`,this.onMessage),e.off(`error`,this.onError),e.off(`exit`,this.onExit)}};function De(e){let t=!1,n=()=>{};return{promise:new Promise((e,t)=>{n=t}),reject(r){t||(t=!0,n(new i(`runner`,r??`Subagent job cancelled: ${e}`)))}}}function Oe(e,t){if(e.job.request.timeoutMs)return setTimeout(()=>{V(e,`Subagent worker timed out`),t(new i(`timeout`,`Subagent worker timed out`))},e.job.request.timeoutMs)}function ke(e,t,n,r){let i=n(e),a=Ae(r);return{taskId:e.taskId,output:t.output,...i?{metadata:{transcriptPath:i,logPath:i}}:{},...t.usage?{usage:t.usage}:{},...a?{deniedToolCalls:a}:{}}}function Ae(e){let t=Object.values(e).reduce((e,t)=>e+t,0);return t===0?void 0:{total:t,byReason:{...e}}}function je(e,t,n){return`Subagent worker exited before result: ${t===null?`exit code ${e===null?`unknown`:e}`:`signal ${t}`}${n.length>0?`: ${n}`:``}`}const H=`--__agent-subagent-worker`;function Me(e){return e.includes(H)}const Ne=process.platform!==`win32`;function Pe(e){return t=>{let n=new U(t,e);return e.worktreeIsolation===!1?n:c({runner:n,worktreeAdapter:e.worktreeAdapter,hooks:t.config.hooks,hookTypeExecutors:t.hookTypeExecutors})}}var U=class{deps;workerEntry;killGraceMs;handshakeBudgetMs;providerConfig;providerDefinitions;env;expectedEnvironment;inheritEnvironment;logsDir;keepProviderCredential;parentSandboxSettings;watchParentSandboxSettings;constructor(e,t){if(this.deps=e,this.workerEntry=t.workerEntry,this.killGraceMs=t.killGraceMs??p,this.handshakeBudgetMs=t.handshakeBudgetMs,this.providerConfig=t.providerConfig,this.providerDefinitions=t.providerDefinitions,this.env=t.env,this.expectedEnvironment=t.expectedEnvironment,this.inheritEnvironment=t.inheritEnvironment!==!1,!this.inheritEnvironment&&this.expectedEnvironment===void 0)throw Error(`An explicit expected environment is required when child inheritance is disabled.`);this.logsDir=t.logsDir,this.keepProviderCredential=t.keepProviderCredential===!0,this.parentSandboxSettings=t.parentSandboxSettings,this.watchParentSandboxSettings=t.watchParentSandboxSettings}start(t){let n=this.workerEntry,r=this.inheritEnvironment?{...process.env,...this.env??{}}:{...this.env??{}},i=O(t,this.deps,{...this.providerConfig===void 0?{}:{providerConfig:this.providerConfig},providerDefinitions:this.providerDefinitions},this.expectedEnvironment??process.env,r),a=e(n.execPath,[...n.execArgv??[],...n.args,H],{cwd:d(t),env:r,stdio:[`ignore`,`ignore`,`pipe`,`ipc`],detached:Ne});xe(a),this.forwardSandboxSettingChanges(a);let o={job:t,child:a,killGraceMs:this.killGraceMs},s=this.createStartPayload(t,i),c=new AbortController,l=this.permissionApprover(t),u=Te({runtime:o,payload:s,...this.handshakeBudgetMs===void 0?{}:{handshakeBudgetMs:this.handshakeBudgetMs},resolveTranscriptPath:e=>this.resolveTranscriptPath(e),permissionAbort:c,...l===void 0?{}:{permissionApprover:l}}),f=De(t.taskId);u.catch(()=>void 0);let p=Promise.race([u,f.promise]);p.catch(()=>void 0);let m=this.resolveTranscriptPath(t);return{taskId:t.taskId,...a.pid!==void 0&&{pid:a.pid},...m!==void 0&&{transcriptPath:m,logPath:m},result:p,cancel:async e=>{c.abort(e),f.reject(e),await V(o,e)},send:async e=>{await B(a,{type:`send`,prompt:e})},...m!==void 0&&{readLog:async e=>Fe(t.taskId,m,e)}}}createStartPayload(e,t){let n=this.parentSandboxSettings?.();return fe(e,this.deps,{connection:t,providerDefinitions:this.providerDefinitions,...this.logsDir===void 0?{}:{logsDir:this.logsDir},...this.keepProviderCredential?{keepProviderCredential:!0}:{},...n===void 0?{}:{parentSandboxSettings:n}})}forwardSandboxSettingChanges(e){let t=this.watchParentSandboxSettings?.(t=>{e.connected&&B(e,{type:`sandbox_settings`,settings:t}).catch(()=>void 0)});t!==void 0&&(e.once(`exit`,t),e.once(`error`,t))}resolveTranscriptPath(e){if(this.logsDir)return r(this.logsDir,e.request.parentSessionId,`subagents`,`${e.taskId}.jsonl`)}permissionApprover(e){let t=this.deps.permissionHandler;if(t===void 0)return;let n={kind:`background-agent`,label:e.request.agentType,taskId:e.taskId};return(e,r,i)=>t(e,r,{requester:n,signal:i})}};function Fe(e,r,i){return t(r)?o(e,n(r,`utf8`).split(/\r?\n/).filter(Boolean),i):{taskId:e,cursor:i,lines:[]}}function Ie(e,t,n){let r=e.request.resumeSessionId;if(r!==void 0){if(n===void 0)throw Error(`subagent worker: job ${e.taskId} asks to resume session ${r}, but this composition opens no session store. Register ISubagentWorkerComposition.openSessionStore at the composition root — the same place providerDefinitions is registered.`);ie(n,r,t)}}function Le(e,t){let n=e.request.resumeSessionId;if(n!==void 0){if(t.openSessionStore===void 0)throw Error(`subagent worker: job ${e.taskId} asks to resume session ${n}, but this composition opens no session store. Register ISubagentWorkerComposition.openSessionStore at the composition root — the same place providerDefinitions is registered.`);return t.openSessionStore({cwd:e.request.cwd})}}async function Re(e,t){if(e===void 0)return;let n=t?.[e.type];if(n===void 0)throw Error(`subagent worker: sandbox type "${e.type}" is not registered in the worker composition. The parent is sandboxed and passed a snapshot reference, but this child cannot construct that client type. Register it in ISubagentWorkerComposition.sandboxFactories at the composition root — the same place providerDefinitions is registered, and for the same reason.`);return n(e.snapshotId)}function ze(e,t=!1,n=process.env){t||e.apiKeyEnv===void 0||delete n[e.apiKeyEnv]}const Be={write:()=>{},writeLine:()=>{},writeMarkdown:()=>{},writeError:()=>{},prompt:()=>Promise.resolve(``),select:()=>Promise.resolve(0),spinner:()=>({stop:()=>{},update:()=>{}})};let W=null,G=!1;const K=new Map;let Ve=0,q,J,Y,X=Promise.resolve();function Z(e){process.send&&process.send(e)}function Q(e,t){let n=!1,r=()=>{n||(n=!0,process.exit(t))};if(process.send){let t=setTimeout(r,2e3);t.unref?.(),process.send(e,void 0,void 0,()=>{clearTimeout(t),r()})}else r()}function He(e){try{return ne(e.getFullHistory())}catch{return}}async function Ue(e,t){try{if(!f(e.connectionCheck,process.env))throw Error(`The provider connection environment changed after the parent checked it; the subagent was not started.`);let n=await Re(e.sandboxProjection,t.sandboxFactories),r=s(e.providerProfile,e.request.model,t.providerDefinitions);ze(e.providerProfile,e.keepProviderCredential===!0);let i=e.logsDir?g(e.request.parentSessionId,e.taskId,e.logsDir):void 0,a=Le(e,t),o=q??e.parentSandboxSettings;J=n===void 0?t.createSandbox?.({cwd:d(e),...o===void 0?{}:{parentSettings:o}}):void 0;let c=n??J?.client;W=re({agentDefinition:w(e.agentDefinition),parentConfig:e.parentConfig,parentContext:D(e.parentContext),parentTools:t.createTools({cwd:d(e),...c===void 0?{}:{sandboxClient:c},...e.sessionTiers===void 0?{}:{sessionTiers:e.sessionTiers}}),cwd:d(e),provider:r,terminal:Be,sessionId:e.request.resumeSessionId??e.taskId,...a===void 0?{}:{sessionStore:a},...i?{sessionLogger:i}:{},permissionMode:e.permissionMode,...J?.commandSandbox===void 0?{}:{commandSandbox:J.commandSandbox},...e.request.permissionPolicy===void 0?{}:{permissionPolicy:e.request.permissionPolicy},...e.request.allowedTools===void 0?{}:{taskAllowedTools:e.request.allowedTools},...e.request.disallowedTools===void 0?{}:{taskDisallowedTools:e.request.disallowedTools},hooks:e.parentConfig.hooks,...t.createHookTypeExecutors===void 0?{}:{hookTypeExecutors:t.createHookTypeExecutors({cwd:d(e),...c===void 0?{}:{sandboxClient:c}})},onTextDelta:e=>Z({type:`text_delta`,delta:e}),onToolExecution:Ge,permissionHandler:We}),Ie(e,W,a);let l=await W.run(e.request.prompt);if(Y!==void 0){Q({type:`error`,message:Y},0);return}if(G){Q({type:`cancelled`,reason:`Subagent worker cancelled`},130);return}let u=He(W);Q({type:`result`,output:l,...u?{usage:u}:{}},0)}catch(e){if(Y!==void 0){Q({type:`error`,message:Y},0);return}if(G){Q({type:`cancelled`,reason:`Subagent worker cancelled`},130);return}Q({type:`error`,message:e instanceof Error?e.message:String(e)},0)}}function We(e,t){let n=`perm_${++Ve}`;return new Promise(r=>{K.set(n,r),Z({type:`permission_request`,requestId:n,toolName:e,toolArgs:t})})}function $(e,t){let n=K.get(e);n!==void 0&&(K.delete(e),n(t))}function Ge(e){if(e.type===`start`){Z({type:`tool_start`,toolName:e.toolName,toolArgs:e.toolArgs});return}Z({type:`tool_end`,toolName:e.toolName,success:e.success??!0})}function Ke(e){if(W===null){Z({type:`error`,message:`Subagent worker has not started`});return}X=X.then(async()=>{try{await W?.run(e)}catch(e){Z({type:`error`,message:e instanceof Error?e.message:String(e)})}})}async function qe(e){G=!0,W?.abort(),Z({type:`cancelled`,reason:e}),await W?.shutdown({reason:`other`}).catch(()=>void 0),setTimeout(()=>process.exit(130),0)}function Je(e){try{return e.createTools({cwd:process.cwd()}).map(e=>e.getName())}catch(e){process.stderr.write(`agent: could not enumerate the composed tool surface: ${e instanceof Error?e.message:String(e)}\n`);return}}function Ye(e){q=e;try{J?.applyParentSettings?.(e)}catch(e){Y??=e instanceof Error?e.message:String(e),W?.abort()}}function Xe(e){process.send===void 0&&(process.stderr.write(`agent: subagent worker mode requires an IPC channel; it is started by the agent runtime, not by hand.
2
+ `),process.exit(2)),process.on(`message`,t=>{if(!L(t)){Z({type:`error`,message:`Malformed subagent worker parent message`});return}switch(t.type){case`start`:X=X.then(()=>Ue(t.payload,e));break;case`send`:Ke(t.prompt);break;case`cancel`:qe(t.reason);break;case`sandbox_settings`:Ye(t.settings);break;case`permission_response`:$(t.requestId,t.result);break;default:Z({type:`error`,message:`Unhandled subagent worker parent message`})}}),process.on(`disconnect`,()=>{G=!0,W?.abort(),W?.shutdown({reason:`other`}).catch(()=>void 0)});let t=Je(e);Z({type:`ready`,...t?{composedToolNames:t}:{}})}export{U as ChildProcessSubagentRunner,H as SUBAGENT_WORKER_MODE_FLAG,Pe as createChildProcessSubagentRunnerFactory,C as decodeAgentDefinitionDto,E as decodeParentContextDto,S as encodeAgentDefinition,T as encodeParentContext,R as isSubagentWorkerChildMessage,Me as isSubagentWorkerModeArgv,L as isSubagentWorkerParentMessage,w as restoreAgentDefinition,D as restoreParentContext,Xe as runSubagentWorkerMain};
3
+ //# sourceMappingURL=index.js.map