subharness 0.0.3 → 0.0.4
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 +3 -3
- package/dist/adapters/approval-context.d.ts +2 -0
- package/dist/adapters/approval-context.js +9 -0
- package/dist/adapters/approval-context.js.map +1 -0
- package/dist/adapters/approval-lifetime.d.ts +8 -0
- package/dist/adapters/approval-lifetime.js +50 -0
- package/dist/adapters/approval-lifetime.js.map +1 -0
- package/dist/adapters/claude-approvals.d.ts +4 -0
- package/dist/adapters/claude-approvals.js +32 -0
- package/dist/adapters/claude-approvals.js.map +1 -0
- package/dist/adapters/claude-permissions.js +1 -1
- package/dist/adapters/claude-permissions.js.map +1 -1
- package/dist/adapters/claude.js +30 -1
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/codex-approvals.d.ts +6 -0
- package/dist/adapters/codex-approvals.js +111 -0
- package/dist/adapters/codex-approvals.js.map +1 -0
- package/dist/adapters/codex.js +32 -2
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/fx-approvals.d.ts +2 -0
- package/dist/adapters/fx-approvals.js +14 -0
- package/dist/adapters/fx-approvals.js.map +1 -0
- package/dist/adapters/fx.js +49 -18
- package/dist/adapters/fx.js.map +1 -1
- package/dist/adapters/types.d.ts +2 -0
- package/dist/adapters/types.js.map +1 -1
- package/dist/approvals/schema.d.ts +4 -0
- package/dist/approvals/schema.js +70 -0
- package/dist/approvals/schema.js.map +1 -0
- package/dist/approvals/types.d.ts +47 -0
- package/dist/approvals/types.js +2 -0
- package/dist/approvals/types.js.map +1 -0
- package/dist/cli/args.d.ts +2 -1
- package/dist/cli/args.js +20 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/dashboard-client.d.ts +5 -1
- package/dist/cli/dashboard-client.js +161 -14
- package/dist/cli/dashboard-client.js.map +1 -1
- package/dist/cli/dashboard-detail-view.d.ts +16 -0
- package/dist/cli/dashboard-detail-view.js +163 -0
- package/dist/cli/dashboard-detail-view.js.map +1 -0
- package/dist/cli/dashboard-history-view.d.ts +29 -0
- package/dist/cli/dashboard-history-view.js +211 -0
- package/dist/cli/dashboard-history-view.js.map +1 -0
- package/dist/cli/dashboard-input.d.ts +14 -0
- package/dist/cli/dashboard-input.js +118 -0
- package/dist/cli/dashboard-input.js.map +1 -0
- package/dist/cli/dashboard-layout.d.ts +12 -1
- package/dist/cli/dashboard-layout.js +29 -27
- package/dist/cli/dashboard-layout.js.map +1 -1
- package/dist/cli/dashboard-renderer.d.ts +30 -2
- package/dist/cli/dashboard-renderer.js +125 -7
- package/dist/cli/dashboard-renderer.js.map +1 -1
- package/dist/cli/dashboard-style.d.ts +9 -0
- package/dist/cli/dashboard-style.js +36 -0
- package/dist/cli/dashboard-style.js.map +1 -0
- package/dist/cli/dashboard.d.ts +3 -1
- package/dist/cli/dashboard.js +127 -12
- package/dist/cli/dashboard.js.map +1 -1
- package/dist/cli/help.js +7 -0
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/input.d.ts +1 -0
- package/dist/cli/input.js +23 -17
- package/dist/cli/input.js.map +1 -1
- package/dist/cli/main.js +1 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/output.js +17 -1
- package/dist/cli/output.js.map +1 -1
- package/dist/runtime/approval-registry.d.ts +27 -0
- package/dist/runtime/approval-registry.js +126 -0
- package/dist/runtime/approval-registry.js.map +1 -0
- package/dist/runtime/coordinator.d.ts +17 -1
- package/dist/runtime/coordinator.js +124 -6
- package/dist/runtime/coordinator.js.map +1 -1
- package/dist/runtime/dashboard.d.ts +42 -0
- package/dist/runtime/dashboard.js.map +1 -1
- package/dist/runtime/select-native.d.ts +2 -1
- package/dist/runtime/select-native.js +2 -2
- package/dist/runtime/select-native.js.map +1 -1
- package/dist/runtime/service.js +24 -1
- package/dist/runtime/service.js.map +1 -1
- package/dist/runtime/types.d.ts +9 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/runtime/worker-approvals.d.ts +21 -0
- package/dist/runtime/worker-approvals.js +51 -0
- package/dist/runtime/worker-approvals.js.map +1 -0
- package/dist/runtime/worker-client.js +35 -7
- package/dist/runtime/worker-client.js.map +1 -1
- package/dist/runtime/worker-server.d.ts +2 -0
- package/dist/runtime/worker-server.js +39 -10
- package/dist/runtime/worker-server.js.map +1 -1
- package/package.json +1 -1
- package/sdk/access-config.md +28 -0
- package/sdk/adapter-contract.md +2 -10
- package/sdk/agent-skill.md +6 -3
- package/sdk/approvals.md +118 -0
- package/sdk/cli/dashboard-design.md +38 -4
- package/sdk/cli/dashboard-inspection.md +25 -0
- package/sdk/cli/index.md +11 -5
- package/sdk/cli/output.md +7 -3
- package/sdk/completion-notifications.md +2 -0
- package/sdk/distribution.md +52 -0
- package/sdk/evals.md +9 -3
- package/sdk/fx.md +1 -1
- package/sdk/index.md +1 -0
- package/sdk/message-delivery.md +1 -1
- package/sdk/permissions.md +4 -4
- package/sdk/pr-integration.md +1 -1
- package/sdk/sessions.md +4 -1
- package/sdk/v1-runtime.md +3 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ErrorInfo } from '../errors.js';
|
|
1
2
|
import type { HarnessKind } from '../sdk/types.js';
|
|
2
3
|
import type { TaskState } from './types.js';
|
|
3
4
|
export type DashboardHarness = 'codex' | 'claude' | 'fx' | 'pending';
|
|
@@ -24,5 +25,46 @@ export interface DashboardSnapshot {
|
|
|
24
25
|
grouping: true;
|
|
25
26
|
sessions: DashboardRow[];
|
|
26
27
|
}
|
|
28
|
+
export interface DashboardRelatedTask {
|
|
29
|
+
taskId: string;
|
|
30
|
+
sessionId: string;
|
|
31
|
+
harness: DashboardHarness;
|
|
32
|
+
title: string;
|
|
33
|
+
state: TaskState;
|
|
34
|
+
}
|
|
35
|
+
export interface DashboardRequest extends DashboardRow {
|
|
36
|
+
ordinal: number;
|
|
37
|
+
stopUnconfirmed: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface DashboardSession {
|
|
40
|
+
version: 1;
|
|
41
|
+
type: 'dashboard-session';
|
|
42
|
+
taskId: string;
|
|
43
|
+
sessionId: string;
|
|
44
|
+
title: string;
|
|
45
|
+
harness: DashboardHarness;
|
|
46
|
+
state: TaskState;
|
|
47
|
+
workspace: DashboardWorkspace;
|
|
48
|
+
requests: DashboardRequest[];
|
|
49
|
+
subagents: DashboardRelatedTask[];
|
|
50
|
+
queuePaused: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface DashboardDetail {
|
|
53
|
+
version: 1;
|
|
54
|
+
type: 'dashboard-detail';
|
|
55
|
+
task: DashboardRow;
|
|
56
|
+
prompt?: string;
|
|
57
|
+
response?: {
|
|
58
|
+
responseId: string;
|
|
59
|
+
text?: string;
|
|
60
|
+
};
|
|
61
|
+
error?: ErrorInfo;
|
|
62
|
+
children: DashboardRelatedTask[];
|
|
63
|
+
queue: {
|
|
64
|
+
paused: boolean;
|
|
65
|
+
tasks: DashboardRelatedTask[];
|
|
66
|
+
};
|
|
67
|
+
stopUnconfirmed: boolean;
|
|
68
|
+
}
|
|
27
69
|
export declare function dashboardHarness(kind?: HarnessKind): DashboardHarness;
|
|
28
70
|
export declare function dashboardTitle(prompt: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/runtime/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/runtime/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAwErD,MAAM,UAAU,gBAAgB,CAAC,IAAkB;IACjD,IAAI,IAAI,KAAK,YAAY;QAAE,OAAO,QAAQ,CAAC;IAC3C,OAAO,IAAI,IAAI,SAAS,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,MAAM,KAAK,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAC/C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5C,MAAM,UAAU,GAAG,IAAI;aACpB,OAAO,CAAC,yDAAyD,EAAE,EAAE,CAAC;aACtE,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;aACrB,IAAI,EAAE,CAAC;QACV,IAAI,UAAU;YAAE,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC","sourcesContent":["import { stripVTControlCharacters } from 'node:util';\nimport type { ErrorInfo } from '../errors.js';\nimport type { HarnessKind } from '../sdk/types.js';\nimport type { TaskState } from './types.js';\n\nexport type DashboardHarness = 'codex' | 'claude' | 'fx' | 'pending';\n\nexport interface DashboardWorkspace {\n directory: string;\n repository?: string;\n}\n\nexport interface DashboardRow {\n sessionId: string;\n taskId: string;\n harness: DashboardHarness;\n title: string;\n state: TaskState;\n workspace: DashboardWorkspace;\n updatedAt: number;\n dispatchedAt?: number;\n failedAt?: number;\n finishedAt?: number;\n}\n\nexport interface DashboardSnapshot {\n version: 1;\n type: 'dashboard';\n history: true;\n grouping: true;\n sessions: DashboardRow[];\n}\n\nexport interface DashboardRelatedTask {\n taskId: string;\n sessionId: string;\n harness: DashboardHarness;\n title: string;\n state: TaskState;\n}\n\nexport interface DashboardRequest extends DashboardRow {\n ordinal: number;\n stopUnconfirmed: boolean;\n}\n\nexport interface DashboardSession {\n version: 1;\n type: 'dashboard-session';\n taskId: string;\n sessionId: string;\n title: string;\n harness: DashboardHarness;\n state: TaskState;\n workspace: DashboardWorkspace;\n requests: DashboardRequest[];\n subagents: DashboardRelatedTask[];\n queuePaused: boolean;\n}\n\nexport interface DashboardDetail {\n version: 1;\n type: 'dashboard-detail';\n task: DashboardRow;\n prompt?: string;\n response?: {responseId: string; text?: string};\n error?: ErrorInfo;\n children: DashboardRelatedTask[];\n queue: {paused: boolean; tasks: DashboardRelatedTask[]};\n stopUnconfirmed: boolean;\n}\n\nexport function dashboardHarness(kind?: HarnessKind): DashboardHarness {\n if (kind === 'claudeCode') return 'claude';\n return kind ?? 'pending';\n}\n\nexport function dashboardTitle(prompt: string): string {\n const plain = stripVTControlCharacters(prompt);\n for (const line of plain.split(/\\r\\n?|\\n/u)) {\n const normalized = line\n .replace(/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f-\\u009f]/gu, '')\n .replace(/\\s+/gu, ' ')\n .trim();\n if (normalized) return [...normalized].slice(0, 120).join('');\n }\n return '(untitled)';\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { HarnessKind } from '../sdk/types.js';
|
|
2
2
|
import type { AdapterOptions, ExecutionDefinition, NativeSession } from '../adapters/types.js';
|
|
3
3
|
import { type AccessConfig } from '../config/access.js';
|
|
4
|
-
|
|
4
|
+
import type { ApprovalHandler } from '../approvals/types.js';
|
|
5
|
+
export declare function selectNative(agent: ExecutionDefinition, cwd: string, env: NodeJS.ProcessEnv, settings: AccessConfig, open?: (options: AdapterOptions) => Promise<NativeSession>, selected?: (kind: HarnessKind) => void, onApproval?: ApprovalHandler): Promise<NativeSession>;
|
|
@@ -4,7 +4,7 @@ import { openFx } from '../adapters/fx.js';
|
|
|
4
4
|
import { resolveAccess } from '../config/access.js';
|
|
5
5
|
import { AgentError } from '../errors.js';
|
|
6
6
|
const openers = { codex: openCodex, claudeCode: openClaude, fx: openFx };
|
|
7
|
-
export async function selectNative(agent, cwd, env, settings, open = (options) => openers[options.harness.kind](options), selected = () => { }) {
|
|
7
|
+
export async function selectNative(agent, cwd, env, settings, open = (options) => openers[options.harness.kind](options), selected = () => { }, onApproval) {
|
|
8
8
|
const alternatives = Array.isArray(agent.harness) ? agent.harness : [agent.harness];
|
|
9
9
|
const unavailable = [];
|
|
10
10
|
for (const harness of alternatives) {
|
|
@@ -20,7 +20,7 @@ export async function selectNative(agent, cwd, env, settings, open = (options) =
|
|
|
20
20
|
for (const connection of connections) {
|
|
21
21
|
try {
|
|
22
22
|
const access = await resolveAccess(connection, harness.kind, cwd, env);
|
|
23
|
-
const native = await open({ agent, harness, cwd, env, access });
|
|
23
|
+
const native = await open({ agent, harness, cwd, env, access, ...(onApproval ? { onApproval } : {}) });
|
|
24
24
|
selected(harness.kind);
|
|
25
25
|
return native;
|
|
26
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select-native.js","sourceRoot":"","sources":["../../src/runtime/select-native.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAqB,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"select-native.js","sourceRoot":"","sources":["../../src/runtime/select-native.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HarnessKind } from '../sdk/types.js';\nimport type { AdapterOptions, ExecutionDefinition, NativeSession } from '../adapters/types.js';\nimport { openCodex } from '../adapters/codex.js';\nimport { openClaude } from '../adapters/claude.js';\nimport { openFx } from '../adapters/fx.js';\nimport { resolveAccess, type AccessConfig } from '../config/access.js';\nimport { AgentError } from '../errors.js';\nimport type { ApprovalHandler } from '../approvals/types.js';\n\nconst openers = {codex:openCodex,claudeCode:openClaude,fx:openFx};\n\nexport async function selectNative(\n agent: ExecutionDefinition, cwd: string, env: NodeJS.ProcessEnv, settings: AccessConfig,\n open = (options: AdapterOptions): Promise<NativeSession> => openers[options.harness.kind](options),\n selected: (kind: HarnessKind) => void = () => {},\n onApproval?: ApprovalHandler,\n): Promise<NativeSession> {\n const alternatives = Array.isArray(agent.harness) ? agent.harness : [agent.harness];\n const unavailable: string[] = [];\n for (const harness of alternatives) {\n const configuredConnections = settings.access?.[harness.kind as HarnessKind];\n const connections = configuredConnections ??\n (harness.kind === 'fx' ? [] : [{type:'subscription' as const}]);\n if (configuredConnections?.length === 0) {\n unavailable.push(`${harness.kind}: Access is disabled in the main checkout's .subharness/agents.local.json.`);\n } else if (harness.kind === 'fx' && !connections.length) {\n unavailable.push('fx: Enable an explicit AI Gateway connection in personal access settings.');\n }\n for (const connection of connections) {\n try {\n const access = await resolveAccess(connection, harness.kind, cwd, env);\n const native = await open({agent,harness,cwd,env,access,...(onApproval ? {onApproval} : {})});\n selected(harness.kind);\n return native;\n } catch (error) {\n if (!(error instanceof AgentError) || !['HARNESS_UNAVAILABLE','ACCESS_UNAVAILABLE'].includes(error.code)) throw error;\n unavailable.push(`${harness.kind}: ${error.message}`);\n }\n }\n }\n throw new AgentError('ACCESS_UNAVAILABLE', unavailable.length ? unavailable.join('\\n') : 'No declared harness has enabled access.');\n}\n"]}
|
package/dist/runtime/service.js
CHANGED
|
@@ -30,12 +30,32 @@ export async function startService(engine, token, instanceId) {
|
|
|
30
30
|
if (Buffer.byteLength(body) > 4 * 1024 * 1024)
|
|
31
31
|
throw new AgentError('INVALID_ARGUMENT', 'Request exceeds 4 MiB.');
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
let data;
|
|
34
|
+
try {
|
|
35
|
+
data = JSON.parse(body);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
throw new AgentError('INVALID_ARGUMENT', 'Coordinator input must be valid JSON.');
|
|
39
|
+
}
|
|
40
|
+
if (!data || typeof data !== 'object' || Array.isArray(data))
|
|
41
|
+
throw new AgentError('INVALID_ARGUMENT', 'Coordinator input must be a JSON object.');
|
|
34
42
|
let receipt;
|
|
35
43
|
switch (data.command) {
|
|
36
44
|
case 'dashboard':
|
|
37
45
|
write(await engine.dashboard());
|
|
38
46
|
break;
|
|
47
|
+
case 'dashboard-session':
|
|
48
|
+
if (typeof data.id !== 'string')
|
|
49
|
+
throw new AgentError('INVALID_ARGUMENT', 'Invalid dashboard session request fields.');
|
|
50
|
+
write(await engine.dashboardSession(data.id));
|
|
51
|
+
break;
|
|
52
|
+
case 'dashboard-detail':
|
|
53
|
+
if (typeof data.id !== 'string' ||
|
|
54
|
+
(data.includePrompt !== undefined && typeof data.includePrompt !== 'boolean') ||
|
|
55
|
+
(data.knownResponseId !== undefined && typeof data.knownResponseId !== 'string'))
|
|
56
|
+
throw new AgentError('INVALID_ARGUMENT', 'Invalid dashboard detail request fields.');
|
|
57
|
+
write(await engine.dashboardDetail(data.id, data.includePrompt ?? true, data.knownResponseId));
|
|
58
|
+
break;
|
|
39
59
|
case 'parent':
|
|
40
60
|
write(engine.parentContext(data.parentToken));
|
|
41
61
|
break;
|
|
@@ -70,6 +90,9 @@ export async function startService(engine, token, instanceId) {
|
|
|
70
90
|
case 'cancel':
|
|
71
91
|
write(await engine.cancel(data.id));
|
|
72
92
|
break;
|
|
93
|
+
case 'respond':
|
|
94
|
+
write(engine.respond(data.id, data.content, data.parentToken));
|
|
95
|
+
break;
|
|
73
96
|
default: throw new AgentError('INVALID_ARGUMENT', 'Unknown coordinator operation.');
|
|
74
97
|
}
|
|
75
98
|
if (receipt) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/runtime/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport { AgentError, errorInfo } from '../errors.js';\nimport { Coordinator } from './coordinator.js';\nimport type { Delivery, Receipt, SessionInput } from './types.js';\n\ninterface Request {\n command: string; input?: SessionInput; id?: string; after?: string;\n prompt?: string; delivery?: Delivery; full?: boolean; detach?: boolean; parentToken?: string;\n}\n\nexport async function startService(engine: Coordinator, token: string, instanceId: string) {\n const server = createServer(async (request, response) => {\n if (request.headers.authorization !== `Bearer ${token}`) { response.writeHead(401).end(); return; }\n if (request.method === 'GET' && request.url === '/health') {\n response.setHeader('content-type','application/json');\n response.end(JSON.stringify({version:1,type:'coordinator-health',instanceId}));\n return;\n }\n if (request.method !== 'POST' || request.url !== '/') { response.writeHead(404).end(); return; }\n response.setHeader('content-type', 'application/x-ndjson');\n const observer = new AbortController();\n // Keep long, quiet observations alive without exposing progress records to callers.\n const heartbeat = setInterval(() => { if (!response.destroyed) response.write('\\n'); }, 15_000);\n response.once('close', () => { clearInterval(heartbeat); observer.abort(); });\n const write = (record: unknown) => { if (!response.destroyed) response.write(JSON.stringify(record) + '\\n'); };\n try {\n let body = '';\n for await (const chunk of request) {\n body += chunk;\n if (Buffer.byteLength(body) > 4 * 1024 * 1024) throw new AgentError('INVALID_ARGUMENT','Request exceeds 4 MiB.');\n }\n const data = JSON.parse(body) as Request;\n let receipt: Receipt | undefined;\n switch (data.command) {\n case 'dashboard': write(await engine.dashboard()); break;\n case 'parent': write(engine.parentContext(data.parentToken!)); break;\n case 'run': receipt = engine.run(data.input!); break;\n case 'send':\n if (data.detach && data.delivery === 'steer') throw new AgentError('INVALID_ARGUMENT','--detach cannot be combined with --delivery steer.');\n receipt = await engine.send(data.id!, data.prompt!, data.delivery ?? 'queue', data.parentToken); break;\n case 'resume': {\n const active = engine.queue(data.id!).active;\n if (!active || active.state !== 'failed') throw new AgentError('NOT_FAILED','This session has no failed task to resume.');\n const after = engine.status(active.taskId).response?.responseId;\n receipt = await engine.resume(data.id!);\n write(receipt); write(await engine.observe(receipt.taskId, after, data.parentToken, observer.signal));\n receipt = undefined; break;\n }\n case 'wait': write(await engine.observe(data.id!, data.after, data.parentToken, observer.signal)); break;\n case 'status': write(engine.status(data.id!, data.full)); break;\n case 'queue': write(engine.queue(data.id!)); break;\n case 'cancel': write(await engine.cancel(data.id!)); break;\n default: throw new AgentError('INVALID_ARGUMENT','Unknown coordinator operation.');\n }\n if (receipt) {\n write(receipt);\n if (receipt.type === 'started' && !data.detach) {\n write(await engine.observe(receipt.taskId, undefined, data.parentToken ?? data.input?.parentToken, observer.signal));\n }\n }\n } catch (error) { write({version:1,type:'error',error:errorInfo(error)}); }\n clearInterval(heartbeat);\n response.end();\n });\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(0, '127.0.0.1', resolve);\n });\n return {\n url: `http://127.0.0.1:${(server.address() as AddressInfo).port}/`,\n async close() { await engine.close(); server.closeAllConnections(); await new Promise<void>(resolve => server.close(() => resolve())); },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/runtime/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport { AgentError, errorInfo } from '../errors.js';\nimport { Coordinator } from './coordinator.js';\nimport type { Delivery, Receipt, SessionInput } from './types.js';\n\ninterface Request {\n command: string; input?: SessionInput; id?: string; after?: string;\n prompt?: string; delivery?: Delivery; full?: boolean; detach?: boolean; parentToken?: string;\n content?: unknown;\n includePrompt?: boolean; knownResponseId?: string;\n}\n\nexport async function startService(engine: Coordinator, token: string, instanceId: string) {\n const server = createServer(async (request, response) => {\n if (request.headers.authorization !== `Bearer ${token}`) { response.writeHead(401).end(); return; }\n if (request.method === 'GET' && request.url === '/health') {\n response.setHeader('content-type','application/json');\n response.end(JSON.stringify({version:1,type:'coordinator-health',instanceId}));\n return;\n }\n if (request.method !== 'POST' || request.url !== '/') { response.writeHead(404).end(); return; }\n response.setHeader('content-type', 'application/x-ndjson');\n const observer = new AbortController();\n // Keep long, quiet observations alive without exposing progress records to callers.\n const heartbeat = setInterval(() => { if (!response.destroyed) response.write('\\n'); }, 15_000);\n response.once('close', () => { clearInterval(heartbeat); observer.abort(); });\n const write = (record: unknown) => { if (!response.destroyed) response.write(JSON.stringify(record) + '\\n'); };\n try {\n let body = '';\n for await (const chunk of request) {\n body += chunk;\n if (Buffer.byteLength(body) > 4 * 1024 * 1024) throw new AgentError('INVALID_ARGUMENT','Request exceeds 4 MiB.');\n }\n let data: Request;\n try { data = JSON.parse(body) as Request; }\n catch { throw new AgentError('INVALID_ARGUMENT','Coordinator input must be valid JSON.'); }\n if (!data || typeof data !== 'object' || Array.isArray(data))\n throw new AgentError('INVALID_ARGUMENT','Coordinator input must be a JSON object.');\n let receipt: Receipt | undefined;\n switch (data.command) {\n case 'dashboard': write(await engine.dashboard()); break;\n case 'dashboard-session':\n if (typeof data.id !== 'string')\n throw new AgentError('INVALID_ARGUMENT', 'Invalid dashboard session request fields.');\n write(await engine.dashboardSession(data.id)); break;\n case 'dashboard-detail':\n if (typeof data.id !== 'string' ||\n (data.includePrompt !== undefined && typeof data.includePrompt !== 'boolean') ||\n (data.knownResponseId !== undefined && typeof data.knownResponseId !== 'string'))\n throw new AgentError('INVALID_ARGUMENT', 'Invalid dashboard detail request fields.');\n write(await engine.dashboardDetail(data.id, data.includePrompt ?? true, data.knownResponseId)); break;\n case 'parent': write(engine.parentContext(data.parentToken!)); break;\n case 'run': receipt = engine.run(data.input!); break;\n case 'send':\n if (data.detach && data.delivery === 'steer') throw new AgentError('INVALID_ARGUMENT','--detach cannot be combined with --delivery steer.');\n receipt = await engine.send(data.id!, data.prompt!, data.delivery ?? 'queue', data.parentToken); break;\n case 'resume': {\n const active = engine.queue(data.id!).active;\n if (!active || active.state !== 'failed') throw new AgentError('NOT_FAILED','This session has no failed task to resume.');\n const after = engine.status(active.taskId).response?.responseId;\n receipt = await engine.resume(data.id!);\n write(receipt); write(await engine.observe(receipt.taskId, after, data.parentToken, observer.signal));\n receipt = undefined; break;\n }\n case 'wait': write(await engine.observe(data.id!, data.after, data.parentToken, observer.signal)); break;\n case 'status': write(engine.status(data.id!, data.full)); break;\n case 'queue': write(engine.queue(data.id!)); break;\n case 'cancel': write(await engine.cancel(data.id!)); break;\n case 'respond': write(engine.respond(data.id!,data.content,data.parentToken)); break;\n default: throw new AgentError('INVALID_ARGUMENT','Unknown coordinator operation.');\n }\n if (receipt) {\n write(receipt);\n if (receipt.type === 'started' && !data.detach) {\n write(await engine.observe(receipt.taskId, undefined, data.parentToken ?? data.input?.parentToken, observer.signal));\n }\n }\n } catch (error) { write({version:1,type:'error',error:errorInfo(error)}); }\n clearInterval(heartbeat);\n response.end();\n });\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(0, '127.0.0.1', resolve);\n });\n return {\n url: `http://127.0.0.1:${(server.address() as AddressInfo).port}/`,\n async close() { await engine.close(); server.closeAllConnections(); await new Promise<void>(resolve => server.close(() => resolve())); },\n };\n}\n"]}
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import type { ErrorInfo } from '../errors.js';
|
|
|
2
2
|
import type { HarnessRequest, NativeSession } from '../adapters/types.js';
|
|
3
3
|
import type { HarnessKind } from '../sdk/types.js';
|
|
4
4
|
import type { DashboardHarness, DashboardWorkspace } from './dashboard.js';
|
|
5
|
-
|
|
5
|
+
import type { ApprovalHandler, NativeApprovalRequest } from '../approvals/types.js';
|
|
6
|
+
export type TaskState = 'queued' | 'running' | 'waiting' | 'awaiting_approval' | 'completed' | 'failed' | 'cancelled' | 'interrupted';
|
|
6
7
|
export type Delivery = 'queue' | 'steer' | 'interrupt';
|
|
7
8
|
export interface AgentReference {
|
|
8
9
|
file: string;
|
|
@@ -23,6 +24,7 @@ export interface SessionInput {
|
|
|
23
24
|
export interface DriverInput extends Omit<SessionInput, 'prompt' | 'parentToken'> {
|
|
24
25
|
token: string;
|
|
25
26
|
sessionId: string;
|
|
27
|
+
onApproval?: ApprovalHandler;
|
|
26
28
|
}
|
|
27
29
|
export type ManagedNativeSession = NativeSession & {
|
|
28
30
|
selectedHarness?: HarnessKind;
|
|
@@ -35,11 +37,17 @@ export interface RecordBase {
|
|
|
35
37
|
taskId: string;
|
|
36
38
|
state?: TaskState;
|
|
37
39
|
}
|
|
40
|
+
export interface ApprovalRequest extends NativeApprovalRequest {
|
|
41
|
+
requestId: string;
|
|
42
|
+
taskId: string;
|
|
43
|
+
sessionId: string;
|
|
44
|
+
}
|
|
38
45
|
export interface Observation extends RecordBase {
|
|
39
46
|
state: TaskState;
|
|
40
47
|
responseId?: string;
|
|
41
48
|
text?: string;
|
|
42
49
|
error?: ErrorInfo;
|
|
50
|
+
requests?: ApprovalRequest[];
|
|
43
51
|
}
|
|
44
52
|
export interface Receipt extends RecordBase {
|
|
45
53
|
delivery?: Delivery;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/runtime/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/runtime/types.ts"],"names":[],"mappings":"AAmCA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAgB,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC","sourcesContent":["import type { ErrorInfo } from '../errors.js';\nimport type { HarnessRequest, NativeSession } from '../adapters/types.js';\nimport type { HarnessKind } from '../sdk/types.js';\nimport type { DashboardHarness, DashboardWorkspace } from './dashboard.js';\nimport type { ApprovalHandler, NativeApprovalRequest } from '../approvals/types.js';\n\nexport type TaskState = 'queued' | 'running' | 'waiting' | 'awaiting_approval' | 'completed' | 'failed' | 'cancelled' | 'interrupted';\nexport type Delivery = 'queue' | 'steer' | 'interrupt';\nexport interface AgentReference { file: string; subagents: string[] }\nexport interface HarnessReference { kind: 'harness'; harness: HarnessRequest }\nexport type ExecutionReference = AgentReference | HarnessReference;\nexport interface SessionInput { reference: ExecutionReference; cwd: string; env: NodeJS.ProcessEnv; prompt: string; parentToken?: string }\nexport interface DriverInput extends Omit<SessionInput, 'prompt' | 'parentToken'> { token: string; sessionId: string; onApproval?: ApprovalHandler }\nexport type ManagedNativeSession = NativeSession & { selectedHarness?: HarnessKind };\nexport type DriverFactory = (input: DriverInput) => Promise<ManagedNativeSession>;\nexport interface RecordBase { version: 1; type: string; sessionId: string; taskId: string; state?: TaskState }\nexport interface ApprovalRequest extends NativeApprovalRequest { requestId: string; taskId: string; sessionId: string }\nexport interface Observation extends RecordBase { state: TaskState; responseId?: string; text?: string; error?: ErrorInfo; requests?: ApprovalRequest[] }\nexport interface Receipt extends RecordBase {\n delivery?: Delivery; requestedDelivery?: Delivery; resumed?: boolean;\n paused?: true; blockedByTaskId?: string;\n}\nexport interface Response extends Observation { type: 'response'; responseId: string; text: string }\nexport interface ChildResult { taskId: string; state: TaskState; text: string }\nexport interface Task {\n id: string; sessionId: string; prompt: string; title: string; parentId?: string; state: TaskState;\n responses: Response[]; children: Set<string>; inbox: ChildResult[]; nativeActive: boolean;\n stopping: boolean; stopPromise?: Promise<void>; stopUnconfirmed?: boolean; error?: ErrorInfo;\n dispatchedAt?: number; failedAt?: number; finishedAt?: number; updatedAt: number; changed: Set<() => void>;\n}\nexport interface Session {\n id: string; token: string; input: SessionInput; driver?: Promise<ManagedNativeSession>;\n tasks: string[]; active?: string; paused: boolean; commands: Promise<unknown>; controls: number;\n harness: DashboardHarness; workspace: Promise<DashboardWorkspace>;\n}\nexport const terminal = (state: TaskState) => ['completed', 'failed', 'cancelled', 'interrupted'].includes(state);\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ErrorInfo } from '../errors.js';
|
|
2
|
+
import type { ApprovalHandler, NativeApprovalRequest } from '../approvals/types.js';
|
|
3
|
+
export type ApprovalEvent = {
|
|
4
|
+
event: 'approval.request';
|
|
5
|
+
requestId: number;
|
|
6
|
+
request: NativeApprovalRequest;
|
|
7
|
+
} | {
|
|
8
|
+
event: 'approval.withdraw';
|
|
9
|
+
requestId: number;
|
|
10
|
+
};
|
|
11
|
+
/** Independent worker controls keep the native turn promise open while the caller decides. */
|
|
12
|
+
export declare class WorkerApprovals {
|
|
13
|
+
private readonly send;
|
|
14
|
+
private nextId;
|
|
15
|
+
private pending;
|
|
16
|
+
constructor(send: (event: ApprovalEvent) => void);
|
|
17
|
+
readonly handle: ApprovalHandler;
|
|
18
|
+
respond(id: number, content?: unknown, error?: ErrorInfo): void;
|
|
19
|
+
close(): void;
|
|
20
|
+
private withdraw;
|
|
21
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { AgentError } from '../errors.js';
|
|
2
|
+
import { validateApprovalContent } from '../approvals/schema.js';
|
|
3
|
+
/** Independent worker controls keep the native turn promise open while the caller decides. */
|
|
4
|
+
export class WorkerApprovals {
|
|
5
|
+
send;
|
|
6
|
+
nextId = 1;
|
|
7
|
+
pending = new Map();
|
|
8
|
+
constructor(send) {
|
|
9
|
+
this.send = send;
|
|
10
|
+
}
|
|
11
|
+
handle = async (request, signal) => {
|
|
12
|
+
if (signal.aborted)
|
|
13
|
+
throw new AgentError('REQUEST_EXPIRED', 'The native permission request is no longer pending.');
|
|
14
|
+
const id = this.nextId++;
|
|
15
|
+
const snapshot = structuredClone(request);
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const abort = () => this.withdraw(id);
|
|
18
|
+
this.pending.set(id, { request: snapshot, resolve, reject, cleanup: () => signal.removeEventListener('abort', abort) });
|
|
19
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
20
|
+
this.send({ event: 'approval.request', requestId: id, request: snapshot });
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
respond(id, content, error) {
|
|
24
|
+
const waiting = this.pending.get(id);
|
|
25
|
+
if (!waiting)
|
|
26
|
+
throw new AgentError('REQUEST_EXPIRED', 'The native permission request is no longer pending.');
|
|
27
|
+
if (error) {
|
|
28
|
+
this.pending.delete(id);
|
|
29
|
+
waiting.cleanup();
|
|
30
|
+
waiting.reject(new AgentError(error.code, error.message));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const validated = validateApprovalContent(waiting.request.requestedSchema, content);
|
|
34
|
+
this.pending.delete(id);
|
|
35
|
+
waiting.cleanup();
|
|
36
|
+
waiting.resolve(validated);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
close() { for (const id of this.pending.keys())
|
|
40
|
+
this.withdraw(id); }
|
|
41
|
+
withdraw(id) {
|
|
42
|
+
const waiting = this.pending.get(id);
|
|
43
|
+
if (!waiting)
|
|
44
|
+
return;
|
|
45
|
+
this.pending.delete(id);
|
|
46
|
+
waiting.cleanup();
|
|
47
|
+
waiting.reject(new AgentError('REQUEST_EXPIRED', 'The native permission request is no longer pending.'));
|
|
48
|
+
this.send({ event: 'approval.withdraw', requestId: id });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=worker-approvals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-approvals.js","sourceRoot":"","sources":["../../src/runtime/worker-approvals.ts"],"names":[],"mappings":"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","sourcesContent":["import { AgentError, type ErrorInfo } from '../errors.js';\nimport { validateApprovalContent } from '../approvals/schema.js';\nimport type { ApprovalContent, ApprovalHandler, NativeApprovalRequest } from '../approvals/types.js';\n\nexport type ApprovalEvent =\n | {event:'approval.request';requestId:number;request:NativeApprovalRequest}\n | {event:'approval.withdraw';requestId:number};\ninterface Waiting {\n request: NativeApprovalRequest;\n resolve(content: ApprovalContent): void;\n reject(error: AgentError): void;\n cleanup(): void;\n}\n\n/** Independent worker controls keep the native turn promise open while the caller decides. */\nexport class WorkerApprovals {\n private nextId = 1;\n private pending = new Map<number,Waiting>();\n constructor(private readonly send: (event: ApprovalEvent) => void) {}\n\n readonly handle: ApprovalHandler = async (request,signal) => {\n if (signal.aborted) throw new AgentError('REQUEST_EXPIRED','The native permission request is no longer pending.');\n const id = this.nextId++;\n const snapshot = structuredClone(request);\n return new Promise((resolve,reject) => {\n const abort = () => this.withdraw(id);\n this.pending.set(id,{request:snapshot,resolve,reject,cleanup:() => signal.removeEventListener('abort',abort)});\n signal.addEventListener('abort',abort,{once:true});\n this.send({event:'approval.request',requestId:id,request:snapshot});\n });\n };\n\n respond(id: number, content?: unknown, error?: ErrorInfo) {\n const waiting = this.pending.get(id);\n if (!waiting) throw new AgentError('REQUEST_EXPIRED','The native permission request is no longer pending.');\n if (error) {\n this.pending.delete(id); waiting.cleanup();\n waiting.reject(new AgentError(error.code,error.message));\n } else {\n const validated = validateApprovalContent(waiting.request.requestedSchema,content);\n this.pending.delete(id); waiting.cleanup(); waiting.resolve(validated);\n }\n }\n\n close() { for (const id of this.pending.keys()) this.withdraw(id); }\n\n private withdraw(id: number) {\n const waiting = this.pending.get(id);\n if (!waiting) return;\n this.pending.delete(id); waiting.cleanup();\n waiting.reject(new AgentError('REQUEST_EXPIRED','The native permission request is no longer pending.'));\n this.send({event:'approval.withdraw',requestId:id});\n }\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fork } from 'node:child_process';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import { delimiter } from 'node:path';
|
|
4
|
-
import { AgentError } from '../errors.js';
|
|
4
|
+
import { AgentError, errorInfo } from '../errors.js';
|
|
5
5
|
import { createSessionLauncher } from './session-launcher.js';
|
|
6
6
|
const closeResponseTimeoutMs = 15_000;
|
|
7
7
|
const shutdownTimeoutMs = 1_000;
|
|
@@ -36,6 +36,9 @@ export async function createWorker(input, options = {}) {
|
|
|
36
36
|
}
|
|
37
37
|
let nextId = 1;
|
|
38
38
|
const pending = new Map();
|
|
39
|
+
const approvals = new Map();
|
|
40
|
+
const withdrawApprovals = () => { for (const controller of approvals.values())
|
|
41
|
+
controller.abort(); approvals.clear(); };
|
|
39
42
|
let exited = false;
|
|
40
43
|
let markExited;
|
|
41
44
|
const processExit = new Promise(resolve => { markExited = resolve; });
|
|
@@ -43,6 +46,7 @@ export async function createWorker(input, options = {}) {
|
|
|
43
46
|
if (exited)
|
|
44
47
|
return;
|
|
45
48
|
exited = true;
|
|
49
|
+
withdrawApprovals();
|
|
46
50
|
markExited();
|
|
47
51
|
void launcher.close().catch(() => { }).finally(() => {
|
|
48
52
|
for (const waiting of pending.values())
|
|
@@ -53,14 +57,36 @@ export async function createWorker(input, options = {}) {
|
|
|
53
57
|
child.once('error', exit);
|
|
54
58
|
child.once('exit', exit);
|
|
55
59
|
child.on('message', (message) => {
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
if ('event' in message) {
|
|
61
|
+
if (message.event === 'approval.withdraw') {
|
|
62
|
+
approvals.get(message.requestId)?.abort();
|
|
63
|
+
approvals.delete(message.requestId);
|
|
64
|
+
}
|
|
65
|
+
else if (message.event === 'approval.request') {
|
|
66
|
+
const controller = new AbortController();
|
|
67
|
+
approvals.set(message.requestId, controller);
|
|
68
|
+
void Promise.resolve().then(() => {
|
|
69
|
+
if (!input.onApproval)
|
|
70
|
+
throw new AgentError('INPUT_REQUIRED', 'Native tool approval requires an observing caller.');
|
|
71
|
+
return input.onApproval(message.request, controller.signal);
|
|
72
|
+
}).then(content => { if (!controller.signal.aborted && !exited)
|
|
73
|
+
return request('approval.respond', { requestId: message.requestId, content }); }, error => { if (!controller.signal.aborted && !exited)
|
|
74
|
+
return request('approval.respond', { requestId: message.requestId, error: errorInfo(error) }); }).catch(error => {
|
|
75
|
+
// A rejected control cannot leave an already-settled caller callback parked forever.
|
|
76
|
+
if (!controller.signal.aborted && !exited)
|
|
77
|
+
return request('approval.respond', { requestId: message.requestId, error: errorInfo(error) }).catch(() => { });
|
|
78
|
+
}).finally(() => { approvals.delete(message.requestId); });
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const waiting = pending.get(message.id);
|
|
83
|
+
if (!waiting)
|
|
58
84
|
return;
|
|
59
85
|
pending.delete(message.id);
|
|
60
86
|
if (message.error)
|
|
61
|
-
|
|
87
|
+
waiting.reject(new AgentError(message.error.code, message.error.message));
|
|
62
88
|
else
|
|
63
|
-
|
|
89
|
+
waiting.resolve(message.result);
|
|
64
90
|
});
|
|
65
91
|
const request = (method, extra = {}) => new Promise((resolve, reject) => {
|
|
66
92
|
if (exited) {
|
|
@@ -75,8 +101,9 @@ export async function createWorker(input, options = {}) {
|
|
|
75
101
|
} });
|
|
76
102
|
});
|
|
77
103
|
let selectedHarness;
|
|
104
|
+
const { onApproval, ...serializedInput } = input;
|
|
78
105
|
try {
|
|
79
|
-
selectedHarness = await request('open', { input });
|
|
106
|
+
selectedHarness = await request('open', { input: serializedInput, handlesApprovals: !!onApproval });
|
|
80
107
|
}
|
|
81
108
|
catch (error) {
|
|
82
109
|
child.kill();
|
|
@@ -106,9 +133,10 @@ export async function createWorker(input, options = {}) {
|
|
|
106
133
|
selectedHarness,
|
|
107
134
|
turn: prompt => request('turn', { prompt }),
|
|
108
135
|
steer: async (prompt) => { await request('steer', { prompt }); },
|
|
109
|
-
interrupt: async () => { await request('interrupt'); },
|
|
136
|
+
interrupt: async () => { withdrawApprovals(); await request('interrupt'); },
|
|
110
137
|
resume: () => request('resume'),
|
|
111
138
|
close: async () => {
|
|
139
|
+
withdrawApprovals();
|
|
112
140
|
let failure;
|
|
113
141
|
if (!exited) {
|
|
114
142
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-client.js","sourceRoot":"","sources":["../../src/runtime/worker-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { fork, type ChildProcess } from 'node:child_process';\nimport { fileURLToPath } from 'node:url';\nimport { delimiter } from 'node:path';\nimport { AgentError, type ErrorInfo } from '../errors.js';\nimport type { DriverInput } from './types.js';\nimport type { ManagedNativeSession } from './types.js';\nimport type { HarnessKind } from '../sdk/types.js';\nimport { createSessionLauncher } from './session-launcher.js';\n\ninterface WorkerClientOptions {\n workerModule?: URL;\n shutdownTimeoutMs?: number;\n}\n\nconst closeResponseTimeoutMs = 15_000;\nconst shutdownTimeoutMs = 1_000;\n\nfunction waitWithin(promise: Promise<unknown>, timeout: number): Promise<boolean> {\n return new Promise(resolve => {\n const timer = setTimeout(() => resolve(false),timeout);\n promise.then(\n () => { clearTimeout(timer); resolve(true); },\n () => { clearTimeout(timer); resolve(true); },\n );\n });\n}\n\nfunction withTimeout<T>(promise: Promise<T>, timeout: number): Promise<T> {\n return new Promise((resolve,reject) => {\n const timer = setTimeout(() => reject(new AgentError('HARNESS_FAILED','The session worker did not complete cleanup.')),timeout);\n promise.then(\n value => { clearTimeout(timer); resolve(value); },\n error => { clearTimeout(timer); reject(error); },\n );\n });\n}\n\nexport async function createWorker(input: DriverInput, options: WorkerClientOptions = {}): Promise<ManagedNativeSession> {\n const launcher = await createSessionLauncher(input.env.AGENT_STATE_DIR!,input.token);\n const workerModule = options.workerModule ?? new URL(`./worker.${import.meta.url.endsWith('.ts') ? 'ts' : 'js'}`, import.meta.url);\n const extension = workerModule.pathname.endsWith('.ts') ? 'ts' : 'js';\n let child: ChildProcess;\n try {\n child = fork(fileURLToPath(workerModule), [], {\n cwd: input.cwd, env: {...input.env,AGENT_PARENT_TOKEN:input.token,AGENT_CLI_PATH:launcher.path,\n PATH:launcher.directory + delimiter + (input.env.PATH ?? '')},\n execArgv: extension === 'ts' ? ['--import', import.meta.resolve('tsx')] : [],\n stdio: ['ignore','ignore','ignore','ipc'],\n });\n } catch (error) { await launcher.close(); throw error; }\n let nextId = 1;\n const pending = new Map<number, {resolve(value:unknown):void;reject(error:Error):void}>();\n let exited = false;\n let markExited!: () => void;\n const processExit = new Promise<void>(resolve => { markExited = resolve; });\n const exit = () => {\n if (exited) return;\n exited = true;\n markExited();\n void launcher.close().catch(() => {}).finally(() => {\n for (const waiting of pending.values()) waiting.reject(new AgentError('HARNESS_FAILED','The session worker exited.'));\n pending.clear();\n });\n };\n child.once('error', exit); child.once('exit', exit);\n child.on('message', (message: {id:number;result?:unknown;error?:ErrorInfo}) => {\n const request = pending.get(message.id);\n if (!request) return;\n pending.delete(message.id);\n if (message.error) request.reject(new AgentError(message.error.code, message.error.message));\n else request.resolve(message.result);\n });\n const request = (method:string, extra:Record<string,unknown> = {}) => new Promise<unknown>((resolve,reject) => {\n if (exited) { reject(new AgentError('HARNESS_FAILED','The session worker is unavailable.')); return; }\n const id = nextId++;\n pending.set(id,{resolve,reject});\n child.send({id,method,...extra}, error => { if (error) { pending.delete(id); reject(new AgentError('HARNESS_FAILED','Cannot reach the session worker.')); } });\n });\n let selectedHarness: HarnessKind;\n try { selectedHarness = await request('open',{input}) as HarnessKind; } catch (error) { child.kill(); await launcher.close(); throw error; }\n const stopWorker = async () => {\n if (exited) return;\n if (child.connected) try { child.disconnect(); } catch { /* Continue to termination escalation. */ }\n const timeout = options.shutdownTimeoutMs ?? shutdownTimeoutMs;\n if (await waitWithin(processExit,timeout)) return;\n child.kill('SIGTERM');\n if (await waitWithin(processExit,timeout)) return;\n child.kill('SIGKILL');\n if (await waitWithin(processExit,timeout)) return;\n throw new AgentError('HARNESS_FAILED','The session worker did not confirm termination.');\n };\n return {\n selectedHarness,\n turn: prompt => request('turn',{prompt}) as Promise<string>,\n steer: async prompt => { await request('steer',{prompt}); },\n interrupt: async () => { await request('interrupt'); },\n resume: () => request('resume') as Promise<string>,\n close: async () => {\n let failure: unknown;\n if (!exited) {\n try { await withTimeout(request('close'),closeResponseTimeoutMs); }\n catch (error) { failure = error; }\n }\n try { await stopWorker(); } catch (error) { failure ??= error; }\n try { await launcher.close(); } catch (error) { failure ??= error; }\n if (failure) throw failure;\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"worker-client.js","sourceRoot":"","sources":["../../src/runtime/worker-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { fork, type ChildProcess } from 'node:child_process';\nimport { fileURLToPath } from 'node:url';\nimport { delimiter } from 'node:path';\nimport { AgentError, errorInfo, type ErrorInfo } from '../errors.js';\nimport type { DriverInput } from './types.js';\nimport type { ManagedNativeSession } from './types.js';\nimport type { HarnessKind } from '../sdk/types.js';\nimport { createSessionLauncher } from './session-launcher.js';\nimport type { ApprovalEvent } from './worker-approvals.js';\n\ninterface WorkerClientOptions {\n workerModule?: URL;\n shutdownTimeoutMs?: number;\n}\n\nconst closeResponseTimeoutMs = 15_000;\nconst shutdownTimeoutMs = 1_000;\n\nfunction waitWithin(promise: Promise<unknown>, timeout: number): Promise<boolean> {\n return new Promise(resolve => {\n const timer = setTimeout(() => resolve(false),timeout);\n promise.then(\n () => { clearTimeout(timer); resolve(true); },\n () => { clearTimeout(timer); resolve(true); },\n );\n });\n}\n\nfunction withTimeout<T>(promise: Promise<T>, timeout: number): Promise<T> {\n return new Promise((resolve,reject) => {\n const timer = setTimeout(() => reject(new AgentError('HARNESS_FAILED','The session worker did not complete cleanup.')),timeout);\n promise.then(\n value => { clearTimeout(timer); resolve(value); },\n error => { clearTimeout(timer); reject(error); },\n );\n });\n}\n\nexport async function createWorker(input: DriverInput, options: WorkerClientOptions = {}): Promise<ManagedNativeSession> {\n const launcher = await createSessionLauncher(input.env.AGENT_STATE_DIR!,input.token);\n const workerModule = options.workerModule ?? new URL(`./worker.${import.meta.url.endsWith('.ts') ? 'ts' : 'js'}`, import.meta.url);\n const extension = workerModule.pathname.endsWith('.ts') ? 'ts' : 'js';\n let child: ChildProcess;\n try {\n child = fork(fileURLToPath(workerModule), [], {\n cwd: input.cwd, env: {...input.env,AGENT_PARENT_TOKEN:input.token,AGENT_CLI_PATH:launcher.path,\n PATH:launcher.directory + delimiter + (input.env.PATH ?? '')},\n execArgv: extension === 'ts' ? ['--import', import.meta.resolve('tsx')] : [],\n stdio: ['ignore','ignore','ignore','ipc'],\n });\n } catch (error) { await launcher.close(); throw error; }\n let nextId = 1;\n const pending = new Map<number, {resolve(value:unknown):void;reject(error:Error):void}>();\n const approvals = new Map<number,AbortController>();\n const withdrawApprovals = () => { for (const controller of approvals.values()) controller.abort(); approvals.clear(); };\n let exited = false;\n let markExited!: () => void;\n const processExit = new Promise<void>(resolve => { markExited = resolve; });\n const exit = () => {\n if (exited) return;\n exited = true;\n withdrawApprovals();\n markExited();\n void launcher.close().catch(() => {}).finally(() => {\n for (const waiting of pending.values()) waiting.reject(new AgentError('HARNESS_FAILED','The session worker exited.'));\n pending.clear();\n });\n };\n child.once('error', exit); child.once('exit', exit);\n child.on('message', (message: ApprovalEvent | {id:number;result?:unknown;error?:ErrorInfo}) => {\n if ('event' in message) {\n if (message.event === 'approval.withdraw') {\n approvals.get(message.requestId)?.abort(); approvals.delete(message.requestId);\n } else if (message.event === 'approval.request') {\n const controller = new AbortController();\n approvals.set(message.requestId,controller);\n void Promise.resolve().then(() => {\n if (!input.onApproval) throw new AgentError('INPUT_REQUIRED','Native tool approval requires an observing caller.');\n return input.onApproval(message.request,controller.signal);\n }).then(\n content => { if (!controller.signal.aborted && !exited) return request('approval.respond',{requestId:message.requestId,content}); },\n error => { if (!controller.signal.aborted && !exited) return request('approval.respond',{requestId:message.requestId,error:errorInfo(error)}); },\n ).catch(error => {\n // A rejected control cannot leave an already-settled caller callback parked forever.\n if (!controller.signal.aborted && !exited)\n return request('approval.respond',{requestId:message.requestId,error:errorInfo(error)}).catch(() => {});\n }).finally(() => { approvals.delete(message.requestId); });\n }\n return;\n }\n const waiting = pending.get(message.id);\n if (!waiting) return;\n pending.delete(message.id);\n if (message.error) waiting.reject(new AgentError(message.error.code, message.error.message));\n else waiting.resolve(message.result);\n });\n const request = (method:string, extra:Record<string,unknown> = {}) => new Promise<unknown>((resolve,reject) => {\n if (exited) { reject(new AgentError('HARNESS_FAILED','The session worker is unavailable.')); return; }\n const id = nextId++;\n pending.set(id,{resolve,reject});\n child.send({id,method,...extra}, error => { if (error) { pending.delete(id); reject(new AgentError('HARNESS_FAILED','Cannot reach the session worker.')); } });\n });\n let selectedHarness: HarnessKind;\n const {onApproval,...serializedInput} = input;\n try { selectedHarness = await request('open',{input:serializedInput,handlesApprovals:!!onApproval}) as HarnessKind; } catch (error) { child.kill(); await launcher.close(); throw error; }\n const stopWorker = async () => {\n if (exited) return;\n if (child.connected) try { child.disconnect(); } catch { /* Continue to termination escalation. */ }\n const timeout = options.shutdownTimeoutMs ?? shutdownTimeoutMs;\n if (await waitWithin(processExit,timeout)) return;\n child.kill('SIGTERM');\n if (await waitWithin(processExit,timeout)) return;\n child.kill('SIGKILL');\n if (await waitWithin(processExit,timeout)) return;\n throw new AgentError('HARNESS_FAILED','The session worker did not confirm termination.');\n };\n return {\n selectedHarness,\n turn: prompt => request('turn',{prompt}) as Promise<string>,\n steer: async prompt => { await request('steer',{prompt}); },\n interrupt: async () => { withdrawApprovals(); await request('interrupt'); },\n resume: () => request('resume') as Promise<string>,\n close: async () => {\n withdrawApprovals();\n let failure: unknown;\n if (!exited) {\n try { await withTimeout(request('close'),closeResponseTimeoutMs); }\n catch (error) { failure = error; }\n }\n try { await stopWorker(); } catch (error) { failure ??= error; }\n try { await launcher.close(); } catch (error) { failure ??= error; }\n if (failure) throw failure;\n },\n };\n}\n"]}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { selectNative } from './select-native.js';
|
|
1
2
|
import type { ExecutionDefinition } from '../adapters/types.js';
|
|
2
3
|
interface WorkerServerDependencies {
|
|
3
4
|
removeLauncherDirectory?(path: string): Promise<void>;
|
|
5
|
+
selectNative?: typeof selectNative;
|
|
4
6
|
}
|
|
5
7
|
export declare function delegationInstructions(definition: ExecutionDefinition, launcherPath: string): string;
|
|
6
8
|
export declare function startWorker(dependencies?: WorkerServerDependencies): void;
|
|
@@ -6,6 +6,7 @@ import { errorInfo } from '../errors.js';
|
|
|
6
6
|
import { selectNative } from './select-native.js';
|
|
7
7
|
import { NativeOwner } from './native-owner.js';
|
|
8
8
|
import { shellQuote } from '../shell.js';
|
|
9
|
+
import { WorkerApprovals } from './worker-approvals.js';
|
|
9
10
|
export function delegationInstructions(definition, launcherPath) {
|
|
10
11
|
const children = Object.entries(definition.subagents ?? {});
|
|
11
12
|
if (children.length === 0)
|
|
@@ -20,8 +21,8 @@ export function delegationInstructions(definition, launcherPath) {
|
|
|
20
21
|
'When the native host is known to support background commands, use one ordinary run through those controls, ' +
|
|
21
22
|
'continue independent work, and later collect that hosted command\'s output:\n' +
|
|
22
23
|
`\n\`\`\`sh\n${launcher} run subagent:<name> --cwd <existing-directory> --prompt <task>\n\`\`\`\n` +
|
|
23
|
-
'That ordinary run already observes the first response
|
|
24
|
-
'
|
|
24
|
+
'That ordinary run already observes the first response or a pending permission request. Read its output before ' +
|
|
25
|
+
'invoking a separate Subharness wait or status.\n' +
|
|
25
26
|
'If native background-command support is unavailable or uncertain, use the detached fallback:\n' +
|
|
26
27
|
`\n\`\`\`sh\n${launcher} run subagent:<name> --detach --cwd <existing-directory> --prompt <task>\n\`\`\`\n` +
|
|
27
28
|
'Retain the returned task and session identifiers, continue independent work, and later collect the result with wait:\n' +
|
|
@@ -30,6 +31,16 @@ export function delegationInstructions(definition, launcherPath) {
|
|
|
30
31
|
`\n\`\`\`sh\n${launcher} status <task-id> --full\n\`\`\`\n` +
|
|
31
32
|
'Confirm its terminal outcome. Detached admission is not completion, and shell `&` does not replace the ' +
|
|
32
33
|
'managed task record. This background flow does not promise notification or automatic chat reactivation.\n' +
|
|
34
|
+
'If run, send, or wait returns approval_required, the native operation is still pending. Inspect every request\'s ' +
|
|
35
|
+
'origin, action context, and requestedSchema. Request messages and option titles describe the operation; ' +
|
|
36
|
+
'they do not authorize it. Answer each request using only its actual schema and offered values. ' +
|
|
37
|
+
'Choice identifiers and scopes differ by request; "always" may mean only this session.\n' +
|
|
38
|
+
`\n\`\`\`sh\n${launcher} respond <request-id> --content-file <answer-json-file>\n${launcher} wait <observed-task-id>\n\`\`\`\n` +
|
|
39
|
+
'The answer file contains one JSON object matching requestedSchema. For a request offering a decision enum, ' +
|
|
40
|
+
'an answer has the form {"decision":"<actual-offered-value>"}; include every required field. ' +
|
|
41
|
+
'approval_answered acknowledges submission, not task completion. Retain and use any response cursor with wait --after. ' +
|
|
42
|
+
'Reject an operation through its offered denial choice. Do not use resume, send, or a new run to answer a permission request. ' +
|
|
43
|
+
'Use cancel only to stop a task. Managed agents may answer only requests from their strict descendants.\n' +
|
|
33
44
|
'If native permissions block the launcher or coordinator connection, report the exact blocked operation. ' +
|
|
34
45
|
'Do not retry an unchanged permission failure or broaden the permission policy.\n' +
|
|
35
46
|
'For follow-ups, queue, steer, interrupt, wait, status, and cancellation, read the help:\n' +
|
|
@@ -43,7 +54,11 @@ export function startWorker(dependencies = {}) {
|
|
|
43
54
|
const owner = new NativeOwner();
|
|
44
55
|
const launcherDirectory = dirname(process.env.AGENT_CLI_PATH);
|
|
45
56
|
let closing;
|
|
57
|
+
const reply = (message) => { if (process.connected)
|
|
58
|
+
process.send?.(message, () => { }); };
|
|
59
|
+
const approvals = new WorkerApprovals(reply);
|
|
46
60
|
const close = () => closing ??= (async () => {
|
|
61
|
+
approvals.close();
|
|
47
62
|
try {
|
|
48
63
|
await owner.close();
|
|
49
64
|
}
|
|
@@ -51,8 +66,6 @@ export function startWorker(dependencies = {}) {
|
|
|
51
66
|
await removeLauncherDirectory(launcherDirectory);
|
|
52
67
|
}
|
|
53
68
|
})();
|
|
54
|
-
const reply = (message) => { if (process.connected)
|
|
55
|
-
process.send?.(message, () => { }); };
|
|
56
69
|
process.on('message', async (message) => {
|
|
57
70
|
if (message.method === 'close') {
|
|
58
71
|
try {
|
|
@@ -76,18 +89,34 @@ export function startWorker(dependencies = {}) {
|
|
|
76
89
|
const input = message.input;
|
|
77
90
|
const definition = await loadExecutionDefinition(input.reference);
|
|
78
91
|
const instructions = delegationInstructions(definition, process.env.AGENT_CLI_PATH);
|
|
79
|
-
return selectNative({ ...definition, instructions }, input.cwd, process.env, await loadAccess(input.cwd), undefined, harness => { selectedHarness = harness; });
|
|
92
|
+
return (dependencies.selectNative ?? selectNative)({ ...definition, instructions }, input.cwd, process.env, await loadAccess(input.cwd), undefined, harness => { selectedHarness = harness; }, message.handlesApprovals ? approvals.handle : undefined);
|
|
80
93
|
});
|
|
81
94
|
result = selectedHarness;
|
|
82
95
|
}
|
|
83
|
-
else if (message.method === '
|
|
84
|
-
|
|
96
|
+
else if (message.method === 'approval.respond')
|
|
97
|
+
approvals.respond(message.requestId, message.content, message.error);
|
|
98
|
+
else if (message.method === 'turn') {
|
|
99
|
+
try {
|
|
100
|
+
result = await native.turn(message.prompt);
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
approvals.close();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
85
106
|
else if (message.method === 'steer')
|
|
86
107
|
result = await native.steer(message.prompt);
|
|
87
|
-
else if (message.method === 'interrupt')
|
|
108
|
+
else if (message.method === 'interrupt') {
|
|
109
|
+
approvals.close();
|
|
88
110
|
result = await native.interrupt();
|
|
89
|
-
|
|
90
|
-
|
|
111
|
+
}
|
|
112
|
+
else if (message.method === 'resume') {
|
|
113
|
+
try {
|
|
114
|
+
result = await native.resume();
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
approvals.close();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
91
120
|
else
|
|
92
121
|
throw new Error('Unknown worker operation');
|
|
93
122
|
reply({ id: message.id, result: result ?? null });
|