@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.
- package/CHANGELOG.md +897 -0
- package/LICENSE +661 -0
- package/README.md +137 -0
- package/dist/node/index.cjs +2 -0
- package/dist/node/index.d.cts +543 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +543 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +3 -0
- package/dist/node/index.js.map +1 -0
- package/package.json +80 -0
package/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# @robota-sdk/agent-subagent-runner
|
|
2
|
+
|
|
3
|
+
Child-process subagent runner for the Robota SDK. It runs each subagent in its own Node.js child
|
|
4
|
+
process, talks to it over a typed IPC protocol, and can wrap each job in worktree isolation and stream
|
|
5
|
+
its logs to disk.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @robota-sdk/agent-subagent-runner
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
This package is an optional add-on for `@robota-sdk/agent-framework`. Without it, subagents run
|
|
16
|
+
in-process; with it, each subagent gets process isolation while reporting back to the parent session
|
|
17
|
+
over structured IPC.
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
composition root (e.g. agent-cli)
|
|
21
|
+
└── createChildProcessSubagentRunnerFactory()
|
|
22
|
+
└── ChildProcessSubagentRunner ← this package
|
|
23
|
+
├── spawn() ← a copy of the running artifact in worker mode
|
|
24
|
+
├── IPC messages ← TSubagentWorkerParentMessage / TSubagentWorkerChildMessage
|
|
25
|
+
└── worktree isolation ← vian agent-executor, with an injected worktree adapter
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The runner composes nothing itself. The composition root supplies how to start a copy of itself
|
|
29
|
+
(`workerEntry`), the tool factory and provider registry the child uses, and the worktree adapter.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import {
|
|
35
|
+
createChildProcessSubagentRunnerFactory,
|
|
36
|
+
isSubagentWorkerModeArgv,
|
|
37
|
+
runSubagentWorkerMain,
|
|
38
|
+
} from '@robota-sdk/agent-subagent-runner';
|
|
39
|
+
import type {
|
|
40
|
+
IProviderDefinition,
|
|
41
|
+
IProviderDefinitionConfig,
|
|
42
|
+
IToolWithEventService,
|
|
43
|
+
} from '@robota-sdk/agent-core';
|
|
44
|
+
import type { ISubagentWorktreeAdapter } from '@robota-sdk/agent-executor';
|
|
45
|
+
|
|
46
|
+
declare const providerConfig: IProviderDefinitionConfig;
|
|
47
|
+
// The concrete worktree adapter (git/fs I/O) is owned and injected by the composition root.
|
|
48
|
+
declare const worktreeAdapter: ISubagentWorktreeAdapter;
|
|
49
|
+
|
|
50
|
+
// Your product's tool surface, built at the CHILD's execution root. This package composes nothing:
|
|
51
|
+
// there is no default tool set or provider registry to fall back to.
|
|
52
|
+
declare const createMyTools: (context: { readonly cwd: string }) => IToolWithEventService[];
|
|
53
|
+
declare const myProviderDefinitions: readonly IProviderDefinition[];
|
|
54
|
+
|
|
55
|
+
// Your entry is the worker. Dispatch before starting your app, so a subagent child re-enters here
|
|
56
|
+
// instead of booting the whole product.
|
|
57
|
+
if (isSubagentWorkerModeArgv(process.argv)) {
|
|
58
|
+
runSubagentWorkerMain({
|
|
59
|
+
createTools: createMyTools,
|
|
60
|
+
providerDefinitions: myProviderDefinitions,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const factory = createChildProcessSubagentRunnerFactory({
|
|
65
|
+
// How to start a copy of THIS artifact. There is no default: only this process knows how it was
|
|
66
|
+
// packaged. A bundled build names the file it is running; a single-file compiled binary names
|
|
67
|
+
// nothing, because `process.execPath` is the binary and re-executing it re-enters its entry.
|
|
68
|
+
workerEntry: { execPath: process.execPath, args: [process.argv[1] ?? ''] },
|
|
69
|
+
providerConfig,
|
|
70
|
+
providerDefinitions: myProviderDefinitions, // required: a job whose provider is not here is refused
|
|
71
|
+
logsDir: hostSelectedLogDirectory,
|
|
72
|
+
worktreeAdapter, // required: no concrete git default — inject the port at the composition root
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Pass `factory` as the `subagentRunnerFactory` option of `agent-framework`'s `createAgentRuntime`.
|
|
77
|
+
|
|
78
|
+
## API
|
|
79
|
+
|
|
80
|
+
### Functions
|
|
81
|
+
|
|
82
|
+
| Export | Description |
|
|
83
|
+
| -------------------------------------------------- | --------------------------------------------------------------------------- |
|
|
84
|
+
| `createChildProcessSubagentRunnerFactory(options)` | Returns a `TSubagentRunnerFactory` that spawns subagents in child processes |
|
|
85
|
+
| `isSubagentWorkerModeArgv(argv)` | True when this process was started as a subagent worker |
|
|
86
|
+
| `runSubagentWorkerMain(composition)` | Enters worker mode; refuses loudly (exit 2) without an IPC channel |
|
|
87
|
+
|
|
88
|
+
### Classes
|
|
89
|
+
|
|
90
|
+
| Export | Description |
|
|
91
|
+
| ---------------------------- | -------------------------------------------------------- |
|
|
92
|
+
| `ChildProcessSubagentRunner` | Implements `ISubagentRunner` using `child_process.spawn` |
|
|
93
|
+
|
|
94
|
+
### Types
|
|
95
|
+
|
|
96
|
+
| Export | Description |
|
|
97
|
+
| ------------------------------------ | -------------------------------------------------------------------------------- |
|
|
98
|
+
| `IChildProcessSubagentRunnerOptions` | Options for `createChildProcessSubagentRunnerFactory` |
|
|
99
|
+
| `ISubagentWorkerEntry` | How to spawn a copy of the running artifact in worker mode |
|
|
100
|
+
| `ISubagentWorkerComposition` | What the product composes in the child: tools, provider registry, optional ports |
|
|
101
|
+
| `ISubagentWorkerStartPayload` | IPC payload sent from parent to worker on start |
|
|
102
|
+
| `TSubagentWorkerParentMessage` | Union of all messages the parent sends to the worker |
|
|
103
|
+
| `TSubagentWorkerChildMessage` | Union of all messages the worker sends to the parent |
|
|
104
|
+
| `TSubagentWorkerWireValue` | Serializable value type used in IPC messages |
|
|
105
|
+
|
|
106
|
+
### Type guards and wire helpers
|
|
107
|
+
|
|
108
|
+
| Export | Description |
|
|
109
|
+
| ------------------------------------------------------------------------------- | ------------------------------------------------------ |
|
|
110
|
+
| `isSubagentWorkerParentMessage` | Narrows to `TSubagentWorkerParentMessage` |
|
|
111
|
+
| `isSubagentWorkerChildMessage` | Narrows to `TSubagentWorkerChildMessage` |
|
|
112
|
+
| `SUBAGENT_WORKER_MODE_FLAG` | The argv flag that puts an entry into worker mode |
|
|
113
|
+
| `encodeAgentDefinition` / `decodeAgentDefinitionDto` / `restoreAgentDefinition` | JSON-safe wire form of the subagent's agent definition |
|
|
114
|
+
| `encodeParentContext` / `decodeParentContextDto` / `restoreParentContext` | JSON-safe wire form of the parent's loaded context |
|
|
115
|
+
|
|
116
|
+
## Forked session records
|
|
117
|
+
|
|
118
|
+
If a job includes `resumeSessionId`, the composition root must provide
|
|
119
|
+
`ISubagentWorkerComposition.openSessionStore`. The worker opens that store for the parent request's
|
|
120
|
+
`cwd`, restores the copied record, and uses the same ID and store for the child `Session`, so each
|
|
121
|
+
completed turn is persisted back into the fork record. Only the ID crosses IPC; the conversation does
|
|
122
|
+
not. Jobs without `resumeSessionId` remain transient.
|
|
123
|
+
|
|
124
|
+
## Dependencies
|
|
125
|
+
|
|
126
|
+
- `@robota-sdk/agent-executor` — `ISubagentRunner`, worktree isolation, and background task primitives
|
|
127
|
+
- `@robota-sdk/agent-framework` — `TSubagentRunnerFactory` and session assembly for the worker
|
|
128
|
+
- `@robota-sdk/agent-core` — provider and tool types
|
|
129
|
+
- `@robota-sdk/agent-interface-execution` — subagent job contracts
|
|
130
|
+
- `@robota-sdk/agent-process` — process-tree termination
|
|
131
|
+
|
|
132
|
+
See [docs/SPEC.md](./docs/SPEC.md) for the package contract: required composition, IPC validation, and
|
|
133
|
+
how the provider connection is checked across the process boundary.
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
This package is dual-licensed under the [GNU AGPL-3.0](../../LICENSE) or a [commercial license](../../COMMERCIAL.md). See [LICENSING.md](../../LICENSING.md).
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("node:child_process"),t=require("node:fs"),n=require("node:path"),r=require("@robota-sdk/agent-executor"),i=require("@robota-sdk/agent-process"),a=require("@robota-sdk/agent-core"),o=require("@robota-sdk/agent-framework");function s(e){return{provider:{model:e.provider.model},permissions:e.permissions,defaultTrustLevel:e.defaultTrustLevel,...e.hooks===void 0?{}:{hooks:e.hooks}}}function c(e){return{agentsMd:e.agentsMd,projectNotesMd:e.projectNotesMd}}const l={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}},u={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 d(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function f(e){return Array.isArray(e)&&e.every(e=>typeof e==`string`)}function p(e){return d(e)&&typeof e.filePath==`string`&&typeof e.content==`string`&&typeof e.contentHash==`string`}function m(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`&&(0,a.isModelEffort)(t);case`string[]`:return f(t);case`file-entry[]`:return Array.isArray(t)&&t.every(p)}}function h(e,t){let n={};for(let r of Object.keys(t)){let t=e[r];t!==void 0&&(n[r]=t)}return n}function g(e,t,n){if(!d(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(!m(i.kind,n))return{ok:!1,reason:`${e}.${r}: expected ${i.kind}`}}return{ok:!0,value:h(t,n)}}function _(e){return h(e,l)}function v(e){return g(`agentDefinition`,e,l)}function y(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 b(e){return h(e,u)}function x(e){return g(`parentContext`,e,u)}function S(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 ee(e){return e.sessionTiers===void 0?{}:{sessionTiers:e.sessionTiers}}async function C(e){let{sandboxClient:t,sandboxType:n}=e;return t?.snapshot===void 0||n===void 0?{}:{sandboxProjection:{type:n,snapshotId:await t.snapshot()}}}function w(e,t,n,i,o){let s=n.providerDefinitions,c=(n.providerConfig??t.config.provider).name;if((0,a.findProviderDefinition)(s,c)===void 0)throw new r.BackgroundTaskError(`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 l=E(n.providerConfig,t,e,s),u=(0,r.connectionEnvironmentNames)(l,s),d=(0,r.findConnectionEnvironmentDivergence)(u,i,o);if(d!==void 0)throw new r.BackgroundTaskError(`validation`,`The subagent's environment sets ${d} differently from this session, which would change where its provider connects or which credential it sends; the subagent was not started.`);return{providerProfile:l,connectionCheck:(0,r.sealConnectionEnvironment)(u,o)}}function te(e,t,n){let r=ne(e.request.agentType,t.customAgentRegistry,t.builtInAgents,t.agentDefinitions),i={taskId:e.taskId,request:e.request,...e.worktree?{worktree:e.worktree}:{},agentDefinition:_(re(r,e)),parentConfig:s(t.getParentPermissionRules===void 0?t.config:{...t.config,permissions:t.getParentPermissionRules()}),parentContext:b(c(t.context)),...n.connection??w(e,t,n,process.env,process.env),permissionMode:t.getParentPermissionMode?.()??t.permissionMode,...ee(t),...n.parentSandboxSettings===void 0?{}:{parentSandboxSettings:n.parentSandboxSettings},...n.logsDir?{logsDir:n.logsDir}:{},...n.keepProviderCredential?{keepProviderCredential:!0}:{}};return C(t).then(e=>({...i,...e}))}function ne(e,t,n,i){let a=t?.(e)??n?.find(t=>t.name===e)??i?.find(t=>t.name===e);if(!a)throw new r.BackgroundTaskError(`validation`,`Unknown agent type: ${e}`);return a}function re(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 T(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 E(e,t,n,r){let i=e??t.config.provider,o=(0,a.findProviderDefinition)(r,i.name)?.defaults??{},s=i.baseURL??o.baseURL,c=i.options??o.options,l=T(i,o.apiKey);return{...e===void 0&&t.config.currentProvider!==void 0?{profileName:t.config.currentProvider}:{},type:i.name,model:n.request.model??i.model,...l,...s===void 0?{}:{baseURL:s},...i.timeout===void 0?{}:{timeout:i.timeout},...c===void 0?{}:{options:c}}}function D(e){return typeof e==`object`&&!!e}function ie(e){if(!D(e)||!O(e,`nonce`)||!O(e,`digest`))return!1;let t=e.names;return Array.isArray(t)&&t.every(e=>typeof e==`string`)}function O(e,t){return typeof e[t]==`string`}function k(e,t){return O(e,t)}function ae(e,t){return O(e,t)}function oe(e,t){return e[t]===void 0||typeof e[t]==`string`}function se(e){if(e.usage===void 0)return!0;let t=e.usage;return D(t)?typeof t.promptTokens==`number`&&typeof t.completionTokens==`number`&&typeof t.totalTokens==`number`:!1}function ce(e){if(e.composedToolNames===void 0)return!0;let t=e.composedToolNames;return Array.isArray(t)?t.every(e=>typeof e==`string`):!1}function le(e){return!D(e)||!ae(e,`taskId`)||!D(e.request)||!k(e.request,`agentType`)||!k(e.request,`prompt`)||!k(e.request,`permissionPolicy`)||!k(e.request,`cwd`)||!oe(e.request,`resumeSessionId`)||e.worktree!==void 0&&(!D(e.worktree)||!O(e.worktree,`path`))||!v(e.agentDefinition).ok||!D(e.parentConfig)||!x(e.parentContext).ok||!D(e.providerProfile)||!O(e.providerProfile,`type`)||!O(e.providerProfile,`model`)||e.parentSandboxSettings!==void 0&&!D(e.parentSandboxSettings)?!1:ie(e.connectionCheck)}function A(e){if(!D(e)||!O(e,`type`))return!1;switch(e.type){case`start`:return le(e.payload);case`send`:return O(e,`prompt`);case`cancel`:return e.reason===void 0||typeof e.reason==`string`;case`sandbox_settings`:return D(e.settings);case`permission_response`:return O(e,`requestId`)&&ue(e.result);default:return!1}}function ue(e){return typeof e==`boolean`||e===`allow-session`||e===`allow-project`}function j(e){if(!D(e)||!O(e,`type`))return!1;switch(e.type){case`ready`:return ce(e);case`text_delta`:return O(e,`delta`);case`tool_start`:return O(e,`toolName`);case`tool_end`:return O(e,`toolName`)&&typeof e.success==`boolean`;case`permission_request`:return O(e,`requestId`)&&O(e,`toolName`);case`result`:return O(e,`output`)&&se(e);case`error`:return O(e,`message`);case`cancelled`:return e.reason===void 0||typeof e.reason==`string`;default:return!1}}const de=process.platform!==`win32`;function fe(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 M=new WeakMap;function N(e){let t=e.stderr;if(!t)return;let n=(0,a.createBoundedOutput)({maxBytes:4096,retain:`tail`,truncationMarker:()=>``});M.set(e,n),t.on(`error`,()=>{}),t.on(`data`,e=>n.append(e))}function P(e){return(M.get(e)?.toString()??``).trim()}function F(e,t,n,i,a,o){switch(e.type){case`ready`:t();break;case`result`:n(e);break;case`error`:i(new r.BackgroundTaskError(`runner`,e.message));break;case`cancelled`:i(new r.BackgroundTaskError(`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:I(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:i(new r.BackgroundTaskError(`runner`,`Unhandled subagent worker message`))}}function I(e){if(!e)return;let t=Object.values(e)[0];if(t!==void 0)return typeof t==`object`?JSON.stringify(t):String(t)}function L(e,t){return new Promise((n,i)=>{if(!e.connected){i(new r.BackgroundTaskError(`crash`,`Subagent worker IPC channel is closed`));return}e.send(t,e=>{if(e){i(e);return}n()})})}async function R(e,t){await(0,i.killProcessTree)(e.child,{graceMs:e.killGraceMs,processGroup:de,preKill:async()=>{e.child.connected&&(await L(e.child,{type:`cancel`,reason:t}).catch(()=>void 0),await fe(e.child,e.killGraceMs))}})}function z(e){return new Promise((t,n)=>{new B(e,t,n).start()})}var B=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 i=e.handshakeBudgetMs;this.handshakeBudgetMs=i!==void 0&&i>0?i:3e4,this.payload=e.payload.catch(e=>{this.rejectOnce(e instanceof Error?e:Error(String(e)))}),this.timeoutTimer=pe(this.options.runtime,e=>this.rejectOnce(e)),this.handshakeTimer=setTimeout(()=>{this.ready||this.settled||(R(this.options.runtime,`Subagent worker never signalled ready`),this.rejectOnce(new r.BackgroundTaskError(`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:L(e,{type:`start`,payload:t})).catch(e=>{this.rejectOnce(e instanceof Error?e:Error(String(e)))})};onMessage=e=>{if(!j(e)){this.rejectOnce(new r.BackgroundTaskError(`runner`,`Received malformed subagent worker message`));return}this.ready=!0,clearTimeout(this.handshakeTimer);let{job:t}=this.options.runtime;F(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 L(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 r.BackgroundTaskError(`crash`,e.message))};onExit=(e,t)=>{this.settled||this.rejectOnce(new r.BackgroundTaskError(`crash`,ge(e,t,P(this.options.runtime.child))))};resolveOnce=e=>{this.settled||(this.settled=!0,this.settleAfterPendingPermissionRequests(()=>{let{runtime:t,resolveTranscriptPath:n}=this.options;this.resolve(me(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 V(e){let t=!1,n=()=>{};return{promise:new Promise((e,t)=>{n=t}),reject(i){t||(t=!0,n(new r.BackgroundTaskError(`runner`,i??`Subagent job cancelled: ${e}`)))}}}function pe(e,t){if(e.job.request.timeoutMs)return setTimeout(()=>{R(e,`Subagent worker timed out`),t(new r.BackgroundTaskError(`timeout`,`Subagent worker timed out`))},e.job.request.timeoutMs)}function me(e,t,n,r){let i=n(e),a=he(r);return{taskId:e.taskId,output:t.output,...i?{metadata:{transcriptPath:i,logPath:i}}:{},...t.usage?{usage:t.usage}:{},...a?{deniedToolCalls:a}:{}}}function he(e){let t=Object.values(e).reduce((e,t)=>e+t,0);return t===0?void 0:{total:t,byReason:{...e}}}function ge(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 _e(e){return e.includes(H)}const ve=process.platform!==`win32`;function ye(e){return t=>{let n=new U(t,e);return e.worktreeIsolation===!1?n:(0,r.createWorktreeSubagentRunner)({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??i.DEFAULT_KILL_GRACE_MS,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,i=this.inheritEnvironment?{...process.env,...this.env??{}}:{...this.env??{}},a=w(t,this.deps,{...this.providerConfig===void 0?{}:{providerConfig:this.providerConfig},providerDefinitions:this.providerDefinitions},this.expectedEnvironment??process.env,i),o=(0,e.spawn)(n.execPath,[...n.execArgv??[],...n.args,H],{cwd:(0,r.subagentExecutionRoot)(t),env:i,stdio:[`ignore`,`ignore`,`pipe`,`ipc`],detached:ve});N(o),this.forwardSandboxSettingChanges(o);let s={job:t,child:o,killGraceMs:this.killGraceMs},c=this.createStartPayload(t,a),l=new AbortController,u=this.permissionApprover(t),d=z({runtime:s,payload:c,...this.handshakeBudgetMs===void 0?{}:{handshakeBudgetMs:this.handshakeBudgetMs},resolveTranscriptPath:e=>this.resolveTranscriptPath(e),permissionAbort:l,...u===void 0?{}:{permissionApprover:u}}),f=V(t.taskId);d.catch(()=>void 0);let p=Promise.race([d,f.promise]);p.catch(()=>void 0);let m=this.resolveTranscriptPath(t);return{taskId:t.taskId,...o.pid!==void 0&&{pid:o.pid},...m!==void 0&&{transcriptPath:m,logPath:m},result:p,cancel:async e=>{l.abort(e),f.reject(e),await R(s,e)},send:async e=>{await L(o,{type:`send`,prompt:e})},...m!==void 0&&{readLog:async e=>be(t.taskId,m,e)}}}createStartPayload(e,t){let n=this.parentSandboxSettings?.();return te(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&&L(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(0,n.join)(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 be(e,n,i){return(0,t.existsSync)(n)?(0,r.createBackgroundTaskLogPage)(e,(0,t.readFileSync)(n,`utf8`).split(/\r?\n/).filter(Boolean),i):{taskId:e,cursor:i,lines:[]}}function xe(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.`);(0,o.restoreSessionRecordIntoSession)(n,r,t)}}function Se(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 Ce(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 we(e,t=!1,n=process.env){t||e.apiKeyEnv===void 0||delete n[e.apiKeyEnv]}const Te={write:()=>{},writeLine:()=>{},writeMarkdown:()=>{},writeError:()=>{},prompt:()=>Promise.resolve(``),select:()=>Promise.resolve(0),spinner:()=>({stop:()=>{},update:()=>{}})};let W=null,G=!1;const K=new Map;let Ee=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 De(e){try{return(0,a.sumHistoryUsage)(e.getFullHistory())}catch{return}}async function Oe(e,t){try{if(!(0,r.verifyConnectionEnvironment)(e.connectionCheck,process.env))throw Error(`The provider connection environment changed after the parent checked it; the subagent was not started.`);let n=await Ce(e.sandboxProjection,t.sandboxFactories),i=(0,r.createProviderFromExactProfile)(e.providerProfile,e.request.model,t.providerDefinitions);we(e.providerProfile,e.keepProviderCredential===!0);let a=e.logsDir?(0,o.createSubagentLogger)(e.request.parentSessionId,e.taskId,e.logsDir):void 0,s=Se(e,t),c=q??e.parentSandboxSettings;J=n===void 0?t.createSandbox?.({cwd:(0,r.subagentExecutionRoot)(e),...c===void 0?{}:{parentSettings:c}}):void 0;let l=n??J?.client;W=(0,o.createSubagentSession)({agentDefinition:y(e.agentDefinition),parentConfig:e.parentConfig,parentContext:S(e.parentContext),parentTools:t.createTools({cwd:(0,r.subagentExecutionRoot)(e),...l===void 0?{}:{sandboxClient:l},...e.sessionTiers===void 0?{}:{sessionTiers:e.sessionTiers}}),cwd:(0,r.subagentExecutionRoot)(e),provider:i,terminal:Te,sessionId:e.request.resumeSessionId??e.taskId,...s===void 0?{}:{sessionStore:s},...a?{sessionLogger:a}:{},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:(0,r.subagentExecutionRoot)(e),...l===void 0?{}:{sandboxClient:l}})},onTextDelta:e=>Z({type:`text_delta`,delta:e}),onToolExecution:$,permissionHandler:ke}),xe(e,W,s);let u=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 d=De(W);Q({type:`result`,output:u,...d?{usage:d}:{}},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 ke(e,t){let n=`perm_${++Ee}`;return new Promise(r=>{K.set(n,r),Z({type:`permission_request`,requestId:n,toolName:e,toolArgs:t})})}function Ae(e,t){let n=K.get(e);n!==void 0&&(K.delete(e),n(t))}function $(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 je(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 Me(e){G=!0,W?.abort(),Z({type:`cancelled`,reason:e}),await W?.shutdown({reason:`other`}).catch(()=>void 0),setTimeout(()=>process.exit(130),0)}function Ne(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 Pe(e){q=e;try{J?.applyParentSettings?.(e)}catch(e){Y??=e instanceof Error?e.message:String(e),W?.abort()}}function Fe(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(!A(t)){Z({type:`error`,message:`Malformed subagent worker parent message`});return}switch(t.type){case`start`:X=X.then(()=>Oe(t.payload,e));break;case`send`:je(t.prompt);break;case`cancel`:Me(t.reason);break;case`sandbox_settings`:Pe(t.settings);break;case`permission_response`:Ae(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=Ne(e);Z({type:`ready`,...t?{composedToolNames:t}:{}})}exports.ChildProcessSubagentRunner=U,exports.SUBAGENT_WORKER_MODE_FLAG=H,exports.createChildProcessSubagentRunnerFactory=ye,exports.decodeAgentDefinitionDto=v,exports.decodeParentContextDto=x,exports.encodeAgentDefinition=_,exports.encodeParentContext=b,exports.isSubagentWorkerChildMessage=j,exports.isSubagentWorkerModeArgv=_e,exports.isSubagentWorkerParentMessage=A,exports.restoreAgentDefinition=y,exports.restoreParentContext=S,exports.runSubagentWorkerMain=Fe;
|