@robota-sdk/agent-subagent-runner 3.0.0-beta.79 → 3.0.0-beta.81
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/README.md +50 -14
- package/dist/node/index.cjs +2 -1
- package/dist/node/index.d.cts +436 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +357 -17
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +2 -1
- package/dist/node/index.js.map +1 -1
- package/package.json +31 -18
- package/dist/node/child-process-subagent-ipc-BKEo2kRL.js +0 -2
- package/dist/node/child-process-subagent-ipc-BKEo2kRL.js.map +0 -1
- package/dist/node/child-process-subagent-ipc-C4zByGSA.cjs +0 -1
- package/dist/node/child-process-subagent-worker.cjs +0 -1
- package/dist/node/child-process-subagent-worker.d.ts +0 -1
- package/dist/node/child-process-subagent-worker.js +0 -2
- package/dist/node/child-process-subagent-worker.js.map +0 -1
package/README.md
CHANGED
|
@@ -26,16 +26,42 @@ agent-cli
|
|
|
26
26
|
```typescript
|
|
27
27
|
import {
|
|
28
28
|
createChildProcessSubagentRunnerFactory,
|
|
29
|
-
|
|
29
|
+
isSubagentWorkerModeArgv,
|
|
30
|
+
runSubagentWorkerMain,
|
|
30
31
|
} from '@robota-sdk/agent-subagent-runner';
|
|
31
|
-
import type {
|
|
32
|
+
import type {
|
|
33
|
+
IProviderDefinition,
|
|
34
|
+
IProviderDefinitionConfig,
|
|
35
|
+
IToolWithEventService,
|
|
36
|
+
} from '@robota-sdk/agent-core';
|
|
37
|
+
import type { ISubagentWorktreeAdapter } from '@robota-sdk/agent-executor';
|
|
32
38
|
|
|
33
39
|
declare const providerConfig: IProviderDefinitionConfig;
|
|
40
|
+
// The concrete worktree adapter (git/fs I/O) is owned and injected by the composition root.
|
|
41
|
+
declare const worktreeAdapter: ISubagentWorktreeAdapter;
|
|
42
|
+
|
|
43
|
+
// ARCH-021: YOUR product's surface, built at the CHILD's execution root. This package composes
|
|
44
|
+
// nothing — an optional parameter falling back to defaults is exactly the defect the port removes.
|
|
45
|
+
declare const createMyTools: (context: { readonly cwd: string }) => IToolWithEventService[];
|
|
46
|
+
declare const myProviderDefinitions: readonly IProviderDefinition[];
|
|
47
|
+
|
|
48
|
+
// DIST-006: your entry IS the worker. Dispatch before starting your app, so a subagent child
|
|
49
|
+
// re-enters here instead of booting the whole product.
|
|
50
|
+
if (isSubagentWorkerModeArgv(process.argv)) {
|
|
51
|
+
runSubagentWorkerMain({
|
|
52
|
+
createTools: createMyTools,
|
|
53
|
+
providerDefinitions: myProviderDefinitions,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
34
56
|
|
|
35
57
|
const factory = createChildProcessSubagentRunnerFactory({
|
|
36
|
-
|
|
58
|
+
// How to start a copy of THIS artifact. There is no default: only this process knows how it was
|
|
59
|
+
// packaged. A bundled build names the file it is running; a single-file compiled binary names
|
|
60
|
+
// nothing, because `process.execPath` is the binary and re-executing it re-enters its entry.
|
|
61
|
+
workerEntry: { execPath: process.execPath, args: [process.argv[1] ?? ''] },
|
|
37
62
|
providerConfig,
|
|
38
63
|
logsDir: '.robota/logs',
|
|
64
|
+
worktreeAdapter, // required: no concrete git default — inject the port at the composition root
|
|
39
65
|
});
|
|
40
66
|
```
|
|
41
67
|
|
|
@@ -48,23 +74,33 @@ Pass `factory` to `createAgentRuntime({ subagentRunnerFactory: factory })`.
|
|
|
48
74
|
| Export | Description |
|
|
49
75
|
| -------------------------------------------------- | --------------------------------------------------------------------------- |
|
|
50
76
|
| `createChildProcessSubagentRunnerFactory(options)` | Returns a `TSubagentRunnerFactory` that spawns subagents in child processes |
|
|
51
|
-
| `
|
|
77
|
+
| `isSubagentWorkerModeArgv(argv)` | True when this process was started as a subagent worker |
|
|
78
|
+
| `runSubagentWorkerMain()` | Enters worker mode; refuses loudly (exit 2) without an IPC channel |
|
|
52
79
|
|
|
53
80
|
### Classes
|
|
54
81
|
|
|
55
|
-
| Export | Description
|
|
56
|
-
| ---------------------------- |
|
|
57
|
-
| `ChildProcessSubagentRunner` | Implements `ISubagentRunner` using `child_process.
|
|
82
|
+
| Export | Description |
|
|
83
|
+
| ---------------------------- | -------------------------------------------------------- |
|
|
84
|
+
| `ChildProcessSubagentRunner` | Implements `ISubagentRunner` using `child_process.spawn` |
|
|
58
85
|
|
|
59
86
|
### Types
|
|
60
87
|
|
|
61
|
-
| Export | Description
|
|
62
|
-
| ------------------------------------ |
|
|
63
|
-
| `IChildProcessSubagentRunnerOptions` | Options for `createChildProcessSubagentRunnerFactory`
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
88
|
+
| Export | Description |
|
|
89
|
+
| ------------------------------------ | ---------------------------------------------------------- |
|
|
90
|
+
| `IChildProcessSubagentRunnerOptions` | Options for `createChildProcessSubagentRunnerFactory` |
|
|
91
|
+
| `ISubagentWorkerEntry` | How to spawn a copy of the running artifact in worker mode |
|
|
92
|
+
| `ISubagentWorkerStartPayload` | IPC payload sent from parent to worker on start |
|
|
93
|
+
| `TSubagentWorkerParentMessage` | Union of all messages the parent sends to the worker |
|
|
94
|
+
| `TSubagentWorkerChildMessage` | Union of all messages the worker sends to the parent |
|
|
95
|
+
| `TSubagentWorkerWireValue` | Serializable value type used in IPC messages |
|
|
96
|
+
|
|
97
|
+
### Forked session records
|
|
98
|
+
|
|
99
|
+
If a job includes `resumeSessionId`, the composition root must provide
|
|
100
|
+
`ISubagentWorkerComposition.openSessionStore`. The worker opens that store for the parent request's
|
|
101
|
+
`cwd`, restores the copied record, and uses the same ID and store for the child `Session`, so each
|
|
102
|
+
completed turn is persisted back into the fork record. Only the ID crosses IPC; the conversation does
|
|
103
|
+
not. Jobs without `resumeSessionId` remain transient.
|
|
68
104
|
|
|
69
105
|
### Type Guards
|
|
70
106
|
|
package/dist/node/index.cjs
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./child-process-subagent-ipc-C4zByGSA.cjs");let t=require("node:child_process"),n=require("node:fs"),r=require("node:path"),i=require("@robota-sdk/agent-executor"),a=require("@robota-sdk/agent-framework"),o=require("@robota-sdk/agent-process"),s=require("node:url");const c=process.platform!==`win32`;function l(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)})}function u(e,t,n,r,a){switch(e.type){case`ready`:t();break;case`result`:n(e);break;case`error`:r(new i.BackgroundTaskError(`runner`,e.message));break;case`cancelled`:r(new i.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:d(e.toolArgs)});break;case`tool_end`:a?.({type:`background_task_tool_end`,toolName:e.toolName,success:e.success});break;default:r(new i.BackgroundTaskError(`runner`,`Unhandled subagent worker message`))}}function d(e){if(!e)return;let t=Object.values(e)[0];if(t!==void 0)return typeof t==`object`?JSON.stringify(t):String(t)}function f(e,t){return new Promise((n,r)=>{if(!e.connected){r(new i.BackgroundTaskError(`crash`,`Subagent worker IPC channel is closed`));return}e.send(t,e=>{if(e){r(e);return}n()})})}async function p(e,t){await(0,o.killProcessTree)(e.child,{graceMs:e.killGraceMs,processGroup:c,preKill:async()=>{e.child.connected&&(await f(e.child,{type:`cancel`,reason:t}).catch(()=>void 0),await l(e.child,e.killGraceMs))}})}function m(e){return new Promise((t,n)=>{new h(e,t,n).start()})}var h=class{options;resolve;reject;settled=!1;started=!1;timeoutTimer;constructor(e,t,n){this.options=e,this.resolve=t,this.reject=n,this.timeoutTimer=_(this.options.runtime,e=>this.rejectOnce(e))}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;f(e,{type:`start`,payload:this.options.payload}).catch(e=>{this.rejectOnce(e instanceof Error?e:Error(String(e)))})};onMessage=t=>{if(!e.t(t)){this.rejectOnce(new i.BackgroundTaskError(`runner`,`Received malformed subagent worker message`));return}let{job:n}=this.options.runtime;u(t,this.startWorker,this.resolveOnce,this.rejectOnce,n.emit)};onError=e=>{this.rejectOnce(new i.BackgroundTaskError(`crash`,e.message))};onExit=(e,t)=>{this.settled||this.rejectOnce(new i.BackgroundTaskError(`crash`,y(e,t)))};resolveOnce=e=>{if(this.settled)return;this.settled=!0,this.clearTimers(),this.cleanup();let{runtime:t,resolveTranscriptPath:n}=this.options;this.resolve(v(t.job,e,n))};rejectOnce=e=>{this.settled||(this.settled=!0,this.clearTimers(),this.cleanup(),this.reject(e))};clearTimers(){this.timeoutTimer&&clearTimeout(this.timeoutTimer)}cleanup(){let{child:e}=this.options.runtime;e.off(`message`,this.onMessage),e.off(`error`,this.onError),e.off(`exit`,this.onExit)}};function g(e){let t=!1,n=()=>{};return{promise:new Promise((e,t)=>{n=t}),reject(r){t||(t=!0,n(new i.BackgroundTaskError(`runner`,r??`Subagent job cancelled: ${e}`)))}}}function _(e,t){if(e.job.request.timeoutMs)return setTimeout(()=>{p(e,`Subagent worker timed out`),t(new i.BackgroundTaskError(`timeout`,`Subagent worker timed out`))},e.job.request.timeoutMs)}function v(e,t,n){let r=n(e);return{jobId:e.jobId,output:t.output,...r?{metadata:{transcriptPath:r,logPath:r}}:{},...t.usage?{usage:t.usage}:{}}}function y(e,t){return`Subagent worker exited before result: ${t===null?`exit code ${e===null?`unknown`:e}`:`signal ${t}`}`}const b=process.platform!==`win32`;function x(e){return t=>{let n=new S(t,e);return e.worktreeIsolation===!1?n:(0,i.createWorktreeSubagentRunner)({runner:n,worktreeAdapter:e.worktreeAdapter??(0,i.createGitWorktreeIsolationAdapter)(),hooks:t.config.hooks,hookTypeExecutors:t.hookTypeExecutors})}}var S=class{deps;workerPath;execArgv;killGraceMs;providerConfig;env;logsDir;constructor(e,t){this.deps=e,this.workerPath=t.workerPath,this.execArgv=t.execArgv,this.killGraceMs=t.killGraceMs??o.DEFAULT_KILL_GRACE_MS,this.providerConfig=t.providerConfig,this.env=t.env,this.logsDir=t.logsDir}start(e){let n=(0,t.fork)(this.workerPath,[],{cwd:e.request.cwd,env:{...process.env,...this.env??{}},execArgv:this.execArgv??E(this.workerPath),stdio:[`ignore`,`ignore`,`ignore`,`ipc`],detached:b}),r={job:e,child:n,killGraceMs:this.killGraceMs},i=m({runtime:r,payload:this.createStartPayload(e),resolveTranscriptPath:e=>this.resolveTranscriptPath(e)}),a=g(e.jobId);i.catch(()=>void 0);let o=Promise.race([i,a.promise]);o.catch(()=>void 0);let s=this.resolveTranscriptPath(e);return{jobId:e.jobId,...n.pid!==void 0&&{pid:n.pid},...s!==void 0&&{transcriptPath:s,logPath:s},result:o,cancel:async e=>{a.reject(e),await p(r,e)},send:async e=>{await f(n,{type:`send`,prompt:e})},...s!==void 0&&{readLog:async t=>D(e.jobId,s,t)}}}createStartPayload(e){let t=C(e.request.type,this.deps.customAgentRegistry);return{jobId:e.jobId,request:e.request,agentDefinition:w(t,e),parentConfig:this.deps.config,parentContext:this.deps.context,providerProfile:T(this.providerConfig,this.deps,e),permissionMode:this.deps.permissionMode,...this.logsDir?{logsDir:this.logsDir}:{}}}resolveTranscriptPath(e){if(this.logsDir)return(0,r.join)(this.logsDir,e.request.parentSessionId,`subagents`,`${e.jobId}.jsonl`)}};function C(e,t){let n=t?.(e)??(0,a.getBuiltInAgent)(e);if(!n)throw new i.BackgroundTaskError(`validation`,`Unknown agent type: ${e}`);return n}function w(e,t){return{...e,...t.request.model?{model:t.request.model}:{},...t.request.allowedTools?{tools:t.request.allowedTools}:{},...t.request.disallowedTools?{disallowedTools:t.request.disallowedTools}:{}}}function T(e,t,n){let r=e??t.config.provider;return{profileName:t.config.currentProvider,type:r.name,model:n.request.model??r.model,apiKey:r.apiKey,baseURL:r.baseURL,timeout:r.timeout,options:r.options}}function E(e){return!e.endsWith(`.ts`)||process.execArgv.some(e=>e.includes(`tsx`))?process.execArgv:[...process.execArgv,`--import`,`tsx`]}function D(e,t,r){return(0,n.existsSync)(t)?(0,i.createBackgroundTaskLogPage)(e,(0,n.readFileSync)(t,`utf8`).split(/\r?\n/).filter(Boolean),r):{taskId:e,cursor:r,lines:[]}}function O(){return(0,r.join)((0,r.dirname)((0,s.fileURLToPath)(require("url").pathToFileURL(__filename).href)),`child-process-subagent-worker.js`)}exports.ChildProcessSubagentRunner=S,exports.createChildProcessSubagentRunnerFactory=x,exports.getDefaultSubagentWorkerPath=O,exports.isSubagentWorkerChildMessage=e.t,exports.isSubagentWorkerParentMessage=e.n;
|
|
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 ee(e){return Array.isArray(e)&&e.every(e=>typeof e==`string`)}function te(e){return d(e)&&typeof e.filePath==`string`&&typeof e.content==`string`&&typeof e.contentHash==`string`}function ne(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 ee(t);case`file-entry[]`:return Array.isArray(t)&&t.every(te)}}function f(e,t){let n={};for(let r of Object.keys(t)){let t=e[r];t!==void 0&&(n[r]=t)}return n}function p(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(!ne(i.kind,n))return{ok:!1,reason:`${e}.${r}: expected ${i.kind}`}}return{ok:!0,value:f(t,n)}}function m(e){return f(e,l)}function h(e){return p(`agentDefinition`,e,l)}function g(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 _(e){return f(e,u)}function v(e){return p(`parentContext`,e,u)}function y(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 b(e){return e.sessionTiers===void 0?{}:{sessionTiers:e.sessionTiers}}async function x(e){let{sandboxClient:t,sandboxType:n}=e;return t?.snapshot===void 0||n===void 0?{}:{sandboxProjection:{type:n,snapshotId:await t.snapshot()}}}function S(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=T(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 re(e,t,n){let r=ie(e.request.agentType,t.customAgentRegistry,t.builtInAgents,t.agentDefinitions),i={taskId:e.taskId,request:e.request,...e.worktree?{worktree:e.worktree}:{},agentDefinition:m(C(r,e)),parentConfig:s(t.getParentPermissionRules===void 0?t.config:{...t.config,permissions:t.getParentPermissionRules()}),parentContext:_(c(t.context)),...n.connection??S(e,t,n,process.env,process.env),permissionMode:t.permissionMode,...b(t),...n.logsDir?{logsDir:n.logsDir}:{}};return x(t).then(e=>({...i,...e}))}function ie(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 C(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 w(e,t){return 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}}function T(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=w(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 E(e){return typeof e==`object`&&!!e}function ae(e){if(!E(e)||!D(e,`nonce`)||!D(e,`digest`))return!1;let t=e.names;return Array.isArray(t)&&t.every(e=>typeof e==`string`)}function D(e,t){return typeof e[t]==`string`}function O(e,t){return D(e,t)}function oe(e,t){return D(e,t)}function se(e,t){return e[t]===void 0||typeof e[t]==`string`}function ce(e){if(e.usage===void 0)return!0;let t=e.usage;return E(t)?typeof t.promptTokens==`number`&&typeof t.completionTokens==`number`&&typeof t.totalTokens==`number`:!1}function k(e){if(e.composedToolNames===void 0)return!0;let t=e.composedToolNames;return Array.isArray(t)?t.every(e=>typeof e==`string`):!1}function A(e){return!E(e)||!oe(e,`taskId`)||!E(e.request)||!O(e.request,`agentType`)||!O(e.request,`prompt`)||!O(e.request,`permissionPolicy`)||!O(e.request,`cwd`)||!se(e.request,`resumeSessionId`)||e.worktree!==void 0&&(!E(e.worktree)||!D(e.worktree,`path`))||!h(e.agentDefinition).ok||!E(e.parentConfig)||!v(e.parentContext).ok||!E(e.providerProfile)||!D(e.providerProfile,`type`)||!D(e.providerProfile,`model`)?!1:ae(e.connectionCheck)}function j(e){if(!E(e)||!D(e,`type`))return!1;switch(e.type){case`start`:return A(e.payload);case`send`:return D(e,`prompt`);case`cancel`:return e.reason===void 0||typeof e.reason==`string`;default:return!1}}function M(e){if(!E(e)||!D(e,`type`))return!1;switch(e.type){case`ready`:return k(e);case`text_delta`:return D(e,`delta`);case`tool_start`:return D(e,`toolName`);case`tool_end`:return D(e,`toolName`)&&typeof e.success==`boolean`;case`result`:return D(e,`output`)&&ce(e);case`error`:return D(e,`message`);case`cancelled`:return e.reason===void 0||typeof e.reason==`string`;default:return!1}}const N=process.platform!==`win32`;function P(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 F=new WeakMap;function I(e){let t=e.stderr;if(!t)return;let n=(0,a.createBoundedOutput)({maxBytes:4096,retain:`tail`,truncationMarker:()=>``});F.set(e,n),t.on(`error`,()=>{}),t.on(`data`,e=>n.append(e))}function L(e){return(F.get(e)?.toString()??``).trim()}function R(e,t,n,i,a){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:z(e.toolArgs)});break;case`tool_end`:a?.({type:`background_task_tool_end`,toolName:e.toolName,success:e.success});break;default:i(new r.BackgroundTaskError(`runner`,`Unhandled subagent worker message`))}}function z(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,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 V(e,t){await(0,i.killProcessTree)(e.child,{graceMs:e.killGraceMs,processGroup:N,preKill:async()=>{e.child.connected&&(await B(e.child,{type:`cancel`,reason:t}).catch(()=>void 0),await P(e.child,e.killGraceMs))}})}function H(e){return new Promise((t,n)=>{new U(e,t,n).start()})}var U=class{options;resolve;reject;settled=!1;started=!1;ready=!1;timeoutTimer;handshakeTimer;handshakeBudgetMs;payload;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=G(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 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:B(e,{type:`start`,payload:t})).catch(e=>{this.rejectOnce(e instanceof Error?e:Error(String(e)))})};onMessage=e=>{if(!M(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;R(e,this.startWorker,this.resolveOnce,this.rejectOnce,t.emit)};onError=e=>{this.rejectOnce(new r.BackgroundTaskError(`crash`,e.message))};onExit=(e,t)=>{this.settled||this.rejectOnce(new r.BackgroundTaskError(`crash`,K(e,t,L(this.options.runtime.child))))};resolveOnce=e=>{if(this.settled)return;this.settled=!0,this.clearTimers(),this.cleanup();let{runtime:t,resolveTranscriptPath:n}=this.options;this.resolve(le(t.job,e,n))};rejectOnce=e=>{this.settled||(this.settled=!0,this.clearTimers(),this.cleanup(),this.reject(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 W(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 G(e,t){if(e.job.request.timeoutMs)return setTimeout(()=>{V(e,`Subagent worker timed out`),t(new r.BackgroundTaskError(`timeout`,`Subagent worker timed out`))},e.job.request.timeoutMs)}function le(e,t,n){let r=n(e);return{taskId:e.taskId,output:t.output,...r?{metadata:{transcriptPath:r,logPath:r}}:{},...t.usage?{usage:t.usage}:{}}}function K(e,t,n){return`Subagent worker exited before result: ${t===null?`exit code ${e===null?`unknown`:e}`:`signal ${t}`}${n.length>0?`: ${n}`:``}`}const q=`--__robota-subagent-worker`;function ue(e){return e.includes(q)}const de=process.platform!==`win32`;function fe(e){return t=>{let n=new J(t,e);return e.worktreeIsolation===!1?n:(0,r.createWorktreeSubagentRunner)({runner:n,worktreeAdapter:e.worktreeAdapter,hooks:t.config.hooks,hookTypeExecutors:t.hookTypeExecutors})}}var J=class{deps;workerEntry;killGraceMs;handshakeBudgetMs;providerConfig;providerDefinitions;env;logsDir;constructor(e,t){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.logsDir=t.logsDir}start(t){let n=this.workerEntry,i={...process.env,...this.env??{}},a=S(t,this.deps,{...this.providerConfig===void 0?{}:{providerConfig:this.providerConfig},providerDefinitions:this.providerDefinitions},process.env,i),o=(0,e.spawn)(n.execPath,[...n.execArgv??[],...n.args,q],{cwd:(0,r.subagentExecutionRoot)(t),env:i,stdio:[`ignore`,`ignore`,`pipe`,`ipc`],detached:de});I(o);let s={job:t,child:o,killGraceMs:this.killGraceMs},c=H({runtime:s,payload:this.createStartPayload(t,a),...this.handshakeBudgetMs===void 0?{}:{handshakeBudgetMs:this.handshakeBudgetMs},resolveTranscriptPath:e=>this.resolveTranscriptPath(e)}),l=W(t.taskId);c.catch(()=>void 0);let u=Promise.race([c,l.promise]);u.catch(()=>void 0);let d=this.resolveTranscriptPath(t);return{taskId:t.taskId,...o.pid!==void 0&&{pid:o.pid},...d!==void 0&&{transcriptPath:d,logPath:d},result:u,cancel:async e=>{l.reject(e),await V(s,e)},send:async e=>{await B(o,{type:`send`,prompt:e})},...d!==void 0&&{readLog:async e=>pe(t.taskId,d,e)}}}createStartPayload(e,t){return re(e,this.deps,{connection:t,providerDefinitions:this.providerDefinitions,...this.logsDir===void 0?{}:{logsDir:this.logsDir}})}resolveTranscriptPath(e){if(this.logsDir)return(0,n.join)(this.logsDir,e.request.parentSessionId,`subagents`,`${e.taskId}.jsonl`)}};function pe(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 me(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 he(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 ge(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)}const _e={write:()=>{},writeLine:()=>{},writeMarkdown:()=>{},writeError:()=>{},prompt:()=>Promise.resolve(``),select:()=>Promise.resolve(0),spinner:()=>({stop:()=>{},update:()=>{}})};let Y=null,X=!1,Z=Promise.resolve();function Q(e){process.send&&process.send(e)}function $(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 ve(e){try{return(0,a.sumHistoryUsage)(e.getFullHistory())}catch{return}}async function ye(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 ge(e.sandboxProjection,t.sandboxFactories),i=(0,r.createProviderFromExactProfile)(e.providerProfile,e.request.model,t.providerDefinitions),a=e.logsDir?(0,o.createSubagentLogger)(e.request.parentSessionId,e.taskId,e.logsDir):void 0,s=he(e,t);Y=(0,o.createSubagentSession)({agentDefinition:g(e.agentDefinition),parentConfig:e.parentConfig,parentContext:y(e.parentContext),parentTools:t.createTools({cwd:(0,r.subagentExecutionRoot)(e),...n===void 0?{}:{sandboxClient:n},...e.sessionTiers===void 0?{}:{sessionTiers:e.sessionTiers}}),cwd:(0,r.subagentExecutionRoot)(e),provider:i,terminal:_e,sessionId:e.request.resumeSessionId??e.taskId,...s===void 0?{}:{sessionStore:s},...a?{sessionLogger:a}:{},permissionMode:e.permissionMode,...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()},onTextDelta:e=>Q({type:`text_delta`,delta:e}),onToolExecution:be}),me(e,Y,s);let c=await Y.run(e.request.prompt);if(X){$({type:`cancelled`,reason:`Subagent worker cancelled`},130);return}let l=ve(Y);$({type:`result`,output:c,...l?{usage:l}:{}},0)}catch(e){if(X){$({type:`cancelled`,reason:`Subagent worker cancelled`},130);return}$({type:`error`,message:e instanceof Error?e.message:String(e)},0)}}function be(e){if(e.type===`start`){Q({type:`tool_start`,toolName:e.toolName,toolArgs:e.toolArgs});return}Q({type:`tool_end`,toolName:e.toolName,success:e.success??!0})}function xe(e){if(Y===null){Q({type:`error`,message:`Subagent worker has not started`});return}Z=Z.then(async()=>{try{await Y?.run(e)}catch(e){Q({type:`error`,message:e instanceof Error?e.message:String(e)})}})}async function Se(e){X=!0,Y?.abort(),Q({type:`cancelled`,reason:e}),await Y?.shutdown({reason:`other`}).catch(()=>void 0),setTimeout(()=>process.exit(130),0)}function Ce(e){try{return e.createTools({cwd:process.cwd()}).map(e=>e.getName())}catch(e){process.stderr.write(`robota: could not enumerate the composed tool surface: ${e instanceof Error?e.message:String(e)}\n`);return}}function we(e){process.send===void 0&&(process.stderr.write(`robota: 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(!j(t)){Q({type:`error`,message:`Malformed subagent worker parent message`});return}switch(t.type){case`start`:Z=Z.then(()=>ye(t.payload,e));break;case`send`:xe(t.prompt);break;case`cancel`:Se(t.reason);break;default:Q({type:`error`,message:`Unhandled subagent worker parent message`})}}),process.on(`disconnect`,()=>{X=!0,Y?.abort(),Y?.shutdown({reason:`other`}).catch(()=>void 0)});let t=Ce(e);Q({type:`ready`,...t?{composedToolNames:t}:{}})}exports.ChildProcessSubagentRunner=J,exports.SUBAGENT_WORKER_MODE_FLAG=q,exports.createChildProcessSubagentRunnerFactory=fe,exports.decodeAgentDefinitionDto=h,exports.decodeParentContextDto=v,exports.encodeAgentDefinition=m,exports.encodeParentContext=_,exports.isSubagentWorkerChildMessage=M,exports.isSubagentWorkerModeArgv=ue,exports.isSubagentWorkerParentMessage=j,exports.restoreAgentDefinition=g,exports.restoreParentContext=y,exports.runSubagentWorkerMain=we;
|
|
@@ -0,0 +1,436 @@
|
|
|
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, 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 = "--__robota-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/child-process-subagent-runner.d.ts
|
|
46
|
+
interface IChildProcessSubagentRunnerOptions {
|
|
47
|
+
/**
|
|
48
|
+
* DIST-006: how to start a copy of the running artifact in subagent-worker mode, stated by the
|
|
49
|
+
* composition root. It replaced `workerPath`, which asked this package to locate a file whose
|
|
50
|
+
* location is a property of the packaging step — a question no library can answer, and one that
|
|
51
|
+
* was answered wrongly twice.
|
|
52
|
+
*/
|
|
53
|
+
workerEntry: ISubagentWorkerEntry;
|
|
54
|
+
providerConfig?: IProviderDefinitionConfig;
|
|
55
|
+
/**
|
|
56
|
+
* The parent's provider registry. Its defaults complete the connection the child is given, and
|
|
57
|
+
* each definition names the environment its client reads. Required: a job whose provider has no
|
|
58
|
+
* definition here is refused, because its connection cannot be checked.
|
|
59
|
+
*/
|
|
60
|
+
providerDefinitions: readonly IProviderDefinition[];
|
|
61
|
+
killGraceMs?: number;
|
|
62
|
+
/**
|
|
63
|
+
* How long a spawned worker may take to signal `ready` before the runner gives up. Injectable so
|
|
64
|
+
* the branch is reachable in a test; without that it is a fix that ships untested.
|
|
65
|
+
*/
|
|
66
|
+
handshakeBudgetMs?: number;
|
|
67
|
+
env?: NodeJS.ProcessEnv;
|
|
68
|
+
worktreeIsolation?: boolean;
|
|
69
|
+
worktreeAdapter: ISubagentWorktreeAdapter;
|
|
70
|
+
logsDir?: string;
|
|
71
|
+
}
|
|
72
|
+
declare function createChildProcessSubagentRunnerFactory(options: IChildProcessSubagentRunnerOptions): TSubagentRunnerFactory;
|
|
73
|
+
declare class ChildProcessSubagentRunner implements ISubagentRunner {
|
|
74
|
+
private readonly deps;
|
|
75
|
+
private readonly workerEntry;
|
|
76
|
+
private readonly killGraceMs;
|
|
77
|
+
private readonly handshakeBudgetMs?;
|
|
78
|
+
private readonly providerConfig?;
|
|
79
|
+
private readonly providerDefinitions;
|
|
80
|
+
private readonly env?;
|
|
81
|
+
private readonly logsDir?;
|
|
82
|
+
constructor(deps: IInProcessSubagentRunnerDeps, options: IChildProcessSubagentRunnerOptions);
|
|
83
|
+
start(job: ISubagentJobStart): ISubagentJobHandle;
|
|
84
|
+
/**
|
|
85
|
+
* The payload the child is started with. The builder lives in
|
|
86
|
+
* `child-process-subagent-projection.ts` (CLI-1994 moved it there so the ARCH-044 key-set test
|
|
87
|
+
* pins the code that produces it); review of ARCH-033/ARCH-034 is the reason it is a named
|
|
88
|
+
* producer at all — both fields were declared on the wire type, read by the worker, and set by
|
|
89
|
+
* nothing, because this was the only production site that constructs a payload and no test
|
|
90
|
+
* reached it.
|
|
91
|
+
*/
|
|
92
|
+
private createStartPayload;
|
|
93
|
+
private resolveTranscriptPath;
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/subagent-worker-start-dto.d.ts
|
|
97
|
+
/**
|
|
98
|
+
* The parent's loaded-context RUNTIME model (`ILoadedContext`, which the framework barrel does not
|
|
99
|
+
* export). Named here for the encoder/restore signatures only — the wire DTO below never references it.
|
|
100
|
+
*/
|
|
101
|
+
type TParentContextModel = IInProcessSubagentRunnerDeps['context'];
|
|
102
|
+
interface ISubagentWorkerAgentDefinitionDto {
|
|
103
|
+
readonly name: string;
|
|
104
|
+
readonly description: string;
|
|
105
|
+
readonly systemPrompt: string;
|
|
106
|
+
readonly model?: string;
|
|
107
|
+
readonly effort?: IAgentDefinition['effort'];
|
|
108
|
+
readonly role?: string;
|
|
109
|
+
readonly maxTurns?: number;
|
|
110
|
+
readonly tools?: readonly string[];
|
|
111
|
+
readonly disallowedTools?: readonly string[];
|
|
112
|
+
}
|
|
113
|
+
interface ISubagentWorkerContextFileEntryDto {
|
|
114
|
+
readonly filePath: string;
|
|
115
|
+
readonly content: string;
|
|
116
|
+
readonly contentHash: string;
|
|
117
|
+
}
|
|
118
|
+
interface ISubagentWorkerParentContextDto {
|
|
119
|
+
readonly agentsMd: string;
|
|
120
|
+
readonly projectNotesMd: string;
|
|
121
|
+
readonly memoryMd?: string;
|
|
122
|
+
readonly taskContext?: string;
|
|
123
|
+
readonly compactInstructions?: string;
|
|
124
|
+
readonly agentsFileEntries?: readonly ISubagentWorkerContextFileEntryDto[];
|
|
125
|
+
readonly projectNotesFileEntries?: readonly ISubagentWorkerContextFileEntryDto[];
|
|
126
|
+
}
|
|
127
|
+
type TDtoDecodeResult<TDto> = {
|
|
128
|
+
readonly ok: true;
|
|
129
|
+
readonly value: TDto;
|
|
130
|
+
} | {
|
|
131
|
+
readonly ok: false;
|
|
132
|
+
readonly reason: string;
|
|
133
|
+
};
|
|
134
|
+
declare function encodeAgentDefinition(definition: IAgentDefinition): ISubagentWorkerAgentDefinitionDto;
|
|
135
|
+
declare function decodeAgentDefinitionDto(value: unknown): TDtoDecodeResult<ISubagentWorkerAgentDefinitionDto>;
|
|
136
|
+
/** Explicit restore in the worker: the DTO's fields are the runtime model's, copied, not aliased. */
|
|
137
|
+
declare function restoreAgentDefinition(dto: ISubagentWorkerAgentDefinitionDto): IAgentDefinition;
|
|
138
|
+
/** Accepts the issue #2317 projection (or anything wider, structurally); only declared fields cross. */
|
|
139
|
+
declare function encodeParentContext(context: ISubagentParentContext): ISubagentWorkerParentContextDto;
|
|
140
|
+
declare function decodeParentContextDto(value: unknown): TDtoDecodeResult<ISubagentWorkerParentContextDto>;
|
|
141
|
+
declare function restoreParentContext(dto: ISubagentWorkerParentContextDto): TParentContextModel;
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/worker-composition.d.ts
|
|
144
|
+
/**
|
|
145
|
+
* The session record store a fork job's `resumeSessionId` names a record in, typed FROM the one
|
|
146
|
+
* function that reads it (`restoreSessionRecordIntoSession`, agent-framework) rather than from the
|
|
147
|
+
* interface package that declares the port — this package does not depend on that package, and a
|
|
148
|
+
* type derived from the consumer cannot drift from what the consumer accepts.
|
|
149
|
+
*/
|
|
150
|
+
type TResumeSessionStore = Parameters<typeof restoreSessionRecordIntoSession>[0];
|
|
151
|
+
/**
|
|
152
|
+
* ARCH-021: what the product composes, stated by the composition root.
|
|
153
|
+
*
|
|
154
|
+
* This is the sibling of {@link ISubagentWorkerEntry} one level up. That seam answers "how is this
|
|
155
|
+
* artifact started"; this one answers "what does this product compose" — and the same rule decides
|
|
156
|
+
* both: **the only party that knows is the product itself.**
|
|
157
|
+
*
|
|
158
|
+
* The seam this replaces had a neutral package importing `createDefaultTools()` and
|
|
159
|
+
* `createDefaultProviderDefinitions()` and building the child's surface from them, while the
|
|
160
|
+
* composition root had already handed the runner the fully composed surface. So a product's custom
|
|
161
|
+
* providers and pack-owned tools reached an in-process subagent and not a child-process one, and
|
|
162
|
+
* ARCH-006's invariant — every tool robota runs comes from a pack — was false in the child.
|
|
163
|
+
*
|
|
164
|
+
* **Why a recipe rather than the instances.** A composition cannot be projected across a process
|
|
165
|
+
* boundary, because it is code: `createProvider` is a function and a tool carries `execute`. The two
|
|
166
|
+
* structurally sound answers are to proxy the instances or to stop expressing the contract as
|
|
167
|
+
* instances. Proxying loses on containment — a proxied tool executes in the PARENT, bound to the
|
|
168
|
+
* parent's checkout, while a worktree-isolated child's execution root is a different directory. So
|
|
169
|
+
* the recipe crosses and the child builds an equivalent surface at its own root, which is what every
|
|
170
|
+
* comparable product does.
|
|
171
|
+
*/
|
|
172
|
+
interface ISubagentWorkerComposition {
|
|
173
|
+
/** Product-selected hook executors for the child session. */
|
|
174
|
+
createHookTypeExecutors?: () => IHookTypeExecutor[];
|
|
175
|
+
/**
|
|
176
|
+
* The product's tool surface for THIS subagent's execution root.
|
|
177
|
+
*
|
|
178
|
+
* `cwd` is a required argument for the same reason `ICreateDefaultToolsOptions.cwd` is (ARCH-010):
|
|
179
|
+
* a tool set built without its root carries a disarmed path guard, and the measured consequence
|
|
180
|
+
* was a subagent `Read` returning `/etc/hostname`. Passing the root through the call rather than
|
|
181
|
+
* capturing it in the factory is what stops a child from inheriting the parent's.
|
|
182
|
+
*/
|
|
183
|
+
createTools(context: {
|
|
184
|
+
readonly cwd: string;
|
|
185
|
+
/**
|
|
186
|
+
* ARCH-034: the tiers session assembly adds ON TOP of the product's tool set.
|
|
187
|
+
*
|
|
188
|
+
* The two runners of `ISubagentRunner` were handing a subagent different surfaces, and the
|
|
189
|
+
* difference was silent because both paths succeed. In-process passes the parent's fully
|
|
190
|
+
* ASSEMBLED tools; this path rebuilds the product's set at the child's root. For a product whose
|
|
191
|
+
* packs own the tool surface those agree — but what session assembly adds AFTER the packs did
|
|
192
|
+
* not cross: the goal tool (`includeGoalTool`) and edit-checkpoint wrapping.
|
|
193
|
+
*
|
|
194
|
+
* Choosing a runner is an isolation and packaging decision. It is not supposed to be a capability
|
|
195
|
+
* decision, so the composition root states which of those tiers the child should also receive and
|
|
196
|
+
* the recipe carries the answer rather than the parent's live wrappers.
|
|
197
|
+
*/
|
|
198
|
+
readonly sessionTiers?: {
|
|
199
|
+
/** Whether the parent's session included the goal-status tool. */
|
|
200
|
+
readonly includeGoalTool?: boolean;
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* ARCH-033: the sandbox the child restored, when the parent projected one.
|
|
204
|
+
*
|
|
205
|
+
* Threaded rather than captured, for the same reason `cwd` is: a tool surface built without the
|
|
206
|
+
* sandbox it is supposed to act in would run on the HOST while the parent runs sandboxed, which
|
|
207
|
+
* is the divergence the composition root's refusal exists to prevent. Absent ⇒ no sandbox, and
|
|
208
|
+
* the child's tools act on its own confined root.
|
|
209
|
+
*/
|
|
210
|
+
readonly sandboxClient?: TProjectedSandboxClient;
|
|
211
|
+
}): IToolWithEventService[];
|
|
212
|
+
/**
|
|
213
|
+
* The product's provider registry. Carried as definitions rather than a constructed provider
|
|
214
|
+
* because `createProvider` is code — the child builds its own provider from the serialized profile
|
|
215
|
+
* against THIS registry, so a custom provider type resolves instead of throwing `Unknown provider`.
|
|
216
|
+
*/
|
|
217
|
+
readonly providerDefinitions: readonly IProviderDefinition[];
|
|
218
|
+
/**
|
|
219
|
+
* How the child rebuilds a SANDBOX that the parent is running in (ARCH-033).
|
|
220
|
+
*
|
|
221
|
+
* The same shape as `providerDefinitions`, and for the same reason. A live `ISandboxClient` is an
|
|
222
|
+
* open session against a remote machine; it cannot cross a process boundary. What CAN cross is the
|
|
223
|
+
* pair (which client type, which snapshot) — `ISandboxClient.snapshot()` returns a
|
|
224
|
+
* provider-owned reference and `restore(id)` hydrates a fresh client from it, and a reference is
|
|
225
|
+
* just a string.
|
|
226
|
+
*
|
|
227
|
+
* So the composition root registers the constructor by type name, exactly as it registers provider
|
|
228
|
+
* definitions, and the recipe carries `{ type, snapshotId }`. The child looks the type up here and
|
|
229
|
+
* restores. Neither half works alone: a snapshot with no registry is a reference nothing can open,
|
|
230
|
+
* and a registry with no snapshot rebuilds an EMPTY sandbox, which is worse than refusing because
|
|
231
|
+
* the child would look sandboxed while sharing none of the parent's state.
|
|
232
|
+
*
|
|
233
|
+
* Absent ⇒ the product composes no sandbox, and `assertChildProcessSubagentsCanReproduce` in the
|
|
234
|
+
* composition root refuses to start a sandboxed parent that cannot project. That refusal remains
|
|
235
|
+
* the correct behaviour for a product that has not registered a factory; this seam is what lets one
|
|
236
|
+
* stop refusing.
|
|
237
|
+
*/
|
|
238
|
+
readonly sandboxFactories?: Readonly<Record<string, TSandboxClientFactory>>;
|
|
239
|
+
/**
|
|
240
|
+
* CLI-1994: how the child opens the session store a fork job's record was written to.
|
|
241
|
+
*
|
|
242
|
+
* The same shape as `providerDefinitions` and `sandboxFactories`, and for the same reason: where a
|
|
243
|
+
* product keeps its session records is the composition root's knowledge, and it cannot be
|
|
244
|
+
* projected onto the wire without also projecting the records — which is exactly what ARCH-044
|
|
245
|
+
* keeps off it. So the parent sends the id, and the child asks the composition to open the store
|
|
246
|
+
* FOR THE PARENT'S cwd (`request.cwd`, not the execution root — a worktree-isolated child runs in
|
|
247
|
+
* a directory that has no session records of its own).
|
|
248
|
+
*
|
|
249
|
+
* Absent ⇒ the product composes no store, and a job that names a record to resume fails, stated
|
|
250
|
+
* as such, rather than starting with an empty conversation that looks like a fork.
|
|
251
|
+
*/
|
|
252
|
+
readonly openSessionStore?: (context: {
|
|
253
|
+
readonly cwd: string;
|
|
254
|
+
}) => TResumeSessionStore;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Rebuilds a sandbox client of ONE type from a snapshot reference the parent produced.
|
|
258
|
+
*
|
|
259
|
+
* Deliberately not `() => ISandboxClient`: a factory that cannot receive the reference can only make
|
|
260
|
+
* an empty sandbox, which is the failure mode this seam exists to avoid.
|
|
261
|
+
*/
|
|
262
|
+
type TSandboxClientFactory = (snapshotId: string) => Promise<TProjectedSandboxClient>;
|
|
263
|
+
/**
|
|
264
|
+
* What the factory hands back, expressed STRUCTURALLY rather than as `ISandboxClient`.
|
|
265
|
+
*
|
|
266
|
+
* This package is the neutral runner: it depends on `agent-core`, `agent-executor`,
|
|
267
|
+
* `agent-framework`, `agent-interface-execution` and `agent-process` — deliberately not on
|
|
268
|
+
* `agent-tools`, where `ISandboxClient` lives. Importing that type to describe a value this package
|
|
269
|
+
* only ever passes through would add a dependency edge for a pass-through, which is the shape
|
|
270
|
+
* ARCH-021 removed from here on the provider axis.
|
|
271
|
+
*
|
|
272
|
+
* So the seam names the minimum it needs to be honest about — the object is opaque to the runner and
|
|
273
|
+
* meaningful only to the composition root that registered the factory and the tools that receive it.
|
|
274
|
+
*/
|
|
275
|
+
type TProjectedSandboxClient = object;
|
|
276
|
+
/**
|
|
277
|
+
* The serializable half — what the parent puts in the recipe.
|
|
278
|
+
*
|
|
279
|
+
* Both fields are required. `type` selects the factory; `snapshotId` is what the parent's
|
|
280
|
+
* `snapshot()` returned. Carrying one without the other is the empty-sandbox failure above.
|
|
281
|
+
*/
|
|
282
|
+
interface ISandboxProjection {
|
|
283
|
+
readonly type: string;
|
|
284
|
+
readonly snapshotId: string;
|
|
285
|
+
}
|
|
286
|
+
//#endregion
|
|
287
|
+
//#region src/child-process-subagent-ipc.d.ts
|
|
288
|
+
type TSubagentWorkerWireValue = string | number | boolean | null | undefined | object;
|
|
289
|
+
/** ARCH-044: the four config members the child reads. See `projectParentConfig`. */
|
|
290
|
+
interface ISubagentWorkerParentConfig {
|
|
291
|
+
readonly provider: {
|
|
292
|
+
readonly model: string;
|
|
293
|
+
};
|
|
294
|
+
readonly permissions: IResolvedConfig['permissions'];
|
|
295
|
+
readonly defaultTrustLevel: IResolvedConfig['defaultTrustLevel'];
|
|
296
|
+
readonly hooks?: IResolvedConfig['hooks'];
|
|
297
|
+
}
|
|
298
|
+
interface ISubagentWorkerStartPayload {
|
|
299
|
+
taskId: string;
|
|
300
|
+
request: ISubagentSpawnRequest;
|
|
301
|
+
/**
|
|
302
|
+
* ARCH-031: the worktree the parent's runner prepared, carried across the fork so the child can
|
|
303
|
+
* answer `subagentExecutionRoot` the same way the parent would. Runner-produced, so it rides beside
|
|
304
|
+
* the request rather than on it.
|
|
305
|
+
*
|
|
306
|
+
* `branch` crosses the fork too, even though nothing reads it here yet: dropping it at the IPC
|
|
307
|
+
* boundary would make the child's view of its own isolated run poorer than the parent's, for no
|
|
308
|
+
* reason other than the absence of a present-day consumer.
|
|
309
|
+
*/
|
|
310
|
+
worktree?: {
|
|
311
|
+
readonly path: string;
|
|
312
|
+
readonly branch?: string;
|
|
313
|
+
};
|
|
314
|
+
/** ARCH-044 (issue #2047): a JSON-safe DTO owned here, projected from `IAgentDefinition` by the parent. */
|
|
315
|
+
agentDefinition: ISubagentWorkerAgentDefinitionDto;
|
|
316
|
+
/**
|
|
317
|
+
* ARCH-044 (issue #2047): the config members the child reads, declared here rather than indexed
|
|
318
|
+
* out of the runtime type.
|
|
319
|
+
*
|
|
320
|
+
* It was `IInProcessSubagentRunnerDeps['config']`, so the wire shape was the in-process shape and
|
|
321
|
+
* grew with it — which put the parent's resolved `provider.apiKey` and its `env` map into a second
|
|
322
|
+
* process where nothing read either. Declaring the members means a new field on `IResolvedConfig`
|
|
323
|
+
* does not reach the child by default; `projectParentConfig` is what enforces it at runtime,
|
|
324
|
+
* because structural typing would accept the whole config here.
|
|
325
|
+
*/
|
|
326
|
+
parentConfig: ISubagentWorkerParentConfig;
|
|
327
|
+
/**
|
|
328
|
+
* ARCH-044 (issue #2047): a JSON-safe DTO owned here, decoded totally on the child side. The parent
|
|
329
|
+
* fills it from `projectParentContext` (issue #2317): the two context members the child reads —
|
|
330
|
+
* `agentsMd` and `projectNotesMd` — and never the parent's whole `ILoadedContext`, whose file
|
|
331
|
+
* entries carry the full text of every AGENTS.md and CLAUDE.md the parent loaded.
|
|
332
|
+
*/
|
|
333
|
+
parentContext: ISubagentWorkerParentContextDto;
|
|
334
|
+
providerProfile: ISerializableProviderProfile;
|
|
335
|
+
/**
|
|
336
|
+
* The destination-deciding environment the parent checked before spawning, sealed so the child can
|
|
337
|
+
* repeat the check before it builds a provider. Values never travel; only a keyed digest does.
|
|
338
|
+
*/
|
|
339
|
+
connectionCheck: IConnectionEnvironmentCheck;
|
|
340
|
+
/**
|
|
341
|
+
* ARCH-033: how the child rebuilds the parent's sandbox, as `(type, snapshotId)`.
|
|
342
|
+
*
|
|
343
|
+
* The live client cannot cross a process boundary — it is an open session against a remote machine.
|
|
344
|
+
* This pair can: the type selects a factory the composition root registered, and the snapshot is a
|
|
345
|
+
* provider-owned reference the parent's `snapshot()` returned. Both halves are required, because a
|
|
346
|
+
* snapshot with no registry is a reference nothing opens and a registry with no snapshot rebuilds
|
|
347
|
+
* an EMPTY sandbox — a child that looks sandboxed while sharing none of the parent's state.
|
|
348
|
+
*
|
|
349
|
+
* Absent ⇒ the parent holds no sandbox, which is every product that has not registered one.
|
|
350
|
+
*/
|
|
351
|
+
sandboxProjection?: ISandboxProjection;
|
|
352
|
+
/**
|
|
353
|
+
* ARCH-034: which session-assembly tiers the parent's surface carried.
|
|
354
|
+
*
|
|
355
|
+
* A property of the parent's SESSION rather than of the child's root, so it rides on the payload
|
|
356
|
+
* beside the request instead of being derived at the child. Absent ⇒ the parent had none.
|
|
357
|
+
*/
|
|
358
|
+
sessionTiers?: {
|
|
359
|
+
readonly includeGoalTool?: boolean;
|
|
360
|
+
};
|
|
361
|
+
permissionMode?: TPermissionMode;
|
|
362
|
+
logsDir?: string;
|
|
363
|
+
}
|
|
364
|
+
interface ISubagentWorkerStartMessage {
|
|
365
|
+
type: 'start';
|
|
366
|
+
payload: ISubagentWorkerStartPayload;
|
|
367
|
+
}
|
|
368
|
+
interface ISubagentWorkerSendMessage {
|
|
369
|
+
type: 'send';
|
|
370
|
+
prompt: string;
|
|
371
|
+
}
|
|
372
|
+
interface ISubagentWorkerCancelMessage {
|
|
373
|
+
type: 'cancel';
|
|
374
|
+
reason?: string;
|
|
375
|
+
}
|
|
376
|
+
type TSubagentWorkerParentMessage = ISubagentWorkerStartMessage | ISubagentWorkerSendMessage | ISubagentWorkerCancelMessage;
|
|
377
|
+
interface ISubagentWorkerReadyMessage {
|
|
378
|
+
type: 'ready';
|
|
379
|
+
/**
|
|
380
|
+
* ARCH-021: the tool names the child actually composed, so "the child has the product's surface"
|
|
381
|
+
* is VERIFIED per run rather than assumed by construction. Names only — the tools themselves are
|
|
382
|
+
* code and do not cross this boundary; that is the whole point of the composition port.
|
|
383
|
+
*
|
|
384
|
+
* Enumerated at the worker's own cwd before any job arrives, which is sound because a pack's tool
|
|
385
|
+
* NAMES do not depend on the root (the root binds the path guard, not the name set).
|
|
386
|
+
*/
|
|
387
|
+
composedToolNames?: readonly string[];
|
|
388
|
+
}
|
|
389
|
+
interface ISubagentWorkerTextDeltaMessage {
|
|
390
|
+
type: 'text_delta';
|
|
391
|
+
delta: string;
|
|
392
|
+
}
|
|
393
|
+
interface ISubagentWorkerToolStartMessage {
|
|
394
|
+
type: 'tool_start';
|
|
395
|
+
toolName: string;
|
|
396
|
+
toolArgs?: TToolArgs;
|
|
397
|
+
}
|
|
398
|
+
interface ISubagentWorkerToolEndMessage {
|
|
399
|
+
type: 'tool_end';
|
|
400
|
+
toolName: string;
|
|
401
|
+
success: boolean;
|
|
402
|
+
}
|
|
403
|
+
interface ISubagentWorkerResultMessage {
|
|
404
|
+
type: 'result';
|
|
405
|
+
output: string;
|
|
406
|
+
/** ANALYTICS-001 (Phase 2): total token usage of the subagent run, forwarded to the parent. */
|
|
407
|
+
usage?: ISessionUsageTotals;
|
|
408
|
+
}
|
|
409
|
+
interface ISubagentWorkerErrorMessage {
|
|
410
|
+
type: 'error';
|
|
411
|
+
message: string;
|
|
412
|
+
}
|
|
413
|
+
interface ISubagentWorkerCancelledMessage {
|
|
414
|
+
type: 'cancelled';
|
|
415
|
+
reason?: string;
|
|
416
|
+
}
|
|
417
|
+
type TSubagentWorkerChildMessage = ISubagentWorkerReadyMessage | ISubagentWorkerTextDeltaMessage | ISubagentWorkerToolStartMessage | ISubagentWorkerToolEndMessage | ISubagentWorkerResultMessage | ISubagentWorkerErrorMessage | ISubagentWorkerCancelledMessage;
|
|
418
|
+
declare function isSubagentWorkerParentMessage(value: TSubagentWorkerWireValue): value is TSubagentWorkerParentMessage;
|
|
419
|
+
declare function isSubagentWorkerChildMessage(value: TSubagentWorkerWireValue): value is TSubagentWorkerChildMessage;
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/child-process-subagent-worker.d.ts
|
|
422
|
+
/**
|
|
423
|
+
* DIST-006: worker mode is ENTERED, not implied by loading this module.
|
|
424
|
+
*
|
|
425
|
+
* These handlers used to run as module top-level side effects, which is what forced the worker to
|
|
426
|
+
* be a separate file that something had to locate on disk. As a function, the composition root's
|
|
427
|
+
* own entry can become the worker — so there is no second artifact and no path to get wrong.
|
|
428
|
+
*
|
|
429
|
+
* ARCH-021: `composition` is REQUIRED, deliberately. An optional parameter falling back to imported
|
|
430
|
+
* defaults would reinstate the exact defect this seam removes — and at this line conventions have a
|
|
431
|
+
* measured failure rate of 100% (ARCH-010 and ARCH-006 are both findings here).
|
|
432
|
+
*/
|
|
433
|
+
declare function runSubagentWorkerMain(composition: ISubagentWorkerComposition): void;
|
|
434
|
+
//#endregion
|
|
435
|
+
export { ChildProcessSubagentRunner, type IChildProcessSubagentRunnerOptions, type ISubagentWorkerAgentDefinitionDto, type ISubagentWorkerComposition, type ISubagentWorkerContextFileEntryDto, type ISubagentWorkerEntry, type ISubagentWorkerParentContextDto, type ISubagentWorkerStartPayload, SUBAGENT_WORKER_MODE_FLAG, type TResumeSessionStore, type TSubagentWorkerChildMessage, type TSubagentWorkerParentMessage, type TSubagentWorkerWireValue, createChildProcessSubagentRunnerFactory, decodeAgentDefinitionDto, decodeParentContextDto, encodeAgentDefinition, encodeParentContext, isSubagentWorkerChildMessage, isSubagentWorkerModeArgv, isSubagentWorkerParentMessage, restoreAgentDefinition, restoreParentContext, runSubagentWorkerMain };
|
|
436
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/worker-entry.ts","../../src/child-process-subagent-runner.ts","../../src/subagent-worker-start-dto.ts","../../src/worker-composition.ts","../../src/child-process-subagent-ipc.ts","../../src/child-process-subagent-worker.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;cAmBa;;;;;;;;;;UAWI;;WAEN;;WAEA;;WAEA;;;iBAIK,yBAAyB;;;UCMxB;;;;;;;EAOf,aAAa;EACb,iBAAiB;;;;;;EAMjB,8BAA8B;EAC9B;;;;;EAKA;EACA,MAAM,OAAO;EACb;EACA,iBAAiB;EACjB;;iBAGc,wCACd,SAAS,qCACR;cAaU,sCAAsC;mBAU9B;mBATF;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;EAGE,YAAA,MAAM,8BACvB,SAAS;EAWX,MAAM,KAAK,oBAAoB;;;;;;;;;UAsFvB;UAWA;;;;;;;;KClLL,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;;;;;;;;;KCrMhE,sBAAsB,kBAAkB;;;;;;;;;;;;;;;;;;;;;;UAuBnC;;EAEf,gCAAgC;;;;;;;;;EAShC,YAAY;aACD;;;;;;;;;;;;;;aAcA;;eAEE;;;;;;;;;;aAUF,gBAAgB;MACvB;;;;;;WAOK,8BAA8B;;;;;;;;;;;;;;;;;;;;;WAsB9B,mBAAmB,SAAS,eAAe;;;;;;;;;;;;;;WAe3C,oBAAoB;aAAoB;QAAkB;;;;;;;;KASzD,yBAAyB,uBAAuB,QAAQ;;;;;;;;;;;;;KAcxD;;;;;;;UAQK;WACN;WACA;;;;KCxIC;;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;;EAC1B,iBAAiB;EACjB;;UAGe;EACf;EACA,SAAS;;UAGM;EACf;EACA;;UAGe;EACf;EACA;;KAGU,+BACV,8BAA8B,6BAA6B;UAE5C;EACf;;;;;;;;;EASA;;UAGe;EACf;EACA;;UAGe;EACf;EACA;EACA,WAAW;;UAGI;EACf;EACA;EACA;;UAGe;EACf;EACA;;EAEA,QAAQ;;UAGO;EACf;EACA;;UAGe;EACf;EACA;;KAGU,8BACR,8BACA,kCACA,kCACA,gCACA,+BACA,8BACA;iBA+GY,8BACd,OAAO,2BACN,SAAS;iBAcI,6BACd,OAAO,2BACN,SAAS;;;;;;;;;;;;;;iBCpCI,sBAAsB,aAAa"}
|