subharness 0.0.5 → 0.0.7
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 +59 -3
- package/dist/adapters/claude-process.js +8 -1
- package/dist/adapters/claude-process.js.map +1 -1
- package/dist/adapters/claude-tools.d.ts +6 -2
- package/dist/adapters/claude-tools.js +14 -12
- package/dist/adapters/claude-tools.js.map +1 -1
- package/dist/adapters/claude-worker-client.d.ts +49 -0
- package/dist/adapters/claude-worker-client.js +359 -0
- package/dist/adapters/claude-worker-client.js.map +1 -0
- package/dist/adapters/claude-worker-process.d.ts +20 -0
- package/dist/adapters/claude-worker-process.js +76 -0
- package/dist/adapters/claude-worker-process.js.map +1 -0
- package/dist/adapters/claude-worker-protocol.d.ts +38 -0
- package/dist/adapters/claude-worker-protocol.js +2 -0
- package/dist/adapters/claude-worker-protocol.js.map +1 -0
- package/dist/adapters/claude-worker.d.ts +1 -0
- package/dist/adapters/claude-worker.js +126 -0
- package/dist/adapters/claude-worker.js.map +1 -0
- package/dist/adapters/claude.d.ts +16 -6
- package/dist/adapters/claude.js +138 -68
- package/dist/adapters/claude.js.map +1 -1
- package/dist/cli/catalog-worker.js.map +1 -1
- package/dist/cli/dashboard-client.js +19 -30
- package/dist/cli/dashboard-client.js.map +1 -1
- package/dist/cli/main.js +53 -45
- package/dist/cli/main.js.map +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime/approval-registry.d.ts +9 -0
- package/dist/runtime/approval-registry.js +143 -4
- package/dist/runtime/approval-registry.js.map +1 -1
- package/dist/runtime/capture.d.ts +22 -0
- package/dist/runtime/capture.js +320 -0
- package/dist/runtime/capture.js.map +1 -0
- package/dist/runtime/catalog.d.ts +39 -0
- package/dist/runtime/catalog.js +84 -0
- package/dist/runtime/catalog.js.map +1 -0
- package/dist/runtime/client.d.ts +2 -1
- package/dist/runtime/client.js +52 -65
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/coordinator.d.ts +44 -7
- package/dist/runtime/coordinator.js +265 -31
- package/dist/runtime/coordinator.js.map +1 -1
- package/dist/runtime/daemon.js +12 -6
- package/dist/runtime/daemon.js.map +1 -1
- package/dist/runtime/dashboard-workspace.js +5 -1
- package/dist/runtime/dashboard-workspace.js.map +1 -1
- package/dist/runtime/definition.d.ts +6 -1
- package/dist/runtime/definition.js +31 -1
- package/dist/runtime/definition.js.map +1 -1
- package/dist/runtime/native-owner.js +7 -1
- package/dist/runtime/native-owner.js.map +1 -1
- package/dist/runtime/pagination.d.ts +9 -0
- package/dist/runtime/pagination.js +36 -0
- package/dist/runtime/pagination.js.map +1 -0
- package/dist/runtime/sdk-observation.d.ts +8 -0
- package/dist/runtime/sdk-observation.js +75 -0
- package/dist/runtime/sdk-observation.js.map +1 -0
- package/dist/runtime/sdk-projection.d.ts +8 -0
- package/dist/runtime/sdk-projection.js +19 -0
- package/dist/runtime/sdk-projection.js.map +1 -0
- package/dist/runtime/sdk-service.d.ts +11 -0
- package/dist/runtime/sdk-service.js +109 -0
- package/dist/runtime/sdk-service.js.map +1 -0
- package/dist/runtime/select-native.d.ts +4 -1
- package/dist/runtime/select-native.js +14 -17
- package/dist/runtime/select-native.js.map +1 -1
- package/dist/runtime/service.d.ts +11 -1
- package/dist/runtime/service.js +178 -3
- package/dist/runtime/service.js.map +1 -1
- package/dist/runtime/session-launcher.js +1 -1
- package/dist/runtime/session-launcher.js.map +1 -1
- package/dist/runtime/snapshots.d.ts +103 -0
- package/dist/runtime/snapshots.js +124 -0
- package/dist/runtime/snapshots.js.map +1 -0
- package/dist/runtime/state.d.ts +26 -0
- package/dist/runtime/state.js +86 -17
- package/dist/runtime/state.js.map +1 -1
- package/dist/runtime/task-data.d.ts +38 -0
- package/dist/runtime/task-data.js +2 -0
- package/dist/runtime/task-data.js.map +1 -0
- package/dist/runtime/transport.d.ts +13 -0
- package/dist/runtime/transport.js +166 -0
- package/dist/runtime/transport.js.map +1 -0
- package/dist/runtime/types.d.ts +28 -9
- package/dist/runtime/types.js.map +1 -1
- package/dist/runtime/worker-client.js +2 -0
- package/dist/runtime/worker-client.js.map +1 -1
- package/dist/runtime/worker-server.js +105 -24
- package/dist/runtime/worker-server.js.map +1 -1
- package/dist/sdk/connected-types.d.ts +42 -0
- package/dist/sdk/connected-types.js +2 -0
- package/dist/sdk/connected-types.js.map +1 -0
- package/dist/sdk/connected.d.ts +3 -0
- package/dist/sdk/connected.js +232 -0
- package/dist/sdk/connected.js.map +1 -0
- package/dist/sdk/execution-definition.d.ts +3 -0
- package/dist/sdk/execution-definition.js +59 -0
- package/dist/sdk/execution-definition.js.map +1 -0
- package/dist/sdk/execution-driver.d.ts +6 -0
- package/dist/sdk/execution-driver.js +86 -0
- package/dist/sdk/execution-driver.js.map +1 -0
- package/dist/sdk/execution-observation.d.ts +3 -0
- package/dist/sdk/execution-observation.js +35 -0
- package/dist/sdk/execution-observation.js.map +1 -0
- package/dist/sdk/execution-options.d.ts +17 -0
- package/dist/sdk/execution-options.js +115 -0
- package/dist/sdk/execution-options.js.map +1 -0
- package/dist/sdk/execution-types.d.ts +72 -0
- package/dist/sdk/execution-types.js +2 -0
- package/dist/sdk/execution-types.js.map +1 -0
- package/dist/sdk/execution.d.ts +3 -0
- package/dist/sdk/execution.js +3 -0
- package/dist/sdk/execution.js.map +1 -0
- package/dist/sdk/hosted-error.d.ts +3 -0
- package/dist/sdk/hosted-error.js +14 -0
- package/dist/sdk/hosted-error.js.map +1 -0
- package/dist/sdk/hosted.d.ts +23 -0
- package/dist/sdk/hosted.js +204 -0
- package/dist/sdk/hosted.js.map +1 -0
- package/dist/sdk/runner.d.ts +5 -0
- package/dist/sdk/runner.js +107 -0
- package/dist/sdk/runner.js.map +1 -0
- package/dist/sdk/tools.js +4 -1
- package/dist/sdk/tools.js.map +1 -1
- package/package.json +3 -3
- package/sdk/access-config.md +2 -0
- package/sdk/adapter-contract.md +8 -0
- package/sdk/agent.md +1 -1
- package/sdk/approvals.md +2 -0
- package/sdk/completion-notifications.md +2 -0
- package/sdk/distribution.md +4 -2
- package/sdk/examples/chat-tool.ts +126 -0
- package/sdk/execution.md +743 -0
- package/sdk/index.md +45 -21
- package/sdk/message-delivery.md +2 -0
- package/sdk/permissions.md +2 -0
- package/sdk/plugins/sub-agents.md +4 -2
- package/sdk/project-team.md +23 -1
- package/sdk/sessions.md +2 -0
- package/sdk/tools.md +3 -1
- package/sdk/v1-runtime.md +7 -5
- package/dist/cli/catalog.d.ts +0 -26
- package/dist/cli/catalog.js +0 -41
- package/dist/cli/catalog.js.map +0 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CompleteResponse, SdkTaskSnapshot } from './snapshots.js';
|
|
2
|
+
import { type Task } from './types.js';
|
|
3
|
+
export interface TerminalCapture {
|
|
4
|
+
readonly snapshot: SdkTaskSnapshot;
|
|
5
|
+
readonly response?: CompleteResponse;
|
|
6
|
+
}
|
|
7
|
+
export declare function terminalInterval(task: Task, read: () => SdkTaskSnapshot, signal?: AbortSignal): Promise<TerminalCapture>;
|
|
8
|
+
export declare function responseReplay(task: Task, start: number, signal?: AbortSignal): AsyncIterable<CompleteResponse>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { AgentError } from '../errors.js';
|
|
2
|
+
import { detached } from './snapshots.js';
|
|
3
|
+
import { terminal } from './types.js';
|
|
4
|
+
export function terminalInterval(task, read, signal) {
|
|
5
|
+
if (signal?.aborted)
|
|
6
|
+
return Promise.reject(closed());
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
let settled = false;
|
|
9
|
+
const cleanup = () => { task.watchers.delete(change); signal?.removeEventListener('abort', abort); };
|
|
10
|
+
const change = () => {
|
|
11
|
+
if (settled || !terminal(task.state))
|
|
12
|
+
return;
|
|
13
|
+
settled = true;
|
|
14
|
+
cleanup();
|
|
15
|
+
const response = task.responses.at(-1);
|
|
16
|
+
resolve({ snapshot: read(), ...(response ? { response: detached(response) } : {}) });
|
|
17
|
+
};
|
|
18
|
+
const abort = () => { if (!settled) {
|
|
19
|
+
settled = true;
|
|
20
|
+
cleanup();
|
|
21
|
+
reject(closed());
|
|
22
|
+
} };
|
|
23
|
+
task.watchers.add(change);
|
|
24
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
25
|
+
change();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export function responseReplay(task, start, signal) {
|
|
29
|
+
return {
|
|
30
|
+
[Symbol.asyncIterator]() {
|
|
31
|
+
let position = start, boundary, ended = false;
|
|
32
|
+
const wakes = new Set();
|
|
33
|
+
const wakeAll = () => { for (const wake of wakes)
|
|
34
|
+
wake(); wakes.clear(); };
|
|
35
|
+
const detach = () => { task.watchers.delete(changed); signal?.removeEventListener('abort', aborted); };
|
|
36
|
+
const changed = () => {
|
|
37
|
+
if (terminal(task.state) && boundary === undefined) {
|
|
38
|
+
boundary = task.responses.length;
|
|
39
|
+
detach();
|
|
40
|
+
}
|
|
41
|
+
wakeAll();
|
|
42
|
+
};
|
|
43
|
+
const cleanup = () => { ended = true; detach(); wakeAll(); };
|
|
44
|
+
const aborted = () => cleanup();
|
|
45
|
+
if (!signal?.aborted) {
|
|
46
|
+
task.watchers.add(changed);
|
|
47
|
+
signal?.addEventListener('abort', aborted, { once: true });
|
|
48
|
+
changed();
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
async next() {
|
|
52
|
+
for (;;) {
|
|
53
|
+
if (signal?.aborted) {
|
|
54
|
+
cleanup();
|
|
55
|
+
throw closed();
|
|
56
|
+
}
|
|
57
|
+
if (ended)
|
|
58
|
+
return { done: true, value: undefined };
|
|
59
|
+
const limit = boundary ?? task.responses.length;
|
|
60
|
+
if (position < limit)
|
|
61
|
+
return { done: false, value: detached(task.responses[position++]) };
|
|
62
|
+
if (boundary !== undefined || ended) {
|
|
63
|
+
cleanup();
|
|
64
|
+
return { done: true, value: undefined };
|
|
65
|
+
}
|
|
66
|
+
await new Promise(resolve => { wakes.add(resolve); });
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
async return() { cleanup(); return { done: true, value: undefined }; },
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const closed = () => new AgentError('OBSERVATION_CLOSED', 'The observer disconnected.');
|
|
75
|
+
//# sourceMappingURL=sdk-observation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-observation.js","sourceRoot":"","sources":["../../src/runtime/sdk-observation.ts"],"names":[],"mappings":"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","sourcesContent":["import { AgentError } from '../errors.js';\nimport type { CompleteResponse, SdkTaskSnapshot } from './snapshots.js';\nimport { detached } from './snapshots.js';\nimport { terminal, type Task } from './types.js';\n\nexport interface TerminalCapture {\n readonly snapshot: SdkTaskSnapshot;\n readonly response?: CompleteResponse;\n}\n\nexport function terminalInterval(task: Task, read: () => SdkTaskSnapshot, signal?: AbortSignal): Promise<TerminalCapture> {\n if (signal?.aborted) return Promise.reject(closed());\n return new Promise((resolve,reject) => {\n let settled = false;\n const cleanup = () => { task.watchers.delete(change); signal?.removeEventListener('abort',abort); };\n const change = () => {\n if (settled || !terminal(task.state)) return;\n settled=true; cleanup();\n const response=task.responses.at(-1);\n resolve({snapshot:read(),...(response ? {response:detached(response)} : {})});\n };\n const abort = () => { if (!settled) { settled=true; cleanup(); reject(closed()); } };\n task.watchers.add(change);\n signal?.addEventListener('abort',abort,{once:true});\n change();\n });\n}\n\nexport function responseReplay(task: Task, start: number, signal?: AbortSignal): AsyncIterable<CompleteResponse> {\n return {\n [Symbol.asyncIterator](): AsyncIterator<CompleteResponse> {\n let position=start, boundary:number|undefined, ended=false;\n const wakes=new Set<()=>void>();\n const wakeAll=()=>{ for (const wake of wakes) wake(); wakes.clear(); };\n const detach=()=>{ task.watchers.delete(changed); signal?.removeEventListener('abort',aborted); };\n const changed=()=>{\n if (terminal(task.state) && boundary === undefined) { boundary=task.responses.length; detach(); }\n wakeAll();\n };\n const cleanup=()=>{ ended=true; detach(); wakeAll(); };\n const aborted=()=>cleanup();\n if (!signal?.aborted) {\n task.watchers.add(changed);\n signal?.addEventListener('abort',aborted,{once:true});\n changed();\n }\n return {\n async next() {\n for (;;) {\n if (signal?.aborted) { cleanup(); throw closed(); }\n if (ended) return {done:true,value:undefined};\n const limit=boundary ?? task.responses.length;\n if (position < limit) return {done:false,value:detached(task.responses[position++]!)};\n if (boundary !== undefined || ended) { cleanup(); return {done:true,value:undefined}; }\n await new Promise<void>(resolve=>{ wakes.add(resolve); });\n }\n },\n async return() { cleanup(); return {done:true,value:undefined}; },\n };\n },\n };\n}\n\nconst closed=()=>new AgentError('OBSERVATION_CLOSED','The observer disconnected.');\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HarnessKind } from '../sdk/types.js';
|
|
2
|
+
import type { DashboardWorkspace } from './dashboard.js';
|
|
3
|
+
import type { SdkApprovalRequest } from './task-data.js';
|
|
4
|
+
import type { SdkSessionSummary, SdkTaskSnapshot, SdkTaskSummary } from './snapshots.js';
|
|
5
|
+
import type { Session, Task } from './types.js';
|
|
6
|
+
export declare function sdkTaskSummary(task: Task, selectedHarness?: HarnessKind): SdkTaskSummary;
|
|
7
|
+
export declare function sdkTaskSnapshot(task: Task, summary: (task: Task) => SdkTaskSummary, lookup: (id: string) => Task, requests: readonly SdkApprovalRequest[]): SdkTaskSnapshot;
|
|
8
|
+
export declare function sdkSessionSummary(session: Session, workspace: DashboardWorkspace, summary: (task: Task) => SdkTaskSummary, lookup: (id: string) => Task): SdkSessionSummary;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function sdkTaskSummary(task, selectedHarness) {
|
|
2
|
+
return { taskId: task.id, sessionId: task.sessionId, state: task.state,
|
|
3
|
+
...(selectedHarness ? { selectedHarness } : {}), title: task.title, ordinal: task.ordinal, updatedAt: task.updatedAt,
|
|
4
|
+
...(task.dispatchedAt === undefined ? {} : { dispatchedAt: task.dispatchedAt }),
|
|
5
|
+
...(task.failedAt === undefined ? {} : { failedAt: task.failedAt }),
|
|
6
|
+
...(task.finishedAt === undefined ? {} : { finishedAt: task.finishedAt }) };
|
|
7
|
+
}
|
|
8
|
+
export function sdkTaskSnapshot(task, summary, lookup, requests) {
|
|
9
|
+
const response = task.responses.at(-1);
|
|
10
|
+
return { ...summary(task), revision: task.revision, ...(task.parentId ? { parentTaskId: task.parentId } : {}),
|
|
11
|
+
children: [...task.children].map(id => summary(lookup(id))), requests,
|
|
12
|
+
...(response ? { latestResponse: { responseId: response.responseId, text: response.text, state: response.state } } : {}),
|
|
13
|
+
...(task.error ? { error: task.error } : {}), stopUnconfirmed: task.stopUnconfirmed ?? false };
|
|
14
|
+
}
|
|
15
|
+
export function sdkSessionSummary(session, workspace, summary, lookup) {
|
|
16
|
+
return { sessionId: session.id, state: session.state, paused: session.paused, cwd: session.input.cwd, workspace,
|
|
17
|
+
...(session.active ? { active: summary(lookup(session.active)) } : {}) };
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=sdk-projection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-projection.js","sourceRoot":"","sources":["../../src/runtime/sdk-projection.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,cAAc,CAAC,IAAS,EAAC,eAA4B;IACnE,OAAO,EAAC,MAAM,EAAC,IAAI,CAAC,EAAE,EAAC,SAAS,EAAC,IAAI,CAAC,SAAS,EAAC,KAAK,EAAC,IAAI,CAAC,KAAK;QAC9D,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAC,eAAe,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAC,KAAK,EAAC,IAAI,CAAC,KAAK,EAAC,OAAO,EAAC,IAAI,CAAC,OAAO,EAAC,SAAS,EAAC,IAAI,CAAC,SAAS;QAC5G,GAAG,CAAC,IAAI,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,YAAY,EAAC,IAAI,CAAC,YAAY,EAAC,CAAC;QAC5E,GAAG,CAAC,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,QAAQ,EAAC,IAAI,CAAC,QAAQ,EAAC,CAAC;QAChE,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,UAAU,EAAC,IAAI,CAAC,UAAU,EAAC,CAAC,EAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAS,EAAC,OAAmC,EAC3E,MAAwB,EAAC,QAAsC;IAC/D,MAAM,QAAQ,GAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,OAAO,EAAC,GAAG,OAAO,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAC,IAAI,CAAC,QAAQ,EAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAC,YAAY,EAAC,IAAI,CAAC,QAAQ,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpG,QAAQ,EAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,EAAE,CAAA,EAAE,CAAA,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,QAAQ;QACjE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAC,cAAc,EAAC,EAAC,UAAU,EAAC,QAAQ,CAAC,UAAU,EAAC,IAAI,EAAC,QAAQ,CAAC,IAAI,EAAC,KAAK,EAAC,QAAQ,CAAC,KAAK,EAAC,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9G,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAC,KAAK,EAAC,IAAI,CAAC,KAAK,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAC,eAAe,EAAC,IAAI,CAAC,eAAe,IAAI,KAAK,EAAC,CAAC;AAC7F,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAC,SAA4B,EAC5E,OAAmC,EAAC,MAAwB;IAC5D,OAAO,EAAC,SAAS,EAAC,OAAO,CAAC,EAAE,EAAC,KAAK,EAAC,OAAO,CAAC,KAAK,EAAC,MAAM,EAAC,OAAO,CAAC,MAAM,EAAC,GAAG,EAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAC,SAAS;QACpG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAC,MAAM,EAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAC,CAAC;AACzE,CAAC","sourcesContent":["import type { HarnessKind } from '../sdk/types.js';\nimport type { DashboardWorkspace } from './dashboard.js';\nimport type { SdkApprovalRequest } from './task-data.js';\nimport type { SdkSessionSummary, SdkTaskSnapshot, SdkTaskSummary } from './snapshots.js';\nimport type { Session, Task } from './types.js';\n\nexport function sdkTaskSummary(task:Task,selectedHarness?:HarnessKind):SdkTaskSummary {\n return {taskId:task.id,sessionId:task.sessionId,state:task.state,\n ...(selectedHarness ? {selectedHarness} : {}),title:task.title,ordinal:task.ordinal,updatedAt:task.updatedAt,\n ...(task.dispatchedAt === undefined ? {} : {dispatchedAt:task.dispatchedAt}),\n ...(task.failedAt === undefined ? {} : {failedAt:task.failedAt}),\n ...(task.finishedAt === undefined ? {} : {finishedAt:task.finishedAt})};\n}\n\nexport function sdkTaskSnapshot(task:Task,summary:(task:Task)=>SdkTaskSummary,\n lookup:(id:string)=>Task,requests:readonly SdkApprovalRequest[]):SdkTaskSnapshot {\n const response=task.responses.at(-1);\n return {...summary(task),revision:task.revision,...(task.parentId ? {parentTaskId:task.parentId} : {}),\n children:[...task.children].map(id=>summary(lookup(id))),requests,\n ...(response ? {latestResponse:{responseId:response.responseId,text:response.text,state:response.state}} : {}),\n ...(task.error ? {error:task.error} : {}),stopUnconfirmed:task.stopUnconfirmed ?? false};\n}\n\nexport function sdkSessionSummary(session:Session,workspace:DashboardWorkspace,\n summary:(task:Task)=>SdkTaskSummary,lookup:(id:string)=>Task):SdkSessionSummary {\n return {sessionId:session.id,state:session.state,paused:session.paused,cwd:session.input.cwd,workspace,\n ...(session.active ? {active:summary(lookup(session.active))} : {})};\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Coordinator } from './coordinator.js';
|
|
2
|
+
type Request = Record<string, unknown>;
|
|
3
|
+
export interface SdkServiceOperations {
|
|
4
|
+
listAgents(data: Request): Promise<unknown>;
|
|
5
|
+
checkAgent(data: Request): Promise<void>;
|
|
6
|
+
createSession(data: Request): Promise<string>;
|
|
7
|
+
closeSession(id: string): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export declare function isSdkCommand(value: unknown): boolean;
|
|
10
|
+
export declare function dispatchSdkRequest(engine: Coordinator, data: Request, signal: AbortSignal, operations?: SdkServiceOperations): AsyncGenerator<unknown>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { AgentError } from '../errors.js';
|
|
2
|
+
export function isSdkCommand(value) {
|
|
3
|
+
return typeof value === 'string' && value.startsWith('sdk-');
|
|
4
|
+
}
|
|
5
|
+
export async function* dispatchSdkRequest(engine, data, signal, operations) {
|
|
6
|
+
const command = text(data.command, 'SDK command');
|
|
7
|
+
switch (command) {
|
|
8
|
+
case 'sdk-agent-list':
|
|
9
|
+
if (!operations)
|
|
10
|
+
throw new AgentError('COORDINATOR_UNAVAILABLE', 'Connected agent discovery is unavailable.');
|
|
11
|
+
yield await operations.listAgents(data);
|
|
12
|
+
return;
|
|
13
|
+
case 'sdk-agent-check':
|
|
14
|
+
if (!operations)
|
|
15
|
+
throw new AgentError('COORDINATOR_UNAVAILABLE', 'Connected agent checks are unavailable.');
|
|
16
|
+
await operations.checkAgent(data);
|
|
17
|
+
yield { version: 1, type: 'ack' };
|
|
18
|
+
return;
|
|
19
|
+
case 'sdk-session-create': {
|
|
20
|
+
if (!operations)
|
|
21
|
+
throw new AgentError('COORDINATOR_UNAVAILABLE', 'Connected session creation is unavailable.');
|
|
22
|
+
const sessionId = await operations.createSession(data);
|
|
23
|
+
yield { version: 1, type: 'session-created', sessionId };
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
case 'sdk-session-list':
|
|
27
|
+
yield await engine.listSessions(page(data));
|
|
28
|
+
return;
|
|
29
|
+
case 'sdk-session-get':
|
|
30
|
+
yield await engine.sdkSessionSnapshot(text(data.id, 'Session ID'));
|
|
31
|
+
return;
|
|
32
|
+
case 'sdk-session-prompt': {
|
|
33
|
+
const selected = delivery(data.delivery);
|
|
34
|
+
yield await engine.send(text(data.id, 'Session ID'), text(data.prompt, 'Prompt'), selected);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
case 'sdk-session-close':
|
|
38
|
+
if (operations)
|
|
39
|
+
await operations.closeSession(text(data.id, 'Session ID'));
|
|
40
|
+
else
|
|
41
|
+
await engine.closeSession(text(data.id, 'Session ID'));
|
|
42
|
+
yield { version: 1, type: 'ack' };
|
|
43
|
+
return;
|
|
44
|
+
case 'sdk-task-list':
|
|
45
|
+
yield engine.listTasks({ ...page(data), ...(data.sessionId === undefined ? {} : { sessionId: text(data.sessionId, 'Session ID') }) });
|
|
46
|
+
return;
|
|
47
|
+
case 'sdk-task-get':
|
|
48
|
+
case 'sdk-task-snapshot':
|
|
49
|
+
yield engine.sdkTaskSnapshot(text(data.id, 'Task ID'));
|
|
50
|
+
return;
|
|
51
|
+
case 'sdk-task-inspect':
|
|
52
|
+
yield engine.taskDetails(text(data.id, 'Task ID'));
|
|
53
|
+
return;
|
|
54
|
+
case 'sdk-task-result':
|
|
55
|
+
yield await engine.taskResult(text(data.id, 'Task ID'), signal);
|
|
56
|
+
return;
|
|
57
|
+
case 'sdk-task-watch':
|
|
58
|
+
for await (const snapshot of engine.watchTask(text(data.id, 'Task ID'), signal))
|
|
59
|
+
yield snapshot;
|
|
60
|
+
return;
|
|
61
|
+
case 'sdk-task-responses':
|
|
62
|
+
for await (const response of engine.taskResponses(text(data.id, 'Task ID'), optionalString(data.after, 'Response cursor'), signal))
|
|
63
|
+
yield response;
|
|
64
|
+
return;
|
|
65
|
+
case 'sdk-task-cancel': {
|
|
66
|
+
const id = text(data.id, 'Task ID');
|
|
67
|
+
await engine.cancel(id);
|
|
68
|
+
yield engine.sdkTaskSnapshot(id);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
case 'sdk-approval-respond':
|
|
72
|
+
engine.respondSdk(text(data.id, 'Approval ID'), data.response);
|
|
73
|
+
yield { version: 1, type: 'ack' };
|
|
74
|
+
return;
|
|
75
|
+
default: throw new AgentError('INVALID_ARGUMENT', 'Unknown SDK coordinator operation.');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function page(data) {
|
|
79
|
+
const result = {};
|
|
80
|
+
if (data.limit !== undefined) {
|
|
81
|
+
if (!Number.isSafeInteger(data.limit) || data.limit <= 0)
|
|
82
|
+
throw new AgentError('INVALID_ARGUMENT', 'Page limit must be a positive integer.');
|
|
83
|
+
result.limit = data.limit;
|
|
84
|
+
}
|
|
85
|
+
if (data.cursor !== undefined)
|
|
86
|
+
result.cursor = string(data.cursor, 'Page cursor');
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
function delivery(value) {
|
|
90
|
+
if (value === undefined)
|
|
91
|
+
return 'queue';
|
|
92
|
+
if (value !== 'queue' && value !== 'steer' && value !== 'interrupt')
|
|
93
|
+
throw new AgentError('INVALID_ARGUMENT', 'Unknown delivery mode.');
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
function text(value, label) {
|
|
97
|
+
if (typeof value !== 'string' || !value)
|
|
98
|
+
throw new AgentError('INVALID_ARGUMENT', `${label} must be a nonempty string.`);
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
function string(value, label) {
|
|
102
|
+
if (typeof value !== 'string')
|
|
103
|
+
throw new AgentError('INVALID_ARGUMENT', `${label} must be a string.`);
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
function optionalString(value, label) {
|
|
107
|
+
return value === undefined ? undefined : string(value, label);
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=sdk-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-service.js","sourceRoot":"","sources":["../../src/runtime/sdk-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { AgentError } from '../errors.js';\nimport type { Coordinator } from './coordinator.js';\nimport type { Delivery } from './types.js';\n\ntype Request=Record<string,unknown>;\nexport interface SdkServiceOperations {\n listAgents(data:Request):Promise<unknown>;\n checkAgent(data:Request):Promise<void>;\n createSession(data:Request):Promise<string>;\n closeSession(id:string):Promise<void>;\n}\n\nexport function isSdkCommand(value:unknown):boolean {\n return typeof value === 'string' && value.startsWith('sdk-');\n}\n\nexport async function* dispatchSdkRequest(\n engine:Coordinator,\n data:Request,\n signal:AbortSignal,\n operations?:SdkServiceOperations,\n):AsyncGenerator<unknown> {\n const command=text(data.command,'SDK command');\n switch(command) {\n case 'sdk-agent-list':\n if (!operations) throw new AgentError('COORDINATOR_UNAVAILABLE','Connected agent discovery is unavailable.');\n yield await operations.listAgents(data); return;\n case 'sdk-agent-check':\n if (!operations) throw new AgentError('COORDINATOR_UNAVAILABLE','Connected agent checks are unavailable.');\n await operations.checkAgent(data); yield {version:1,type:'ack'}; return;\n case 'sdk-session-create': {\n if (!operations) throw new AgentError('COORDINATOR_UNAVAILABLE','Connected session creation is unavailable.');\n const sessionId=await operations.createSession(data);\n yield {version:1,type:'session-created',sessionId}; return;\n }\n case 'sdk-session-list':\n yield await engine.listSessions(page(data)); return;\n case 'sdk-session-get':\n yield await engine.sdkSessionSnapshot(text(data.id,'Session ID')); return;\n case 'sdk-session-prompt': {\n const selected=delivery(data.delivery);\n yield await engine.send(text(data.id,'Session ID'),text(data.prompt,'Prompt'),selected); return;\n }\n case 'sdk-session-close':\n if (operations) await operations.closeSession(text(data.id,'Session ID'));\n else await engine.closeSession(text(data.id,'Session ID'));\n yield {version:1,type:'ack'}; return;\n case 'sdk-task-list':\n yield engine.listTasks({...page(data),...(data.sessionId === undefined ? {} : {sessionId:text(data.sessionId,'Session ID')})}); return;\n case 'sdk-task-get':\n case 'sdk-task-snapshot':\n yield engine.sdkTaskSnapshot(text(data.id,'Task ID')); return;\n case 'sdk-task-inspect':\n yield engine.taskDetails(text(data.id,'Task ID')); return;\n case 'sdk-task-result':\n yield await engine.taskResult(text(data.id,'Task ID'),signal); return;\n case 'sdk-task-watch':\n for await (const snapshot of engine.watchTask(text(data.id,'Task ID'),signal)) yield snapshot;\n return;\n case 'sdk-task-responses':\n for await (const response of engine.taskResponses(text(data.id,'Task ID'),optionalString(data.after,'Response cursor'),signal)) yield response;\n return;\n case 'sdk-task-cancel': {\n const id=text(data.id,'Task ID');\n await engine.cancel(id); yield engine.sdkTaskSnapshot(id); return;\n }\n case 'sdk-approval-respond':\n engine.respondSdk(text(data.id,'Approval ID'),data.response as never);\n yield {version:1,type:'ack'}; return;\n default: throw new AgentError('INVALID_ARGUMENT','Unknown SDK coordinator operation.');\n }\n}\n\nfunction page(data:Request):{limit?:number;cursor?:string} {\n const result:{limit?:number;cursor?:string}={};\n if (data.limit !== undefined) {\n if (!Number.isSafeInteger(data.limit) || (data.limit as number) <= 0)\n throw new AgentError('INVALID_ARGUMENT','Page limit must be a positive integer.');\n result.limit=data.limit as number;\n }\n if (data.cursor !== undefined) result.cursor=string(data.cursor,'Page cursor');\n return result;\n}\n\nfunction delivery(value:unknown):Delivery {\n if (value === undefined) return 'queue';\n if (value !== 'queue' && value !== 'steer' && value !== 'interrupt')\n throw new AgentError('INVALID_ARGUMENT','Unknown delivery mode.');\n return value;\n}\n\nfunction text(value:unknown,label:string):string {\n if (typeof value !== 'string' || !value) throw new AgentError('INVALID_ARGUMENT',`${label} must be a nonempty string.`);\n return value;\n}\nfunction string(value:unknown,label:string):string {\n if (typeof value !== 'string') throw new AgentError('INVALID_ARGUMENT',`${label} must be a string.`);\n return value;\n}\nfunction optionalString(value:unknown,label:string):string|undefined {\n return value === undefined ? undefined : string(value,label);\n}\n"]}
|
|
@@ -2,4 +2,7 @@ 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
|
|
5
|
+
export interface NativeSelectionDiagnosticContext {
|
|
6
|
+
readonly origin: 'cli' | 'sdk';
|
|
7
|
+
}
|
|
8
|
+
export declare function selectNative(agent: ExecutionDefinition, cwd: string, env: NodeJS.ProcessEnv, settings: AccessConfig, open?: (options: AdapterOptions) => Promise<NativeSession>, selected?: (kind: HarnessKind) => void, onApproval?: ApprovalHandler, diagnosticContext?: NativeSelectionDiagnosticContext): Promise<NativeSession>;
|
|
@@ -1,22 +1,16 @@
|
|
|
1
|
-
import { openCodex } from '../adapters/codex.js';
|
|
2
|
-
import { openClaude } from '../adapters/claude.js';
|
|
3
|
-
import { openFx } from '../adapters/fx.js';
|
|
4
|
-
import { openOpencode } from '../adapters/opencode.js';
|
|
5
|
-
import { openCopilot } from '../adapters/copilot.js';
|
|
6
|
-
import { openCursor } from '../adapters/cursor.js';
|
|
7
1
|
import { resolveAccess } from '../config/access.js';
|
|
8
2
|
import { accessAttemptContext } from '../config/access-provenance.js';
|
|
9
3
|
import { AgentError } from '../errors.js';
|
|
10
4
|
import { withAccessContext, withSessionAccessContext } from './access-errors.js';
|
|
11
5
|
const openers = {
|
|
12
|
-
codex: openCodex,
|
|
13
|
-
claudeCode: openClaude,
|
|
14
|
-
fx: openFx,
|
|
15
|
-
opencode: openOpencode,
|
|
16
|
-
copilot: openCopilot,
|
|
17
|
-
cursor: openCursor,
|
|
6
|
+
codex: async (options) => (await import('../adapters/codex.js')).openCodex(options),
|
|
7
|
+
claudeCode: async (options) => (await import('../adapters/claude.js')).openClaude(options),
|
|
8
|
+
fx: async (options) => (await import('../adapters/fx.js')).openFx(options),
|
|
9
|
+
opencode: async (options) => (await import('../adapters/opencode.js')).openOpencode(options),
|
|
10
|
+
copilot: async (options) => (await import('../adapters/copilot.js')).openCopilot(options),
|
|
11
|
+
cursor: async (options) => (await import('../adapters/cursor.js')).openCursor(options),
|
|
18
12
|
};
|
|
19
|
-
export async function selectNative(agent, cwd, env, settings, open = (options) => openers[options.harness.kind](options), selected = () => { }, onApproval) {
|
|
13
|
+
export async function selectNative(agent, cwd, env, settings, open = (options) => openers[options.harness.kind](options), selected = () => { }, onApproval, diagnosticContext = { origin: 'cli' }) {
|
|
20
14
|
const alternatives = Array.isArray(agent.harness) ? agent.harness : [agent.harness];
|
|
21
15
|
const unavailable = [];
|
|
22
16
|
for (const harness of alternatives) {
|
|
@@ -25,12 +19,15 @@ export async function selectNative(agent, cwd, env, settings, open = (options) =
|
|
|
25
19
|
const connections = configuredConnections ??
|
|
26
20
|
(explicitAccessRequired ? [] : [{ type: 'subscription' }]);
|
|
27
21
|
if (configuredConnections?.length === 0) {
|
|
28
|
-
unavailable.push(
|
|
22
|
+
unavailable.push(diagnosticContext.origin === 'sdk'
|
|
23
|
+
? `${harness.kind}: Access is disabled by the empty connection list in RunnerOptions.access.`
|
|
24
|
+
: `${harness.kind}: Access is disabled in the main checkout's .subharness/agents.local.json.`);
|
|
29
25
|
}
|
|
30
26
|
else if (!connections.length && explicitAccessRequired) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
:
|
|
27
|
+
const setting = diagnosticContext.origin === 'sdk' ? 'RunnerOptions.access' : 'personal access settings';
|
|
28
|
+
const guidance = harness.kind === 'cursor' ? 'Cursor subscription or API-key'
|
|
29
|
+
: 'native login, API-key, or Gateway';
|
|
30
|
+
unavailable.push(`${harness.kind}: Enable an explicit ${guidance} connection in ${setting}.`);
|
|
34
31
|
}
|
|
35
32
|
for (const [index, connection] of connections.entries()) {
|
|
36
33
|
const context = accessAttemptContext(settings, harness.kind, connection, index, connections.length);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select-native.js","sourceRoot":"","sources":["../../src/runtime/select-native.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,
|
|
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 { resolveAccess, type AccessConfig } from '../config/access.js';\nimport { accessAttemptContext } from '../config/access-provenance.js';\nimport { AgentError } from '../errors.js';\nimport type { ApprovalHandler } from '../approvals/types.js';\nimport { withAccessContext, withSessionAccessContext } from './access-errors.js';\n\nconst openers = {\n codex: async (options: AdapterOptions) => (await import('../adapters/codex.js')).openCodex(options),\n claudeCode: async (options: AdapterOptions) => (await import('../adapters/claude.js')).openClaude(options),\n fx: async (options: AdapterOptions) => (await import('../adapters/fx.js')).openFx(options),\n opencode: async (options: AdapterOptions) => (await import('../adapters/opencode.js')).openOpencode(options),\n copilot: async (options: AdapterOptions) => (await import('../adapters/copilot.js')).openCopilot(options),\n cursor: async (options: AdapterOptions) => (await import('../adapters/cursor.js')).openCursor(options),\n} satisfies Record<HarnessKind, (options: AdapterOptions) => Promise<NativeSession>>;\n\nexport interface NativeSelectionDiagnosticContext {\n readonly origin: 'cli' | 'sdk';\n}\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 diagnosticContext: NativeSelectionDiagnosticContext = {origin: 'cli'},\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 explicitAccessRequired = ['fx','opencode','copilot','cursor'].includes(harness.kind);\n const connections = configuredConnections ??\n (explicitAccessRequired ? [] : [{type:'subscription' as const}]);\n if (configuredConnections?.length === 0) {\n unavailable.push(diagnosticContext.origin === 'sdk'\n ? `${harness.kind}: Access is disabled by the empty connection list in RunnerOptions.access.`\n : `${harness.kind}: Access is disabled in the main checkout's .subharness/agents.local.json.`);\n } else if (!connections.length && explicitAccessRequired) {\n const setting = diagnosticContext.origin === 'sdk' ? 'RunnerOptions.access' : 'personal access settings';\n const guidance = harness.kind === 'cursor' ? 'Cursor subscription or API-key'\n : 'native login, API-key, or Gateway';\n unavailable.push(`${harness.kind}: Enable an explicit ${guidance} connection in ${setting}.`);\n }\n for (const [index, connection] of connections.entries()) {\n const context = accessAttemptContext(settings, harness.kind, connection, index, connections.length);\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 withSessionAccessContext(native, context);\n } catch (error) {\n if (!(error instanceof AgentError)) throw error;\n if (!['HARNESS_UNAVAILABLE','ACCESS_UNAVAILABLE'].includes(error.code)) throw withAccessContext(error, context);\n unavailable.push(`${harness.kind}: ${withAccessContext(error, context).message}`);\n }\n }\n }\n throw new AgentError('ACCESS_UNAVAILABLE', unavailable.length ? unavailable.join('\\n') : 'No declared harness has enabled access.');\n}\n"]}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { Coordinator } from './coordinator.js';
|
|
2
|
-
|
|
2
|
+
import { captureExecution, captureReference, checkCapturedExecution, type CapturedExecution } from './capture.js';
|
|
3
|
+
export interface ServiceCaptureOperations {
|
|
4
|
+
captureExecution: typeof captureExecution;
|
|
5
|
+
captureReference: typeof captureReference;
|
|
6
|
+
checkCapturedExecution: typeof checkCapturedExecution;
|
|
7
|
+
}
|
|
8
|
+
export interface ServiceOptions {
|
|
9
|
+
readonly initialCaptures?: ReadonlyMap<string, CapturedExecution>;
|
|
10
|
+
readonly capture?: Partial<ServiceCaptureOperations>;
|
|
11
|
+
}
|
|
12
|
+
export declare function startService(engine: Coordinator, token: string, instanceId: string, state?: string, options?: ServiceOptions): Promise<{
|
|
3
13
|
url: string;
|
|
4
14
|
close(): Promise<void>;
|
|
5
15
|
}>;
|
package/dist/runtime/service.js
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
import { createServer } from 'node:http';
|
|
2
|
+
import { stat } from 'node:fs/promises';
|
|
3
|
+
import { isAbsolute } from 'node:path';
|
|
2
4
|
import { AgentError, errorInfo } from '../errors.js';
|
|
3
|
-
|
|
5
|
+
import { dispatchSdkRequest, isSdkCommand } from './sdk-service.js';
|
|
6
|
+
import { captureExecution, captureReference, checkCapturedExecution } from './capture.js';
|
|
7
|
+
import { checkAgent, listAgents } from './catalog.js';
|
|
8
|
+
import { resolveStateDirectory } from './state.js';
|
|
9
|
+
export async function startService(engine, token, instanceId, state, options = {}) {
|
|
10
|
+
const directory = resolveStateDirectory(state);
|
|
11
|
+
const captures = new Map(options.initialCaptures);
|
|
12
|
+
const captureOperations = { captureExecution, captureReference, checkCapturedExecution, ...options.capture };
|
|
13
|
+
const sdkOperations = {
|
|
14
|
+
listAgents: async (data) => listAgents(await catalogInput(data, directory)),
|
|
15
|
+
checkAgent: async (data) => {
|
|
16
|
+
const input = await catalogInput(data, directory);
|
|
17
|
+
await checkAgent(connectedAgent(data.agent), input);
|
|
18
|
+
},
|
|
19
|
+
createSession: async (data) => {
|
|
20
|
+
const input = await catalogInput(data, directory);
|
|
21
|
+
const captured = await captureOperations.captureExecution(connectedAgent(data.agent), input);
|
|
22
|
+
try {
|
|
23
|
+
const id = engine.createSession({ reference: captured.reference, cwd: input.cwd, env: input.env }, captured.prepared);
|
|
24
|
+
captures.set(id, captured);
|
|
25
|
+
return id;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return releaseRejected(captured, error);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
closeSession: async (id) => { await engine.closeSession(id); captures.delete(id); },
|
|
32
|
+
};
|
|
4
33
|
const server = createServer(async (request, response) => {
|
|
5
34
|
if (request.headers.authorization !== `Bearer ${token}`) {
|
|
6
35
|
response.writeHead(401).end();
|
|
@@ -8,13 +37,23 @@ export async function startService(engine, token, instanceId) {
|
|
|
8
37
|
}
|
|
9
38
|
if (request.method === 'GET' && request.url === '/health') {
|
|
10
39
|
response.setHeader('content-type', 'application/json');
|
|
11
|
-
response.end(JSON.stringify({
|
|
40
|
+
response.end(JSON.stringify({
|
|
41
|
+
version: 1,
|
|
42
|
+
type: 'coordinator-health',
|
|
43
|
+
instanceId,
|
|
44
|
+
protocolVersion: 1,
|
|
45
|
+
capabilities: ['sdk-v1', 'cli-v1'],
|
|
46
|
+
}));
|
|
12
47
|
return;
|
|
13
48
|
}
|
|
14
49
|
if (request.method !== 'POST' || request.url !== '/') {
|
|
15
50
|
response.writeHead(404).end();
|
|
16
51
|
return;
|
|
17
52
|
}
|
|
53
|
+
if (request.headers['x-subharness-instance'] !== instanceId) {
|
|
54
|
+
response.writeHead(409).end();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
18
57
|
response.setHeader('content-type', 'application/x-ndjson');
|
|
19
58
|
const observer = new AbortController();
|
|
20
59
|
// Keep long, quiet observations alive without exposing progress records to callers.
|
|
@@ -39,6 +78,13 @@ export async function startService(engine, token, instanceId) {
|
|
|
39
78
|
}
|
|
40
79
|
if (!data || typeof data !== 'object' || Array.isArray(data))
|
|
41
80
|
throw new AgentError('INVALID_ARGUMENT', 'Coordinator input must be a JSON object.');
|
|
81
|
+
if (isSdkCommand(data.command)) {
|
|
82
|
+
for await (const record of dispatchSdkRequest(engine, data, observer.signal, sdkOperations))
|
|
83
|
+
write(record);
|
|
84
|
+
clearInterval(heartbeat);
|
|
85
|
+
response.end();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
42
88
|
let receipt;
|
|
43
89
|
switch (data.command) {
|
|
44
90
|
case 'dashboard':
|
|
@@ -59,6 +105,59 @@ export async function startService(engine, token, instanceId) {
|
|
|
59
105
|
case 'parent':
|
|
60
106
|
write(engine.parentContext(data.parentToken));
|
|
61
107
|
break;
|
|
108
|
+
case 'parent-catalog': {
|
|
109
|
+
const { captured } = bindParent(engine, captures, data.parentToken);
|
|
110
|
+
write({ version: 1, type: 'agents', agents: captured.children.map(child => ({
|
|
111
|
+
id: `subagent:${child.name}`, name: child.name, description: child.description, scope: 'subagent',
|
|
112
|
+
})) });
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case 'parent-check': {
|
|
116
|
+
const binding = bindParent(engine, captures, data.parentToken);
|
|
117
|
+
const input = await catalogInput(data, directory);
|
|
118
|
+
const captured = binding.captured;
|
|
119
|
+
const child = await captured.child(childName(data.agent));
|
|
120
|
+
try {
|
|
121
|
+
validateParentBinding(engine, binding);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
await releaseRejected(child, error);
|
|
125
|
+
}
|
|
126
|
+
await captureOperations.checkCapturedExecution(child, input);
|
|
127
|
+
write({ version: 1, type: 'ack' });
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case 'parent-run': {
|
|
131
|
+
const binding = bindParent(engine, captures, data.parentToken);
|
|
132
|
+
const input = await catalogInput(data, directory);
|
|
133
|
+
const captured = binding.captured;
|
|
134
|
+
const child = await captured.child(childName(data.agent));
|
|
135
|
+
try {
|
|
136
|
+
validateParentBinding(engine, binding);
|
|
137
|
+
receipt = engine.run({ reference: child.reference, cwd: input.cwd, env: input.env,
|
|
138
|
+
prompt: data.prompt, parentToken: binding.token }, child.prepared);
|
|
139
|
+
captures.set(receipt.sessionId, child);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
await releaseRejected(child, error);
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
case 'cli-run': {
|
|
147
|
+
const input = await catalogInput(data, directory);
|
|
148
|
+
const captured = data.reference
|
|
149
|
+
? await captureOperations.captureReference(data.reference, input)
|
|
150
|
+
: await captureOperations.captureExecution(connectedAgent(data.agent), input);
|
|
151
|
+
try {
|
|
152
|
+
receipt = engine.run({ reference: captured.reference, cwd: input.cwd, env: input.env,
|
|
153
|
+
prompt: data.prompt, parentToken: data.parentToken }, captured.prepared);
|
|
154
|
+
captures.set(receipt.sessionId, captured);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
await releaseRejected(captured, error);
|
|
158
|
+
}
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
62
161
|
case 'run':
|
|
63
162
|
receipt = engine.run(data.input);
|
|
64
163
|
break;
|
|
@@ -114,7 +213,83 @@ export async function startService(engine, token, instanceId) {
|
|
|
114
213
|
});
|
|
115
214
|
return {
|
|
116
215
|
url: `http://127.0.0.1:${server.address().port}/`,
|
|
117
|
-
async close() {
|
|
216
|
+
async close() {
|
|
217
|
+
let failure;
|
|
218
|
+
try {
|
|
219
|
+
await engine.close();
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
failure = error;
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
server.closeAllConnections();
|
|
226
|
+
await new Promise(resolve => server.close(() => resolve()));
|
|
227
|
+
}
|
|
228
|
+
if (failure)
|
|
229
|
+
throw failure;
|
|
230
|
+
},
|
|
118
231
|
};
|
|
119
232
|
}
|
|
233
|
+
async function catalogInput(data, directory) {
|
|
234
|
+
const cwd = data.cwd;
|
|
235
|
+
if (typeof cwd !== 'string' || !isAbsolute(cwd))
|
|
236
|
+
throw new AgentError('INVALID_ARGUMENT', 'Execution cwd must be an existing absolute directory.');
|
|
237
|
+
try {
|
|
238
|
+
if (!(await stat(cwd)).isDirectory())
|
|
239
|
+
throw new Error();
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
throw new AgentError('INVALID_ARGUMENT', 'Execution cwd must be an existing absolute directory.');
|
|
243
|
+
}
|
|
244
|
+
const source = data.env;
|
|
245
|
+
if (!source || typeof source !== 'object' || Array.isArray(source))
|
|
246
|
+
throw new AgentError('INVALID_ARGUMENT', 'Connected environment must be an object.');
|
|
247
|
+
const env = {};
|
|
248
|
+
for (const key of Reflect.ownKeys(source)) {
|
|
249
|
+
if (typeof key !== 'string')
|
|
250
|
+
throw new AgentError('INVALID_ARGUMENT', 'Connected environment keys must be strings.');
|
|
251
|
+
const value = source[key];
|
|
252
|
+
if (value !== undefined && typeof value !== 'string')
|
|
253
|
+
throw new AgentError('INVALID_ARGUMENT', 'Connected environment values must be strings or undefined.');
|
|
254
|
+
if (typeof value === 'string')
|
|
255
|
+
env[key] = value;
|
|
256
|
+
}
|
|
257
|
+
delete env.AGENT_PARENT_TOKEN;
|
|
258
|
+
delete env.AGENT_CLI_PATH;
|
|
259
|
+
env.AGENT_STATE_DIR = directory;
|
|
260
|
+
return { cwd, env };
|
|
261
|
+
}
|
|
262
|
+
function connectedAgent(value) {
|
|
263
|
+
if (typeof value === 'string' || (value !== null && typeof value === 'object' && !Array.isArray(value)))
|
|
264
|
+
return value;
|
|
265
|
+
throw new AgentError('INVALID_ARGUMENT', 'Connected agent must be an ID or harness configuration.');
|
|
266
|
+
}
|
|
267
|
+
function bindParent(engine, captures, token) {
|
|
268
|
+
if (typeof token !== 'string' || !token)
|
|
269
|
+
throw new AgentError('INVALID_PARENT', 'The parent context is no longer active.');
|
|
270
|
+
const context = engine.parentContext(token);
|
|
271
|
+
const captured = captures.get(context.sessionId);
|
|
272
|
+
if (!captured)
|
|
273
|
+
throw new AgentError('INVALID_PARENT', 'The active parent does not own a captured definition graph.');
|
|
274
|
+
return { token, taskId: context.taskId, sessionId: context.sessionId, captured };
|
|
275
|
+
}
|
|
276
|
+
function validateParentBinding(engine, binding) {
|
|
277
|
+
const current = engine.parentContext(binding.token);
|
|
278
|
+
if (current.taskId !== binding.taskId || current.sessionId !== binding.sessionId)
|
|
279
|
+
throw new AgentError('INVALID_PARENT', 'The parent context is no longer active.');
|
|
280
|
+
}
|
|
281
|
+
function childName(value) {
|
|
282
|
+
if (typeof value !== 'string' || !value.startsWith('subagent:') || !value.slice(9))
|
|
283
|
+
throw new AgentError('INVALID_PARENT', 'This subagent is not available in the active parent context.');
|
|
284
|
+
return value.slice(9);
|
|
285
|
+
}
|
|
286
|
+
async function releaseRejected(captured, admissionError) {
|
|
287
|
+
try {
|
|
288
|
+
await captured.prepared.close();
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
throw new AgentError('HARNESS_FAILED', 'Captured execution cleanup failed after admission was rejected.');
|
|
292
|
+
}
|
|
293
|
+
throw admissionError;
|
|
294
|
+
}
|
|
120
295
|
//# sourceMappingURL=service.js.map
|