subharness 0.0.4 → 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 +95 -9
- package/dist/adapters/claude-process.d.ts +3 -0
- package/dist/adapters/claude-process.js +19 -2
- package/dist/adapters/claude-process.js.map +1 -1
- package/dist/adapters/claude-result.d.ts +2 -0
- package/dist/adapters/claude-result.js +22 -0
- package/dist/adapters/claude-result.js.map +1 -0
- 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 +164 -75
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/codex.js +4 -1
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/copilot-permissions.d.ts +26 -0
- package/dist/adapters/copilot-permissions.js +121 -0
- package/dist/adapters/copilot-permissions.js.map +1 -0
- package/dist/adapters/copilot-tools.d.ts +12 -0
- package/dist/adapters/copilot-tools.js +61 -0
- package/dist/adapters/copilot-tools.js.map +1 -0
- package/dist/adapters/copilot.d.ts +108 -0
- package/dist/adapters/copilot.js +819 -0
- package/dist/adapters/copilot.js.map +1 -0
- package/dist/adapters/cursor-cli-approvals.d.ts +2 -0
- package/dist/adapters/cursor-cli-approvals.js +83 -0
- package/dist/adapters/cursor-cli-approvals.js.map +1 -0
- package/dist/adapters/cursor-cli-model.d.ts +4 -0
- package/dist/adapters/cursor-cli-model.js +64 -0
- package/dist/adapters/cursor-cli-model.js.map +1 -0
- package/dist/adapters/cursor-cli-session.d.ts +32 -0
- package/dist/adapters/cursor-cli-session.js +316 -0
- package/dist/adapters/cursor-cli-session.js.map +1 -0
- package/dist/adapters/cursor-cli-tools.d.ts +20 -0
- package/dist/adapters/cursor-cli-tools.js +181 -0
- package/dist/adapters/cursor-cli-tools.js.map +1 -0
- package/dist/adapters/cursor-cli.d.ts +2 -0
- package/dist/adapters/cursor-cli.js +306 -0
- package/dist/adapters/cursor-cli.js.map +1 -0
- package/dist/adapters/cursor-model.d.ts +7 -0
- package/dist/adapters/cursor-model.js +67 -0
- package/dist/adapters/cursor-model.js.map +1 -0
- package/dist/adapters/cursor-rpc.d.ts +42 -0
- package/dist/adapters/cursor-rpc.js +271 -0
- package/dist/adapters/cursor-rpc.js.map +1 -0
- package/dist/adapters/cursor-session.d.ts +4 -0
- package/dist/adapters/cursor-session.js +327 -0
- package/dist/adapters/cursor-session.js.map +1 -0
- package/dist/adapters/cursor-startup.d.ts +16 -0
- package/dist/adapters/cursor-startup.js +74 -0
- package/dist/adapters/cursor-startup.js.map +1 -0
- package/dist/adapters/cursor-tools.d.ts +11 -0
- package/dist/adapters/cursor-tools.js +49 -0
- package/dist/adapters/cursor-tools.js.map +1 -0
- package/dist/adapters/cursor.d.ts +6 -0
- package/dist/adapters/cursor.js +17 -0
- package/dist/adapters/cursor.js.map +1 -0
- package/dist/adapters/fx-auth.d.ts +12 -2
- package/dist/adapters/fx-auth.js +51 -61
- package/dist/adapters/fx-auth.js.map +1 -1
- package/dist/adapters/fx-profile.d.ts +8 -0
- package/dist/adapters/fx-profile.js +101 -0
- package/dist/adapters/fx-profile.js.map +1 -0
- package/dist/adapters/fx-rpc.d.ts +2 -0
- package/dist/adapters/fx-rpc.js +30 -4
- package/dist/adapters/fx-rpc.js.map +1 -1
- package/dist/adapters/fx-status.d.ts +2 -0
- package/dist/adapters/fx-status.js +96 -0
- package/dist/adapters/fx-status.js.map +1 -0
- package/dist/adapters/fx.js +28 -12
- package/dist/adapters/fx.js.map +1 -1
- package/dist/adapters/opencode-access.d.ts +13 -0
- package/dist/adapters/opencode-access.js +76 -0
- package/dist/adapters/opencode-access.js.map +1 -0
- package/dist/adapters/opencode-config.d.ts +11 -0
- package/dist/adapters/opencode-config.js +238 -0
- package/dist/adapters/opencode-config.js.map +1 -0
- package/dist/adapters/opencode-http.d.ts +28 -0
- package/dist/adapters/opencode-http.js +297 -0
- package/dist/adapters/opencode-http.js.map +1 -0
- package/dist/adapters/opencode-tools.d.ts +19 -0
- package/dist/adapters/opencode-tools.js +127 -0
- package/dist/adapters/opencode-tools.js.map +1 -0
- package/dist/adapters/opencode.d.ts +2 -0
- package/dist/adapters/opencode.js +569 -0
- package/dist/adapters/opencode.js.map +1 -0
- package/dist/adapters/rpc.d.ts +3 -0
- package/dist/adapters/rpc.js +45 -4
- package/dist/adapters/rpc.js.map +1 -1
- package/dist/adapters/types.d.ts +5 -0
- package/dist/adapters/types.js.map +1 -1
- package/dist/approvals/types.d.ts +1 -1
- package/dist/approvals/types.js.map +1 -1
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.js +4 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/catalog-worker.js.map +1 -1
- package/dist/cli/dashboard-client.js +20 -31
- package/dist/cli/dashboard-client.js.map +1 -1
- package/dist/cli/dashboard-controller.d.ts +7 -0
- package/dist/cli/dashboard-controller.js +27 -0
- package/dist/cli/dashboard-controller.js.map +1 -0
- package/dist/cli/dashboard-detail-view.d.ts +1 -1
- package/dist/cli/dashboard-detail-view.js +3 -4
- package/dist/cli/dashboard-detail-view.js.map +1 -1
- package/dist/cli/dashboard-history-view.js.map +1 -1
- package/dist/cli/dashboard-input.d.ts +1 -1
- package/dist/cli/dashboard-input.js +11 -1
- package/dist/cli/dashboard-input.js.map +1 -1
- package/dist/cli/dashboard-layout.js +3 -3
- package/dist/cli/dashboard-layout.js.map +1 -1
- package/dist/cli/dashboard-renderer.js.map +1 -1
- package/dist/cli/dashboard-style.js +2 -2
- package/dist/cli/dashboard-style.js.map +1 -1
- package/dist/cli/dashboard.d.ts +2 -2
- package/dist/cli/dashboard.js +7 -32
- package/dist/cli/dashboard.js.map +1 -1
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +30 -12
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/main.js +55 -43
- package/dist/cli/main.js.map +1 -1
- package/dist/config/access-provenance.d.ts +10 -0
- package/dist/config/access-provenance.js +31 -0
- package/dist/config/access-provenance.js.map +1 -0
- package/dist/config/access.d.ts +12 -2
- package/dist/config/access.js +125 -27
- package/dist/config/access.js.map +1 -1
- package/dist/config/loader.js +1 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/oidc.js +4 -2
- package/dist/config/oidc.js.map +1 -1
- package/dist/config/project.js +2 -1
- package/dist/config/project.js.map +1 -1
- package/dist/config/resolve-access.js +24 -10
- package/dist/config/resolve-access.js.map +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/process-diagnostics.d.ts +2 -0
- package/dist/process-diagnostics.js +11 -0
- package/dist/process-diagnostics.js.map +1 -0
- package/dist/runtime/access-errors.d.ts +4 -0
- package/dist/runtime/access-errors.js +25 -0
- package/dist/runtime/access-errors.js.map +1 -0
- package/dist/runtime/approval-registry.d.ts +9 -0
- package/dist/runtime/approval-registry.js +144 -5
- 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 +74 -47
- 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-sanitize.d.ts +2 -0
- package/dist/runtime/dashboard-sanitize.js +7 -0
- package/dist/runtime/dashboard-sanitize.js.map +1 -0
- package/dist/runtime/dashboard-workspace.js +5 -1
- package/dist/runtime/dashboard-workspace.js.map +1 -1
- package/dist/runtime/dashboard.d.ts +1 -1
- package/dist/runtime/dashboard.js +2 -3
- package/dist/runtime/dashboard.js.map +1 -1
- package/dist/runtime/definition.d.ts +6 -1
- package/dist/runtime/definition.js +42 -2
- 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 +28 -13
- 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 +40 -13
- 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/definitions.d.ts +4 -1
- package/dist/sdk/definitions.js +16 -2
- package/dist/sdk/definitions.js.map +1 -1
- 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/permission-validation.js +10 -1
- package/dist/sdk/permission-validation.js.map +1 -1
- 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/dist/sdk/types.d.ts +29 -1
- package/dist/sdk/types.js.map +1 -1
- package/package.json +6 -3
- package/sdk/access-config.md +60 -4
- package/sdk/adapter-contract.md +13 -3
- package/sdk/additional-harnesses.md +43 -0
- package/sdk/agent-skill.md +2 -2
- package/sdk/agent.md +6 -4
- package/sdk/approvals.md +5 -1
- package/sdk/authentication.md +1 -1
- package/sdk/cli/dashboard-design.md +6 -0
- package/sdk/cli/index.md +9 -6
- package/sdk/cli/output.md +3 -1
- package/sdk/completion-notifications.md +2 -0
- package/sdk/config.md +1 -1
- package/sdk/copilot.md +53 -0
- package/sdk/cursor.md +68 -0
- package/sdk/diagnostics.md +29 -0
- package/sdk/distribution.md +5 -3
- package/sdk/evals.md +1 -1
- package/sdk/examples/chat-tool.ts +126 -0
- package/sdk/execution.md +743 -0
- package/sdk/fx.md +50 -5
- package/sdk/harnesses.md +13 -13
- package/sdk/index.md +45 -20
- package/sdk/message-delivery.md +2 -0
- package/sdk/opencode.md +57 -0
- package/sdk/permissions.md +6 -2
- package/sdk/plugins/sub-agents.md +4 -2
- package/sdk/project-team.md +25 -1
- package/sdk/sessions.md +2 -0
- package/sdk/tools.md +4 -2
- package/sdk/v1-runtime.md +8 -6
- package/dist/cli/catalog.d.ts +0 -26
- package/dist/cli/catalog.js +0 -41
- package/dist/cli/catalog.js.map +0 -1
|
@@ -5,32 +5,51 @@ import { terminal } from './types.js';
|
|
|
5
5
|
import { dashboardHarness, dashboardTitle } from './dashboard.js';
|
|
6
6
|
import { resolveDashboardWorkspace } from './dashboard-workspace.js';
|
|
7
7
|
import { ApprovalRegistry } from './approval-registry.js';
|
|
8
|
+
import { createSnapshotWatch, detached } from './snapshots.js';
|
|
9
|
+
import { paginate } from './pagination.js';
|
|
10
|
+
import { responseReplay, terminalInterval } from './sdk-observation.js';
|
|
11
|
+
import { sdkSessionSummary as projectSessionSummary, sdkTaskSnapshot as projectTaskSnapshot, sdkTaskSummary as projectTaskSummary } from './sdk-projection.js';
|
|
8
12
|
export class Coordinator {
|
|
9
13
|
factory;
|
|
10
14
|
now;
|
|
15
|
+
instanceId;
|
|
11
16
|
sessions = new Map();
|
|
12
17
|
tasks = new Map();
|
|
13
18
|
dashboardHistory;
|
|
14
19
|
closed = false;
|
|
20
|
+
closeComplete = false;
|
|
21
|
+
closePromise;
|
|
22
|
+
membership = 0;
|
|
15
23
|
approvals = new ApprovalRegistry(id => this.approvalChanged(this.task(id)));
|
|
16
|
-
constructor(factory, now = Date.now) {
|
|
24
|
+
constructor(factory, now = Date.now, instanceId = randomUUID()) {
|
|
17
25
|
this.factory = factory;
|
|
18
26
|
this.now = now;
|
|
27
|
+
this.instanceId = instanceId;
|
|
19
28
|
}
|
|
20
29
|
parentContext(token) {
|
|
21
30
|
const session = [...this.sessions.values()].find(session => session.token === token);
|
|
22
31
|
const task = session?.active ? this.task(session.active) : undefined;
|
|
23
32
|
if (!session || !task || task.stopping || terminal(task.state))
|
|
24
33
|
throw new AgentError('INVALID_PARENT', 'The parent context is no longer active.');
|
|
25
|
-
return { taskId: task.id, reference: session.input.reference, cwd: session.input.cwd };
|
|
34
|
+
return { taskId: task.id, sessionId: session.id, reference: session.input.reference, cwd: session.input.cwd };
|
|
26
35
|
}
|
|
27
|
-
|
|
28
|
-
this.
|
|
29
|
-
|
|
36
|
+
createSession(input, prepared) {
|
|
37
|
+
if (this.closed)
|
|
38
|
+
throw new AgentError('COORDINATOR_UNAVAILABLE', 'The coordinator is shutting down.');
|
|
39
|
+
const selected = 'kind' in input.reference && input.reference.kind === 'harness'
|
|
40
|
+
? input.reference.harness.kind : undefined;
|
|
30
41
|
const session = { id: `ses_${randomUUID()}`, token: randomUUID(), input,
|
|
31
|
-
tasks: [], paused: false, commands: Promise.resolve(), controls: 0,
|
|
32
|
-
|
|
42
|
+
tasks: [], paused: false, commands: Promise.resolve(), controls: 0, nextOrdinal: 1,
|
|
43
|
+
membership: ++this.membership, prepared,
|
|
44
|
+
harness: dashboardHarness(selected), workspace: 'kind' in input.reference && input.reference.kind === 'inline'
|
|
45
|
+
? Promise.resolve({ directory: input.cwd }) : resolveDashboardWorkspace(input.cwd), state: 'open' };
|
|
33
46
|
this.sessions.set(session.id, session);
|
|
47
|
+
return session.id;
|
|
48
|
+
}
|
|
49
|
+
run(input, prepared) {
|
|
50
|
+
this.validateAdmission(undefined, input.prompt, input.parentToken);
|
|
51
|
+
const { prompt, parentToken, ...creation } = input;
|
|
52
|
+
const session = this.session(this.createSession(creation, prepared));
|
|
34
53
|
return this.admit(session, input.prompt, false, input.parentToken);
|
|
35
54
|
}
|
|
36
55
|
async send(id, prompt, delivery, parentToken) {
|
|
@@ -68,26 +87,30 @@ export class Coordinator {
|
|
|
68
87
|
return this.command(session, operation);
|
|
69
88
|
}
|
|
70
89
|
async observe(id, after, readerToken, signal) {
|
|
90
|
+
if (signal?.aborted)
|
|
91
|
+
throw new AgentError('OBSERVATION_CLOSED', 'The observer disconnected.');
|
|
71
92
|
const task = this.task(id);
|
|
72
|
-
const
|
|
73
|
-
|
|
93
|
+
const hasCursor = after !== undefined;
|
|
94
|
+
const position = hasCursor ? task.responses.findIndex(response => response.responseId === after) : -1;
|
|
95
|
+
if (hasCursor && position === -1)
|
|
74
96
|
throw new AgentError('UNKNOWN_RESPONSE', 'The response does not belong to this task.');
|
|
75
97
|
for (;;) {
|
|
76
98
|
const response = task.responses[position + 1];
|
|
77
99
|
if (response) {
|
|
78
100
|
if (terminal(response.state))
|
|
79
101
|
this.acknowledge(task, readerToken);
|
|
80
|
-
return response;
|
|
102
|
+
return detached(response);
|
|
81
103
|
}
|
|
82
104
|
if (terminal(task.state)) {
|
|
83
105
|
this.acknowledge(task, readerToken);
|
|
84
|
-
return { version: 1, type: 'terminal', taskId: id,
|
|
85
|
-
sessionId: task.sessionId, state: task.state, ...(task.error ? { error: task.error } : {})
|
|
106
|
+
return detached({ version: 1, type: 'terminal', taskId: id,
|
|
107
|
+
sessionId: task.sessionId, state: task.state, ...(task.error ? { error: task.error } : {}),
|
|
108
|
+
stopUnconfirmed: task.stopUnconfirmed ?? false });
|
|
86
109
|
}
|
|
87
110
|
const requests = this.pendingApprovals(task);
|
|
88
111
|
if (requests.length)
|
|
89
|
-
return { version: 1, type: 'approval_required', taskId: id,
|
|
90
|
-
sessionId: task.sessionId, state: task.state, requests };
|
|
112
|
+
return detached({ version: 1, type: 'approval_required', taskId: id,
|
|
113
|
+
sessionId: task.sessionId, state: task.state, requests });
|
|
91
114
|
await waitForChange(task.changed, signal);
|
|
92
115
|
}
|
|
93
116
|
}
|
|
@@ -108,6 +131,94 @@ export class Coordinator {
|
|
|
108
131
|
truncated: !full && response.text.length > 4000
|
|
109
132
|
} } : {}) };
|
|
110
133
|
}
|
|
134
|
+
snapshot(id) {
|
|
135
|
+
const task = this.task(id);
|
|
136
|
+
const response = task.responses.at(-1);
|
|
137
|
+
return detached({
|
|
138
|
+
...this.summary(task),
|
|
139
|
+
revision: task.revision,
|
|
140
|
+
...(task.parentId ? { parentTaskId: task.parentId } : {}),
|
|
141
|
+
children: [...task.children].map(childId => this.summary(this.task(childId))),
|
|
142
|
+
requests: this.pendingApprovals(task),
|
|
143
|
+
...(response ? { latestResponse: { responseId: response.responseId, text: response.text, state: response.state } } : {}),
|
|
144
|
+
...(task.error ? { error: task.error } : {}),
|
|
145
|
+
stopUnconfirmed: task.stopUnconfirmed ?? false,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
sessionSnapshot(id) {
|
|
149
|
+
const session = this.session(id);
|
|
150
|
+
return detached({
|
|
151
|
+
sessionId: session.id,
|
|
152
|
+
state: session.state,
|
|
153
|
+
paused: session.paused,
|
|
154
|
+
...(session.active ? { active: this.summary(this.task(session.active)) } : {}),
|
|
155
|
+
queued: session.tasks.map(taskId => this.summary(this.task(taskId))),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
sdkTaskSnapshot(id) {
|
|
159
|
+
const task = this.task(id);
|
|
160
|
+
return detached(projectTaskSnapshot(task, candidate => this.sdkSummary(candidate), candidate => this.task(candidate), this.sdkPendingApprovals(task)));
|
|
161
|
+
}
|
|
162
|
+
async sdkSessionSnapshot(id) {
|
|
163
|
+
const session = this.session(id);
|
|
164
|
+
const workspace = await session.workspace;
|
|
165
|
+
return detached({ ...this.sdkSessionSummaryResolved(session, workspace),
|
|
166
|
+
queued: session.tasks.map(taskId => this.sdkSummary(this.task(taskId))) });
|
|
167
|
+
}
|
|
168
|
+
taskDetails(id) {
|
|
169
|
+
const task = this.task(id);
|
|
170
|
+
return detached({ ...this.sdkTaskSnapshot(id), prompt: task.prompt });
|
|
171
|
+
}
|
|
172
|
+
taskResult(id, signal) {
|
|
173
|
+
const task = this.task(id);
|
|
174
|
+
return terminalInterval(task, () => this.sdkTaskSnapshot(id), signal).then(({ snapshot, response }) => {
|
|
175
|
+
if (snapshot.state === 'completed') {
|
|
176
|
+
if (!response)
|
|
177
|
+
throw new AgentError('HARNESS_FAILED', 'The completed task has no final response.');
|
|
178
|
+
return detached({ taskId: task.id, sessionId: task.sessionId, responseId: response.responseId, text: response.text });
|
|
179
|
+
}
|
|
180
|
+
if (snapshot.state === 'cancelled')
|
|
181
|
+
throw new AgentError('CANCELLED', 'The task was cancelled.');
|
|
182
|
+
if (snapshot.state === 'interrupted')
|
|
183
|
+
throw new AgentError('INTERRUPTED', 'The task was interrupted.');
|
|
184
|
+
throw new AgentError(snapshot.error?.code ?? 'HARNESS_FAILED', snapshot.error?.message ?? 'Task execution failed.');
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
taskResponses(id, after, signal) {
|
|
188
|
+
const task = this.task(id);
|
|
189
|
+
const start = after === undefined ? 0 : task.responses.findIndex(response => response.responseId === after) + 1;
|
|
190
|
+
if (after !== undefined && start === 0)
|
|
191
|
+
throw new AgentError('UNKNOWN_RESPONSE', 'The response does not belong to this task.');
|
|
192
|
+
return responseReplay(task, start, signal);
|
|
193
|
+
}
|
|
194
|
+
watchTask(id, signal) {
|
|
195
|
+
const task = this.task(id);
|
|
196
|
+
return createSnapshotWatch(() => this.sdkTaskSnapshot(id), changed => {
|
|
197
|
+
task.watchers.add(changed);
|
|
198
|
+
return () => task.watchers.delete(changed);
|
|
199
|
+
}, signal);
|
|
200
|
+
}
|
|
201
|
+
async listSessions(options) {
|
|
202
|
+
const page = paginate([...this.sessions.values()], options, { instance: this.instanceId, collection: 'sessions', filter: '' }, value => value.membership);
|
|
203
|
+
return detached({ items: await Promise.all(page.items.map(session => this.sdkSessionSummary(session))),
|
|
204
|
+
...(page.nextCursor ? { nextCursor: page.nextCursor } : {}) });
|
|
205
|
+
}
|
|
206
|
+
listTasks(options) {
|
|
207
|
+
const sessionId = options?.sessionId;
|
|
208
|
+
if (sessionId !== undefined)
|
|
209
|
+
this.session(sessionId);
|
|
210
|
+
const candidates = [...this.tasks.values()].filter(task => sessionId === undefined || task.sessionId === sessionId);
|
|
211
|
+
const page = paginate(candidates, options, { instance: this.instanceId, collection: 'tasks', filter: sessionId ?? '' }, value => value.membership);
|
|
212
|
+
return detached({ items: page.items.map(task => this.sdkSummary(task)), ...(page.nextCursor ? { nextCursor: page.nextCursor } : {}) });
|
|
213
|
+
}
|
|
214
|
+
respondSdk(id, response) { return this.approvals.respondSdk(id, response); }
|
|
215
|
+
watch(id, signal) {
|
|
216
|
+
const task = this.task(id);
|
|
217
|
+
return createSnapshotWatch(() => this.snapshot(task.id), changed => {
|
|
218
|
+
task.watchers.add(changed);
|
|
219
|
+
return () => task.watchers.delete(changed);
|
|
220
|
+
}, signal);
|
|
221
|
+
}
|
|
111
222
|
respond(id, content, parentToken) {
|
|
112
223
|
const request = this.approvals.request(id);
|
|
113
224
|
if (parentToken !== undefined) {
|
|
@@ -245,13 +356,92 @@ export class Coordinator {
|
|
|
245
356
|
return { version: 1, type: 'started', sessionId: id, taskId: task.id, state: task.state, resumed: true };
|
|
246
357
|
});
|
|
247
358
|
}
|
|
248
|
-
|
|
359
|
+
close() {
|
|
249
360
|
this.closed = true;
|
|
250
|
-
for (const task of this.tasks.values())
|
|
251
|
-
if (!terminal(task.state))
|
|
252
|
-
task.stopping = true;
|
|
253
361
|
this.approvals.close();
|
|
254
|
-
|
|
362
|
+
if (this.closeComplete)
|
|
363
|
+
return Promise.resolve();
|
|
364
|
+
if (this.closePromise)
|
|
365
|
+
return this.closePromise;
|
|
366
|
+
const attempt = (async () => {
|
|
367
|
+
const results = await Promise.allSettled([...this.sessions].map(([id]) => this.closeSession(id)));
|
|
368
|
+
const failures = results.flatMap(result => result.status === 'rejected'
|
|
369
|
+
? this.cleanupErrors(result.reason, 'Runner cleanup could not be confirmed.') : []);
|
|
370
|
+
if (failures.length)
|
|
371
|
+
throw new AggregateError(failures, 'Coordinator cleanup failed.');
|
|
372
|
+
this.closeComplete = true;
|
|
373
|
+
})();
|
|
374
|
+
this.closePromise = attempt.finally(() => { this.closePromise = undefined; });
|
|
375
|
+
return this.closePromise;
|
|
376
|
+
}
|
|
377
|
+
closeSession(id) {
|
|
378
|
+
const session = this.session(id);
|
|
379
|
+
if (session.state === 'closed')
|
|
380
|
+
return Promise.resolve();
|
|
381
|
+
if (session.closePromise)
|
|
382
|
+
return session.closePromise;
|
|
383
|
+
session.state = 'closing';
|
|
384
|
+
const attempt = this.cleanupSession(session);
|
|
385
|
+
session.closePromise = attempt.finally(() => { session.closePromise = undefined; });
|
|
386
|
+
return session.closePromise;
|
|
387
|
+
}
|
|
388
|
+
async cleanupSession(session) {
|
|
389
|
+
const targets = [...this.tasks.values()].filter(task => task.sessionId === session.id &&
|
|
390
|
+
(!terminal(task.state) || task.stopUnconfirmed));
|
|
391
|
+
let preparedFailure = false;
|
|
392
|
+
const preparedClose = !session.preparedClosed && session.prepared
|
|
393
|
+
? session.prepared.close().then(() => { session.preparedClosed = true; }, () => { preparedFailure = true; })
|
|
394
|
+
: Promise.resolve();
|
|
395
|
+
let stopped = await Promise.allSettled(targets.map(task => this.stop(task, 'cancelled')));
|
|
396
|
+
await session.commands.catch(() => undefined);
|
|
397
|
+
let driverClosedDuringAttempt = false;
|
|
398
|
+
const failures = [];
|
|
399
|
+
if (!session.driverClosed && session.driver) {
|
|
400
|
+
let driver;
|
|
401
|
+
try {
|
|
402
|
+
driver = await session.driver;
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
session.driverClosed = true;
|
|
406
|
+
driverClosedDuringAttempt = true;
|
|
407
|
+
}
|
|
408
|
+
if (driver) {
|
|
409
|
+
try {
|
|
410
|
+
await driver.close();
|
|
411
|
+
session.driverClosed = true;
|
|
412
|
+
driverClosedDuringAttempt = true;
|
|
413
|
+
for (const task of this.tasks.values())
|
|
414
|
+
if (task.sessionId === session.id)
|
|
415
|
+
task.nativeActive = false;
|
|
416
|
+
}
|
|
417
|
+
catch {
|
|
418
|
+
failures.push(new AgentError('HARNESS_FAILED', 'Native session cleanup could not be confirmed.'));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
await preparedClose;
|
|
423
|
+
if (preparedFailure)
|
|
424
|
+
failures.push(new AgentError('HARNESS_FAILED', 'Prepared execution cleanup could not be confirmed.'));
|
|
425
|
+
if ((driverClosedDuringAttempt || session.preparedClosed) && stopped.some(result => result.status === 'rejected')) {
|
|
426
|
+
const retry = targets.filter(task => task.stopUnconfirmed);
|
|
427
|
+
stopped = await Promise.allSettled(retry.map(task => this.stop(task, 'cancelled')));
|
|
428
|
+
}
|
|
429
|
+
failures.push(...stopped.flatMap(result => result.status === 'rejected'
|
|
430
|
+
? [new AgentError('CANCEL_FAILED', 'Task execution could not be confirmed stopped.')] : []));
|
|
431
|
+
if (failures.length)
|
|
432
|
+
throw new AggregateError(failures, 'Session cleanup failed.');
|
|
433
|
+
session.paused = false;
|
|
434
|
+
session.state = 'closed';
|
|
435
|
+
}
|
|
436
|
+
cleanupErrors(reason, message) {
|
|
437
|
+
if (reason instanceof AggregateError) {
|
|
438
|
+
const errors = [...reason.errors];
|
|
439
|
+
return errors.length ? errors.map(error => error instanceof AgentError
|
|
440
|
+
? new AgentError(error.code, error.message) : new AgentError('HARNESS_FAILED', message)) : [];
|
|
441
|
+
}
|
|
442
|
+
return [reason instanceof AgentError
|
|
443
|
+
? new AgentError(reason.code, reason.message)
|
|
444
|
+
: new AgentError('HARNESS_FAILED', message)];
|
|
255
445
|
}
|
|
256
446
|
async cancel(id) {
|
|
257
447
|
const task = this.task(id);
|
|
@@ -297,7 +487,7 @@ export class Coordinator {
|
|
|
297
487
|
stops.push(session.driver.then(async (driver) => {
|
|
298
488
|
await driver.interrupt();
|
|
299
489
|
task.nativeActive = false;
|
|
300
|
-
}));
|
|
490
|
+
}, () => { task.nativeActive = false; }));
|
|
301
491
|
const outcomes = await Promise.allSettled(stops);
|
|
302
492
|
if (outcomes.some(outcome => outcome.status === 'rejected'))
|
|
303
493
|
throw new Error('Cancellation unconfirmed');
|
|
@@ -308,7 +498,7 @@ export class Coordinator {
|
|
|
308
498
|
task.error = { code: 'CANCEL_FAILED', message: 'Native execution could not be confirmed stopped.' };
|
|
309
499
|
task.stopUnconfirmed = true;
|
|
310
500
|
session.paused = true;
|
|
311
|
-
this.
|
|
501
|
+
this.publish(task);
|
|
312
502
|
throw new AgentError('CANCEL_FAILED', task.error.message);
|
|
313
503
|
}
|
|
314
504
|
task.nativeActive = false;
|
|
@@ -319,7 +509,7 @@ export class Coordinator {
|
|
|
319
509
|
session.active = undefined;
|
|
320
510
|
session.paused = false;
|
|
321
511
|
}
|
|
322
|
-
this.
|
|
512
|
+
this.publish(task);
|
|
323
513
|
this.deliver(task);
|
|
324
514
|
}
|
|
325
515
|
admit(session, prompt, front = false, parentToken) {
|
|
@@ -327,9 +517,12 @@ export class Coordinator {
|
|
|
327
517
|
const blockedByTaskId = session.paused ? session.active : undefined;
|
|
328
518
|
const task = { id: `tsk_${randomUUID()}`, sessionId: session.id, prompt, title: dashboardTitle(prompt),
|
|
329
519
|
parentId: parent?.id, state: 'queued', responses: [], children: new Set(), inbox: [], nativeActive: false,
|
|
330
|
-
stopping: false, updatedAt: this.now(),
|
|
520
|
+
stopping: false, updatedAt: this.now(), revision: 1, ordinal: session.nextOrdinal++, membership: ++this.membership,
|
|
521
|
+
changed: new Set(), watchers: new Set() };
|
|
331
522
|
this.tasks.set(task.id, task);
|
|
332
523
|
parent?.children.add(task.id);
|
|
524
|
+
if (parent)
|
|
525
|
+
this.publish(parent);
|
|
333
526
|
if (front)
|
|
334
527
|
session.tasks.unshift(task.id);
|
|
335
528
|
else
|
|
@@ -341,6 +534,8 @@ export class Coordinator {
|
|
|
341
534
|
validateAdmission(session, prompt, parentToken, replacement = false) {
|
|
342
535
|
if (this.closed)
|
|
343
536
|
throw new AgentError('COORDINATOR_UNAVAILABLE', 'The coordinator is shutting down.');
|
|
537
|
+
if (session && session.state !== 'open')
|
|
538
|
+
throw new AgentError('SESSION_CLOSED', 'The session is closing or closed.');
|
|
344
539
|
if (typeof prompt !== 'string' || !prompt.trim() || Buffer.byteLength(prompt) > 1024 * 1024)
|
|
345
540
|
throw new AgentError('INVALID_ARGUMENT', 'Prompt must contain text and cannot exceed 1 MiB.');
|
|
346
541
|
if (!replacement && session && session.tasks.length >= 100)
|
|
@@ -358,7 +553,7 @@ export class Coordinator {
|
|
|
358
553
|
return parent;
|
|
359
554
|
}
|
|
360
555
|
dispatch(session) {
|
|
361
|
-
if (this.closed || session.active || session.paused || session.controls)
|
|
556
|
+
if (this.closed || session.state !== 'open' || session.active || session.paused || session.controls)
|
|
362
557
|
return;
|
|
363
558
|
const id = session.tasks.shift();
|
|
364
559
|
if (!id)
|
|
@@ -370,11 +565,13 @@ export class Coordinator {
|
|
|
370
565
|
const session = this.session(task.sessionId);
|
|
371
566
|
const dispatchedAt = this.now();
|
|
372
567
|
task.dispatchedAt ??= dispatchedAt;
|
|
373
|
-
this.touch(task, dispatchedAt);
|
|
374
568
|
task.state = 'running';
|
|
375
569
|
task.nativeActive = true;
|
|
570
|
+
this.touch(task, dispatchedAt);
|
|
571
|
+
this.publish(task);
|
|
376
572
|
try {
|
|
377
|
-
|
|
573
|
+
const open = session.prepared ? session.prepared.open.bind(session.prepared) : this.factory;
|
|
574
|
+
session.driver ??= open({ ...session.input, sessionId: session.id, token: session.token,
|
|
378
575
|
onApproval: async (request, signal) => {
|
|
379
576
|
const active = session.active ? this.task(session.active) : undefined;
|
|
380
577
|
if (this.closed || !active || active.stopping || !active.nativeActive || terminal(active.state))
|
|
@@ -384,9 +581,13 @@ export class Coordinator {
|
|
|
384
581
|
const driver = await session.driver;
|
|
385
582
|
if (driver.selectedHarness) {
|
|
386
583
|
const harness = dashboardHarness(driver.selectedHarness);
|
|
387
|
-
if (session.
|
|
584
|
+
if (session.selectedHarness !== driver.selectedHarness) {
|
|
585
|
+
session.selectedHarness = driver.selectedHarness;
|
|
388
586
|
session.harness = harness;
|
|
389
587
|
this.touch(task);
|
|
588
|
+
for (const candidate of this.tasks.values())
|
|
589
|
+
if (candidate.sessionId === session.id)
|
|
590
|
+
this.publish(candidate);
|
|
390
591
|
}
|
|
391
592
|
}
|
|
392
593
|
if (task.stopping)
|
|
@@ -406,7 +607,7 @@ export class Coordinator {
|
|
|
406
607
|
this.touch(task, respondedAt);
|
|
407
608
|
task.responses.push({ version: 1, type: 'response', sessionId: session.id, taskId: task.id,
|
|
408
609
|
responseId: `rsp_${randomUUID()}`, state: task.state, text });
|
|
409
|
-
this.
|
|
610
|
+
this.publish(task);
|
|
410
611
|
if (task.state === 'completed') {
|
|
411
612
|
session.active = undefined;
|
|
412
613
|
this.deliver(task);
|
|
@@ -428,7 +629,7 @@ export class Coordinator {
|
|
|
428
629
|
task.error = task.stopUnconfirmed
|
|
429
630
|
? { code: 'CANCEL_FAILED', message: 'Descendant execution could not be confirmed stopped.' } : errorInfo(error);
|
|
430
631
|
session.paused = true;
|
|
431
|
-
this.
|
|
632
|
+
this.publish(task);
|
|
432
633
|
this.deliver(task);
|
|
433
634
|
}
|
|
434
635
|
}
|
|
@@ -454,7 +655,34 @@ export class Coordinator {
|
|
|
454
655
|
results.map(result => `${result.taskId} [${result.state}]:\n${result.text}`).join('\n\n');
|
|
455
656
|
void this.execute(task, prompt);
|
|
456
657
|
}
|
|
658
|
+
summary(task) {
|
|
659
|
+
const selectedHarness = this.session(task.sessionId).selectedHarness;
|
|
660
|
+
return { taskId: task.id, sessionId: task.sessionId, state: task.state,
|
|
661
|
+
...(selectedHarness ? { selectedHarness } : {}) };
|
|
662
|
+
}
|
|
663
|
+
sdkSummary(task) {
|
|
664
|
+
return projectTaskSummary(task, this.session(task.sessionId).selectedHarness);
|
|
665
|
+
}
|
|
666
|
+
async sdkSessionSummary(session) {
|
|
667
|
+
const workspace = await session.workspace;
|
|
668
|
+
return this.sdkSessionSummaryResolved(session, workspace);
|
|
669
|
+
}
|
|
670
|
+
sdkSessionSummaryResolved(session, workspace) {
|
|
671
|
+
return projectSessionSummary(session, workspace, task => this.sdkSummary(task), id => this.task(id));
|
|
672
|
+
}
|
|
673
|
+
sdkPendingApprovals(task) {
|
|
674
|
+
return [...this.approvals.sdkPendingFor(task.id),
|
|
675
|
+
...[...task.children].flatMap(id => this.sdkPendingApprovals(this.task(id)))];
|
|
676
|
+
}
|
|
457
677
|
touch(task, at = this.now()) { task.updatedAt = at; }
|
|
678
|
+
publish(task) {
|
|
679
|
+
let current = task;
|
|
680
|
+
while (current) {
|
|
681
|
+
current.revision++;
|
|
682
|
+
this.notify(current);
|
|
683
|
+
current = current.parentId ? this.task(current.parentId) : undefined;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
458
686
|
pendingApprovals(task) {
|
|
459
687
|
return [...this.approvals.pendingFor(task.id), ...[...task.children].flatMap(id => this.pendingApprovals(this.task(id)))];
|
|
460
688
|
}
|
|
@@ -464,12 +692,18 @@ export class Coordinator {
|
|
|
464
692
|
this.touch(task);
|
|
465
693
|
let current = task;
|
|
466
694
|
while (current) {
|
|
695
|
+
current.revision++;
|
|
467
696
|
this.notify(current);
|
|
468
697
|
current = current.parentId ? this.task(current.parentId) : undefined;
|
|
469
698
|
}
|
|
470
699
|
}
|
|
471
|
-
notify(task) {
|
|
472
|
-
|
|
700
|
+
notify(task) {
|
|
701
|
+
for (const resolve of task.changed)
|
|
702
|
+
resolve();
|
|
703
|
+
task.changed.clear();
|
|
704
|
+
for (const update of task.watchers)
|
|
705
|
+
update();
|
|
706
|
+
}
|
|
473
707
|
task(id) { const task = this.tasks.get(id); if (!task)
|
|
474
708
|
throw new AgentError('UNKNOWN_TASK', 'Task is unavailable in this coordinator.'); return task; }
|
|
475
709
|
session(id) { const session = this.sessions.get(id); if (!session)
|