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
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { AgentError } from "../errors.js";
|
|
2
|
+
import { NativeApprovals } from "./approval-lifetime.js";
|
|
3
|
+
import { cursorCliApproval } from "./cursor-cli-approvals.js";
|
|
4
|
+
import { applyCursorCliModel, verifyCursorCliModelUpdate } from "./cursor-cli-model.js";
|
|
5
|
+
import { CursorRpcNativeError } from "./cursor-rpc.js";
|
|
6
|
+
export class CursorCliSession {
|
|
7
|
+
rpc;
|
|
8
|
+
sessionId;
|
|
9
|
+
selected;
|
|
10
|
+
options;
|
|
11
|
+
tools;
|
|
12
|
+
cleanup;
|
|
13
|
+
closePromise;
|
|
14
|
+
active;
|
|
15
|
+
failure;
|
|
16
|
+
first = true;
|
|
17
|
+
closed = false;
|
|
18
|
+
constructor(rpc, sessionId, selected, options, tools, cleanup) {
|
|
19
|
+
this.rpc = rpc;
|
|
20
|
+
this.sessionId = sessionId;
|
|
21
|
+
this.selected = selected;
|
|
22
|
+
this.options = options;
|
|
23
|
+
this.tools = tools;
|
|
24
|
+
this.cleanup = cleanup;
|
|
25
|
+
rpc.onMessage = message => this.message(message);
|
|
26
|
+
rpc.setFailureHandler(error => this.fail(error));
|
|
27
|
+
tools.onFailure(error => this.fail(error));
|
|
28
|
+
}
|
|
29
|
+
async turn(prompt) {
|
|
30
|
+
if (this.closed)
|
|
31
|
+
throw new AgentError("SESSION_CLOSED", "The Cursor session is closed.");
|
|
32
|
+
if (this.failure)
|
|
33
|
+
throw this.failure;
|
|
34
|
+
if (this.active)
|
|
35
|
+
throw new AgentError("SESSION_BUSY", "Cursor already has an active turn.");
|
|
36
|
+
let resolve;
|
|
37
|
+
let reject;
|
|
38
|
+
let releaseAdmission;
|
|
39
|
+
let resolveNativeStopped;
|
|
40
|
+
const completion = new Promise((accept, fail) => { resolve = accept; reject = fail; });
|
|
41
|
+
const admission = new Promise(accept => { releaseAdmission = accept; });
|
|
42
|
+
const nativeStopped = new Promise(accept => { resolveNativeStopped = accept; });
|
|
43
|
+
void completion.catch(() => { });
|
|
44
|
+
const active = {
|
|
45
|
+
text: "", responseBytes: 0, responseTooLarge: false, completion, resolve, reject,
|
|
46
|
+
terminal: false, submitted: false, cancelRequested: false,
|
|
47
|
+
admission, releaseAdmission, nativeStopped, resolveNativeStopped,
|
|
48
|
+
approvals: new NativeApprovals(), pendingApprovalIds: new Set(), withdrawnApprovals: new Set(),
|
|
49
|
+
};
|
|
50
|
+
this.active = active;
|
|
51
|
+
try {
|
|
52
|
+
await applyCursorCliModel(this.rpc, this.sessionId, this.selected);
|
|
53
|
+
if (this.failure)
|
|
54
|
+
throw this.failure;
|
|
55
|
+
if (active.cancelRequested || this.closed)
|
|
56
|
+
throw new AgentError("INTERRUPTED", "The Cursor turn was interrupted before submission.");
|
|
57
|
+
this.tools.beginTurn();
|
|
58
|
+
const text = this.first && this.options.agent.instructions
|
|
59
|
+
? firstPrompt(this.options.agent.instructions, prompt)
|
|
60
|
+
: prompt;
|
|
61
|
+
active.submitted = true;
|
|
62
|
+
active.releaseAdmission();
|
|
63
|
+
const promptRequest = this.rpc.request("session/prompt", { sessionId: this.sessionId, prompt: [{ type: "text", text }] });
|
|
64
|
+
this.first = false;
|
|
65
|
+
const result = await promptRequest;
|
|
66
|
+
active.resolveNativeStopped();
|
|
67
|
+
await this.finish(active, result, active.failure);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
active.releaseAdmission();
|
|
71
|
+
active.resolveNativeStopped();
|
|
72
|
+
const failure = active.failure ?? (error instanceof CursorRpcNativeError
|
|
73
|
+
? new AgentError("HARNESS_ERROR", "Cursor could not complete the native prompt.")
|
|
74
|
+
: error instanceof AgentError ? error : new AgentError("HARNESS_ERROR", "Cursor could not complete the native prompt."));
|
|
75
|
+
await this.finish(active, undefined, failure);
|
|
76
|
+
}
|
|
77
|
+
return await completion;
|
|
78
|
+
}
|
|
79
|
+
async steer(_prompt) {
|
|
80
|
+
throw new AgentError("UNSUPPORTED_DELIVERY", "Cursor does not expose verified active steering.");
|
|
81
|
+
}
|
|
82
|
+
async interrupt() {
|
|
83
|
+
const active = this.active;
|
|
84
|
+
if (!active)
|
|
85
|
+
return;
|
|
86
|
+
active.stopTask ??= this.stop(active);
|
|
87
|
+
return await active.stopTask;
|
|
88
|
+
}
|
|
89
|
+
async resume() {
|
|
90
|
+
throw new AgentError("RECOVERY_UNSUPPORTED", "Cursor recovery without a new prompt is unavailable.");
|
|
91
|
+
}
|
|
92
|
+
close() {
|
|
93
|
+
this.closePromise ??= this.closeOwnedResources();
|
|
94
|
+
return this.closePromise;
|
|
95
|
+
}
|
|
96
|
+
async closeOwnedResources() {
|
|
97
|
+
this.closed = true;
|
|
98
|
+
this.tools.stopAdmission();
|
|
99
|
+
const active = this.active;
|
|
100
|
+
if (active) {
|
|
101
|
+
active.cancelRequested = true;
|
|
102
|
+
active.failure ??= new AgentError("SESSION_CLOSED", "The Cursor session was closed during execution.");
|
|
103
|
+
this.retireApprovals(active);
|
|
104
|
+
if (active.submitted && !active.terminal) {
|
|
105
|
+
try {
|
|
106
|
+
this.rpc.send({ method: "session/cancel", params: { sessionId: this.sessionId } });
|
|
107
|
+
}
|
|
108
|
+
catch { }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
let closeFailure;
|
|
112
|
+
try {
|
|
113
|
+
await this.rpc.close();
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
closeFailure = error;
|
|
117
|
+
}
|
|
118
|
+
await this.tools.close();
|
|
119
|
+
await active?.completion.catch(() => { });
|
|
120
|
+
if (closeFailure)
|
|
121
|
+
throw closeFailure;
|
|
122
|
+
await this.cleanup();
|
|
123
|
+
}
|
|
124
|
+
message(message) {
|
|
125
|
+
const active = this.active;
|
|
126
|
+
const params = asObject(message.params);
|
|
127
|
+
if (message.method === "$/cancel_request" && active && (typeof params?.requestId === "string" || typeof params?.requestId === "number")) {
|
|
128
|
+
if (active.pendingApprovalIds.delete(params.requestId)) {
|
|
129
|
+
active.withdrawnApprovals.add(params.requestId);
|
|
130
|
+
active.approvals.withdraw(params.requestId);
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (message.id !== undefined) {
|
|
135
|
+
if (active && active.submitted && !active.terminal && !active.cancelRequested && !active.failure
|
|
136
|
+
&& params?.sessionId === this.sessionId && message.method === "session/request_permission" && this.options.onApproval) {
|
|
137
|
+
try {
|
|
138
|
+
const request = cursorCliApproval(message.params);
|
|
139
|
+
active.pendingApprovalIds.add(message.id);
|
|
140
|
+
void active.approvals.request(message.id, request, this.options.onApproval).then(content => {
|
|
141
|
+
if (this.active === active && !active.terminal && !active.cancelRequested && !active.failure && !active.withdrawnApprovals.has(message.id)) {
|
|
142
|
+
active.pendingApprovalIds.delete(message.id);
|
|
143
|
+
this.rpc.send({ id: message.id, result: { outcome: { outcome: "selected", optionId: content.decision } } });
|
|
144
|
+
}
|
|
145
|
+
}).catch(error => {
|
|
146
|
+
if (!(error instanceof AgentError && error.code === "REQUEST_EXPIRED"))
|
|
147
|
+
this.unsupportedRequest(message, active);
|
|
148
|
+
}).finally(() => {
|
|
149
|
+
active.pendingApprovalIds.delete(message.id);
|
|
150
|
+
active.withdrawnApprovals.delete(message.id);
|
|
151
|
+
});
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
catch { }
|
|
155
|
+
}
|
|
156
|
+
this.unsupportedRequest(message, active);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (message.method !== "session/update")
|
|
160
|
+
return;
|
|
161
|
+
if (!params || params.sessionId !== this.sessionId || !active || active.terminal)
|
|
162
|
+
return;
|
|
163
|
+
const update = asObject(params.update);
|
|
164
|
+
if (!update || typeof update.sessionUpdate !== "string") {
|
|
165
|
+
this.fail(new AgentError("PROTOCOL_ERROR", "Cursor returned an invalid ACP session update."));
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (update.sessionUpdate === "config_option_update") {
|
|
169
|
+
try {
|
|
170
|
+
verifyCursorCliModelUpdate(update.configOptions, this.selected);
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
this.stopForModelFailure(active, error instanceof AgentError
|
|
174
|
+
? error
|
|
175
|
+
: new AgentError("PROTOCOL_ERROR", "Cursor returned an invalid native model configuration."));
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (update.sessionUpdate !== "agent_message_chunk")
|
|
180
|
+
return;
|
|
181
|
+
const content = asObject(update.content);
|
|
182
|
+
if (content?.type !== "text" || typeof content.text !== "string") {
|
|
183
|
+
this.fail(new AgentError("PROTOCOL_ERROR", "Cursor returned an invalid assistant message update."));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
active.responseBytes += Buffer.byteLength(content.text, "utf8");
|
|
187
|
+
if (active.responseBytes > 1024 * 1024)
|
|
188
|
+
active.responseTooLarge = true;
|
|
189
|
+
else
|
|
190
|
+
active.text += content.text;
|
|
191
|
+
}
|
|
192
|
+
async finish(active, value, error) {
|
|
193
|
+
if (active.terminal)
|
|
194
|
+
return;
|
|
195
|
+
active.terminal = true;
|
|
196
|
+
this.retireApprovals(active);
|
|
197
|
+
this.tools.stopAdmission();
|
|
198
|
+
await this.tools.drain();
|
|
199
|
+
if (this.active === active)
|
|
200
|
+
this.active = undefined;
|
|
201
|
+
if (error)
|
|
202
|
+
return active.reject(error);
|
|
203
|
+
const result = asObject(value);
|
|
204
|
+
if (!result || typeof result.stopReason !== "string") {
|
|
205
|
+
const failure = new AgentError("PROTOCOL_ERROR", "Cursor returned an invalid native terminal result.");
|
|
206
|
+
this.failure = failure;
|
|
207
|
+
return active.reject(failure);
|
|
208
|
+
}
|
|
209
|
+
if (active.responseTooLarge)
|
|
210
|
+
return active.reject(new AgentError("RESPONSE_TOO_LARGE", "Cursor response exceeds 1 MiB."));
|
|
211
|
+
if (result.stopReason === "end_turn")
|
|
212
|
+
return active.resolve(active.text);
|
|
213
|
+
if (result.stopReason === "cancelled")
|
|
214
|
+
return active.reject(new AgentError("INTERRUPTED", "The Cursor turn was interrupted."));
|
|
215
|
+
active.reject(new AgentError("HARNESS_ERROR", "Cursor did not complete the native turn successfully."));
|
|
216
|
+
}
|
|
217
|
+
async stop(active) {
|
|
218
|
+
active.cancelRequested = true;
|
|
219
|
+
this.retireApprovals(active);
|
|
220
|
+
this.tools.stopAdmission();
|
|
221
|
+
try {
|
|
222
|
+
await bounded(active.admission, 2_000);
|
|
223
|
+
if (active.submitted && !active.terminal)
|
|
224
|
+
this.rpc.send({ method: "session/cancel", params: { sessionId: this.sessionId } });
|
|
225
|
+
const callbacks = this.tools.drain();
|
|
226
|
+
await bounded(active.nativeStopped, 3_000);
|
|
227
|
+
await callbacks;
|
|
228
|
+
await active.completion.catch(() => { });
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
const failure = new AgentError("CANCELLATION_FAILED", "Cursor did not confirm that native execution stopped.");
|
|
232
|
+
active.failure = failure;
|
|
233
|
+
this.failure = failure;
|
|
234
|
+
this.tools.stopAdmission();
|
|
235
|
+
await this.rpc.close().catch(() => { });
|
|
236
|
+
await this.tools.drain();
|
|
237
|
+
await this.finish(active, undefined, failure);
|
|
238
|
+
throw failure;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
fail(error) {
|
|
242
|
+
this.failure ??= error;
|
|
243
|
+
this.tools.stopAdmission();
|
|
244
|
+
const active = this.active;
|
|
245
|
+
if (active)
|
|
246
|
+
this.retireApprovals(active);
|
|
247
|
+
if (active)
|
|
248
|
+
void this.finish(active, undefined, this.failure);
|
|
249
|
+
void this.rpc.close().catch(() => { });
|
|
250
|
+
}
|
|
251
|
+
stopForModelFailure(active, failure) {
|
|
252
|
+
if (active.failure)
|
|
253
|
+
return;
|
|
254
|
+
active.failure = failure;
|
|
255
|
+
this.failure ??= failure;
|
|
256
|
+
this.tools.stopAdmission();
|
|
257
|
+
this.retireApprovals(active);
|
|
258
|
+
active.stopTask ??= this.stop(active);
|
|
259
|
+
void active.stopTask.catch(() => { });
|
|
260
|
+
}
|
|
261
|
+
unsupportedRequest(message, active) {
|
|
262
|
+
if (active && message.id !== undefined && active.pendingApprovalIds.has(message.id))
|
|
263
|
+
this.retireApproval(active, message.id);
|
|
264
|
+
else
|
|
265
|
+
this.respondCancelled(message.id);
|
|
266
|
+
const failure = new AgentError("INPUT_REQUIRED", "Cursor requested unsupported interactive input; no permission was granted.");
|
|
267
|
+
if (!active)
|
|
268
|
+
return this.fail(failure);
|
|
269
|
+
if (active.failure)
|
|
270
|
+
return;
|
|
271
|
+
active.failure = failure;
|
|
272
|
+
this.retireApprovals(active);
|
|
273
|
+
this.tools.stopAdmission();
|
|
274
|
+
active.stopTask ??= this.stop(active);
|
|
275
|
+
void active.stopTask.catch(() => { });
|
|
276
|
+
}
|
|
277
|
+
retireApprovals(active) {
|
|
278
|
+
for (const id of [...active.pendingApprovalIds])
|
|
279
|
+
this.retireApproval(active, id);
|
|
280
|
+
active.approvals.clear();
|
|
281
|
+
}
|
|
282
|
+
retireApproval(active, id) {
|
|
283
|
+
if (!active.pendingApprovalIds.delete(id))
|
|
284
|
+
return;
|
|
285
|
+
active.approvals.withdraw(id);
|
|
286
|
+
if (!active.withdrawnApprovals.has(id))
|
|
287
|
+
this.respondCancelled(id);
|
|
288
|
+
}
|
|
289
|
+
respondCancelled(id) {
|
|
290
|
+
if (id === undefined)
|
|
291
|
+
return;
|
|
292
|
+
try {
|
|
293
|
+
this.rpc.send({ id, result: { outcome: { outcome: "cancelled" } } });
|
|
294
|
+
}
|
|
295
|
+
catch { }
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function firstPrompt(instructions, prompt) {
|
|
299
|
+
return `<subharness-instructions>\n${instructions}\n</subharness-instructions>\n\n${prompt}`;
|
|
300
|
+
}
|
|
301
|
+
function asObject(value) {
|
|
302
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
303
|
+
}
|
|
304
|
+
async function bounded(promise, milliseconds) {
|
|
305
|
+
let timer;
|
|
306
|
+
try {
|
|
307
|
+
return await Promise.race([promise, new Promise((_resolve, reject) => {
|
|
308
|
+
timer = setTimeout(() => reject(new Error("timeout")), milliseconds);
|
|
309
|
+
timer.unref();
|
|
310
|
+
})]);
|
|
311
|
+
}
|
|
312
|
+
finally {
|
|
313
|
+
clearTimeout(timer);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=cursor-cli-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-cli-session.js","sourceRoot":"","sources":["../../src/adapters/cursor-cli-session.ts"],"names":[],"mappings":"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","sourcesContent":["import { AgentError } from \"../errors.js\";\nimport { NativeApprovals } from \"./approval-lifetime.js\";\nimport { cursorCliApproval } from \"./cursor-cli-approvals.js\";\nimport type { AdapterOptions, NativeSession } from \"./types.js\";\nimport { applyCursorCliModel, verifyCursorCliModelUpdate } from \"./cursor-cli-model.js\";\nimport type { CursorCliToolEndpoint } from \"./cursor-cli-tools.js\";\nimport { CursorRpcNativeError, type CursorRpc, type CursorRpcEnvelope } from \"./cursor-rpc.js\";\n\ntype ActiveTurn = {\n text: string;\n responseBytes: number;\n responseTooLarge: boolean;\n completion: Promise<string>;\n resolve(value: string): void;\n reject(error: Error): void;\n terminal: boolean;\n submitted: boolean;\n cancelRequested: boolean;\n admission: Promise<void>;\n releaseAdmission(): void;\n nativeStopped: Promise<void>;\n resolveNativeStopped(): void;\n stopTask?: Promise<void>;\n failure?: AgentError;\n approvals: NativeApprovals;\n pendingApprovalIds: Set<string | number>;\n withdrawnApprovals: Set<string | number>;\n};\n\nexport class CursorCliSession implements NativeSession {\n private closePromise?: Promise<void>;\n private active?: ActiveTurn;\n private failure?: AgentError;\n private first = true;\n private closed = false;\n\n constructor(\n private readonly rpc: CursorRpc,\n private readonly sessionId: string,\n private readonly selected: string,\n private readonly options: AdapterOptions,\n private readonly tools: CursorCliToolEndpoint,\n private readonly cleanup: () => Promise<void>,\n ) {\n rpc.onMessage = message => this.message(message);\n rpc.setFailureHandler(error => this.fail(error));\n tools.onFailure(error => this.fail(error));\n }\n\n async turn(prompt: string): Promise<string> {\n if (this.closed) throw new AgentError(\"SESSION_CLOSED\", \"The Cursor session is closed.\");\n if (this.failure) throw this.failure;\n if (this.active) throw new AgentError(\"SESSION_BUSY\", \"Cursor already has an active turn.\");\n let resolve!: (value: string) => void;\n let reject!: (error: Error) => void;\n let releaseAdmission!: () => void;\n let resolveNativeStopped!: () => void;\n const completion = new Promise<string>((accept, fail) => { resolve = accept; reject = fail; });\n const admission = new Promise<void>(accept => { releaseAdmission = accept; });\n const nativeStopped = new Promise<void>(accept => { resolveNativeStopped = accept; });\n void completion.catch(() => {});\n const active: ActiveTurn = {\n text: \"\", responseBytes: 0, responseTooLarge: false, completion, resolve, reject,\n terminal: false, submitted: false, cancelRequested: false,\n admission, releaseAdmission, nativeStopped, resolveNativeStopped,\n approvals: new NativeApprovals(), pendingApprovalIds: new Set(), withdrawnApprovals: new Set(),\n };\n this.active = active;\n try {\n await applyCursorCliModel(this.rpc, this.sessionId, this.selected);\n if (this.failure) throw this.failure;\n if (active.cancelRequested || this.closed) throw new AgentError(\"INTERRUPTED\", \"The Cursor turn was interrupted before submission.\");\n this.tools.beginTurn();\n const text = this.first && this.options.agent.instructions\n ? firstPrompt(this.options.agent.instructions, prompt)\n : prompt;\n active.submitted = true;\n active.releaseAdmission();\n const promptRequest = this.rpc.request(\"session/prompt\", { sessionId: this.sessionId, prompt: [{ type: \"text\", text }] });\n this.first = false;\n const result = await promptRequest;\n active.resolveNativeStopped();\n await this.finish(active, result, active.failure);\n } catch (error) {\n active.releaseAdmission();\n active.resolveNativeStopped();\n const failure = active.failure ?? (error instanceof CursorRpcNativeError\n ? new AgentError(\"HARNESS_ERROR\", \"Cursor could not complete the native prompt.\")\n : error instanceof AgentError ? error : new AgentError(\"HARNESS_ERROR\", \"Cursor could not complete the native prompt.\"));\n await this.finish(active, undefined, failure);\n }\n return await completion;\n }\n\n async steer(_prompt: string): Promise<void> {\n throw new AgentError(\"UNSUPPORTED_DELIVERY\", \"Cursor does not expose verified active steering.\");\n }\n\n async interrupt(): Promise<void> {\n const active = this.active;\n if (!active) return;\n active.stopTask ??= this.stop(active);\n return await active.stopTask;\n }\n\n async resume(): Promise<string> {\n throw new AgentError(\"RECOVERY_UNSUPPORTED\", \"Cursor recovery without a new prompt is unavailable.\");\n }\n\n close(): Promise<void> {\n this.closePromise ??= this.closeOwnedResources();\n return this.closePromise;\n }\n\n private async closeOwnedResources(): Promise<void> {\n this.closed = true;\n this.tools.stopAdmission();\n const active = this.active;\n if (active) {\n active.cancelRequested = true;\n active.failure ??= new AgentError(\"SESSION_CLOSED\", \"The Cursor session was closed during execution.\");\n this.retireApprovals(active);\n if (active.submitted && !active.terminal) {\n try { this.rpc.send({ method: \"session/cancel\", params: { sessionId: this.sessionId } }); } catch {}\n }\n }\n let closeFailure: unknown;\n try { await this.rpc.close(); } catch (error) { closeFailure = error; }\n await this.tools.close();\n await active?.completion.catch(() => {});\n if (closeFailure) throw closeFailure;\n await this.cleanup();\n }\n\n private message(message: CursorRpcEnvelope): void {\n const active = this.active;\n const params = asObject(message.params);\n if (message.method === \"$/cancel_request\" && active && (typeof params?.requestId === \"string\" || typeof params?.requestId === \"number\")) {\n if (active.pendingApprovalIds.delete(params.requestId)) {\n active.withdrawnApprovals.add(params.requestId);\n active.approvals.withdraw(params.requestId);\n }\n return;\n }\n if (message.id !== undefined) {\n if (active && active.submitted && !active.terminal && !active.cancelRequested && !active.failure\n && params?.sessionId === this.sessionId && message.method === \"session/request_permission\" && this.options.onApproval) {\n try {\n const request = cursorCliApproval(message.params);\n active.pendingApprovalIds.add(message.id);\n void active.approvals.request(message.id, request, this.options.onApproval).then(content => {\n if (this.active === active && !active.terminal && !active.cancelRequested && !active.failure && !active.withdrawnApprovals.has(message.id!)) {\n active.pendingApprovalIds.delete(message.id!);\n this.rpc.send({ id: message.id, result: { outcome: { outcome: \"selected\", optionId: content.decision } } });\n }\n }).catch(error => {\n if (!(error instanceof AgentError && error.code === \"REQUEST_EXPIRED\")) this.unsupportedRequest(message, active);\n }).finally(() => {\n active.pendingApprovalIds.delete(message.id!);\n active.withdrawnApprovals.delete(message.id!);\n });\n return;\n } catch {}\n }\n this.unsupportedRequest(message, active);\n return;\n }\n if (message.method !== \"session/update\") return;\n if (!params || params.sessionId !== this.sessionId || !active || active.terminal) return;\n const update = asObject(params.update);\n if (!update || typeof update.sessionUpdate !== \"string\") {\n this.fail(new AgentError(\"PROTOCOL_ERROR\", \"Cursor returned an invalid ACP session update.\"));\n return;\n }\n if (update.sessionUpdate === \"config_option_update\") {\n try { verifyCursorCliModelUpdate(update.configOptions, this.selected); }\n catch (error) {\n this.stopForModelFailure(active, error instanceof AgentError\n ? error\n : new AgentError(\"PROTOCOL_ERROR\", \"Cursor returned an invalid native model configuration.\"));\n }\n return;\n }\n if (update.sessionUpdate !== \"agent_message_chunk\") return;\n const content = asObject(update.content);\n if (content?.type !== \"text\" || typeof content.text !== \"string\") {\n this.fail(new AgentError(\"PROTOCOL_ERROR\", \"Cursor returned an invalid assistant message update.\"));\n return;\n }\n active.responseBytes += Buffer.byteLength(content.text, \"utf8\");\n if (active.responseBytes > 1024 * 1024) active.responseTooLarge = true;\n else active.text += content.text;\n }\n\n private async finish(active: ActiveTurn, value?: unknown, error?: AgentError): Promise<void> {\n if (active.terminal) return;\n active.terminal = true;\n this.retireApprovals(active);\n this.tools.stopAdmission();\n await this.tools.drain();\n if (this.active === active) this.active = undefined;\n if (error) return active.reject(error);\n const result = asObject(value);\n if (!result || typeof result.stopReason !== \"string\") {\n const failure = new AgentError(\"PROTOCOL_ERROR\", \"Cursor returned an invalid native terminal result.\");\n this.failure = failure;\n return active.reject(failure);\n }\n if (active.responseTooLarge) return active.reject(new AgentError(\"RESPONSE_TOO_LARGE\", \"Cursor response exceeds 1 MiB.\"));\n if (result.stopReason === \"end_turn\") return active.resolve(active.text);\n if (result.stopReason === \"cancelled\") return active.reject(new AgentError(\"INTERRUPTED\", \"The Cursor turn was interrupted.\"));\n active.reject(new AgentError(\"HARNESS_ERROR\", \"Cursor did not complete the native turn successfully.\"));\n }\n\n private async stop(active: ActiveTurn): Promise<void> {\n active.cancelRequested = true;\n this.retireApprovals(active);\n this.tools.stopAdmission();\n try {\n await bounded(active.admission, 2_000);\n if (active.submitted && !active.terminal) this.rpc.send({ method: \"session/cancel\", params: { sessionId: this.sessionId } });\n const callbacks = this.tools.drain();\n await bounded(active.nativeStopped, 3_000);\n await callbacks;\n await active.completion.catch(() => {});\n } catch {\n const failure = new AgentError(\"CANCELLATION_FAILED\", \"Cursor did not confirm that native execution stopped.\");\n active.failure = failure;\n this.failure = failure;\n this.tools.stopAdmission();\n await this.rpc.close().catch(() => {});\n await this.tools.drain();\n await this.finish(active, undefined, failure);\n throw failure;\n }\n }\n\n private fail(error: AgentError): void {\n this.failure ??= error;\n this.tools.stopAdmission();\n const active = this.active;\n if (active) this.retireApprovals(active);\n if (active) void this.finish(active, undefined, this.failure);\n void this.rpc.close().catch(() => {});\n }\n\n private stopForModelFailure(active: ActiveTurn, failure: AgentError): void {\n if (active.failure) return;\n active.failure = failure;\n this.failure ??= failure;\n this.tools.stopAdmission();\n this.retireApprovals(active);\n active.stopTask ??= this.stop(active);\n void active.stopTask.catch(() => {});\n }\n\n private unsupportedRequest(message: CursorRpcEnvelope, active: ActiveTurn | undefined): void {\n if (active && message.id !== undefined && active.pendingApprovalIds.has(message.id)) this.retireApproval(active, message.id);\n else this.respondCancelled(message.id);\n const failure = new AgentError(\"INPUT_REQUIRED\", \"Cursor requested unsupported interactive input; no permission was granted.\");\n if (!active) return this.fail(failure);\n if (active.failure) return;\n active.failure = failure;\n this.retireApprovals(active);\n this.tools.stopAdmission();\n active.stopTask ??= this.stop(active);\n void active.stopTask.catch(() => {});\n }\n\n private retireApprovals(active: ActiveTurn): void {\n for (const id of [...active.pendingApprovalIds]) this.retireApproval(active, id);\n active.approvals.clear();\n }\n\n private retireApproval(active: ActiveTurn, id: string | number): void {\n if (!active.pendingApprovalIds.delete(id)) return;\n active.approvals.withdraw(id);\n if (!active.withdrawnApprovals.has(id)) this.respondCancelled(id);\n }\n\n private respondCancelled(id: string | number | undefined): void {\n if (id === undefined) return;\n try { this.rpc.send({ id, result: { outcome: { outcome: \"cancelled\" } } }); } catch {}\n }\n}\n\nfunction firstPrompt(instructions: string, prompt: string): string {\n return `<subharness-instructions>\\n${instructions}\\n</subharness-instructions>\\n\\n${prompt}`;\n}\n\nfunction asObject(value: unknown): Record<string, any> | undefined {\n return value && typeof value === \"object\" && !Array.isArray(value) ? value as Record<string, any> : undefined;\n}\n\nasync function bounded<T>(promise: Promise<T>, milliseconds: number): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n try {\n return await Promise.race([promise, new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(new Error(\"timeout\")), milliseconds);\n timer.unref();\n })]);\n } finally { clearTimeout(timer); }\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AgentError } from "../errors.js";
|
|
2
|
+
import type { ExecutionDefinition } from "./types.js";
|
|
3
|
+
export type CursorCliToolEndpoint = {
|
|
4
|
+
readonly descriptor?: {
|
|
5
|
+
readonly type: "http";
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
readonly headers: {
|
|
9
|
+
name: string;
|
|
10
|
+
value: string;
|
|
11
|
+
}[];
|
|
12
|
+
};
|
|
13
|
+
waitReady(timeoutMs: number): Promise<void>;
|
|
14
|
+
onFailure(handler: (error: AgentError) => void): void;
|
|
15
|
+
beginTurn(): void;
|
|
16
|
+
stopAdmission(): void;
|
|
17
|
+
drain(): Promise<void>;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
export declare function startCursorCliTools(agent: Pick<ExecutionDefinition, "tools">): Promise<CursorCliToolEndpoint>;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5
|
+
import { AgentError } from "../errors.js";
|
|
6
|
+
import { executeTool, toolJsonSchema } from "../sdk/tools.js";
|
|
7
|
+
import { mcpToolContent } from "./mcp-tool-content.js";
|
|
8
|
+
const MAX_MCP_BODY_BYTES = 16 * 1024 * 1024;
|
|
9
|
+
export async function startCursorCliTools(agent) {
|
|
10
|
+
const definitions = Object.entries(agent.tools ?? {});
|
|
11
|
+
if (definitions.length === 0)
|
|
12
|
+
return emptyEndpoint();
|
|
13
|
+
for (const [, definition] of definitions)
|
|
14
|
+
toolJsonSchema(definition);
|
|
15
|
+
const token = randomBytes(32).toString("base64url");
|
|
16
|
+
const namespace = `subharness_${randomUUID().replaceAll("-", "")}`;
|
|
17
|
+
const pending = new Set();
|
|
18
|
+
let accepting = false;
|
|
19
|
+
let endpointFailure;
|
|
20
|
+
let failureHandler;
|
|
21
|
+
let initialized = false;
|
|
22
|
+
let resolveReady;
|
|
23
|
+
let rejectReady;
|
|
24
|
+
const readiness = new Promise((resolve, reject) => { resolveReady = resolve; rejectReady = reject; });
|
|
25
|
+
void readiness.catch(() => { });
|
|
26
|
+
const mcp = new McpServer({ name: namespace, version: "1.0.0" });
|
|
27
|
+
mcp.server.oninitialized = () => { initialized = true; resolveReady(); };
|
|
28
|
+
for (const [name, definition] of definitions) {
|
|
29
|
+
mcp.registerTool(name, { description: definition.description, inputSchema: definition.inputSchema }, async (input) => {
|
|
30
|
+
if (!accepting)
|
|
31
|
+
return toolError("The task is not accepting tool calls.");
|
|
32
|
+
const execution = executeTool(definition, input);
|
|
33
|
+
pending.add(execution);
|
|
34
|
+
try {
|
|
35
|
+
return { content: mcpToolContent(await execution) };
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
return toolError(error instanceof AgentError ? `Tool failed (${error.code}).` : "The custom tool failed.");
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
pending.delete(execution);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: randomUUID, enableJsonResponse: true });
|
|
46
|
+
await mcp.connect(transport);
|
|
47
|
+
let http;
|
|
48
|
+
let closing;
|
|
49
|
+
let draining;
|
|
50
|
+
const drain = () => {
|
|
51
|
+
accepting = false;
|
|
52
|
+
return draining ??= Promise.allSettled([...pending]).then(() => { }).finally(() => { draining = undefined; });
|
|
53
|
+
};
|
|
54
|
+
const closeEndpoint = () => closing ??= (async () => {
|
|
55
|
+
accepting = false;
|
|
56
|
+
rejectReady(new AgentError("HARNESS_FAILED", "The private Cursor tool endpoint closed before initialization."));
|
|
57
|
+
await drain();
|
|
58
|
+
await mcp.close().catch(() => { });
|
|
59
|
+
if (http)
|
|
60
|
+
await closeHttp(http);
|
|
61
|
+
})();
|
|
62
|
+
const failEndpoint = () => {
|
|
63
|
+
if (endpointFailure || closing)
|
|
64
|
+
return;
|
|
65
|
+
endpointFailure = new AgentError("HARNESS_FAILED", "The private Cursor tool endpoint failed.");
|
|
66
|
+
accepting = false;
|
|
67
|
+
rejectReady(endpointFailure);
|
|
68
|
+
failureHandler?.(endpointFailure);
|
|
69
|
+
void closeEndpoint();
|
|
70
|
+
};
|
|
71
|
+
mcp.server.onerror = failEndpoint;
|
|
72
|
+
mcp.server.onclose = failEndpoint;
|
|
73
|
+
try {
|
|
74
|
+
http = createServer({ maxHeaderSize: 16 * 1024 }, (request, response) => {
|
|
75
|
+
if (request.url !== "/mcp" || !authorized(request.headers.authorization, token)) {
|
|
76
|
+
response.writeHead(401, { "content-type": "application/json" });
|
|
77
|
+
response.end(JSON.stringify({ error: "unauthorized" }));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
void handleRequest(transport, request, response).catch(() => {
|
|
81
|
+
if (!response.headersSent)
|
|
82
|
+
response.writeHead(500);
|
|
83
|
+
response.end();
|
|
84
|
+
failEndpoint();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
await new Promise((resolve, reject) => {
|
|
88
|
+
http.once("error", reject);
|
|
89
|
+
http.listen(0, "127.0.0.1", () => {
|
|
90
|
+
http.off("error", reject);
|
|
91
|
+
http.on("error", failEndpoint);
|
|
92
|
+
resolve();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
await closeEndpoint();
|
|
98
|
+
throw new AgentError("HARNESS_FAILED", "The private Cursor tool endpoint could not start.");
|
|
99
|
+
}
|
|
100
|
+
const address = http.address();
|
|
101
|
+
if (!address || typeof address === "string") {
|
|
102
|
+
await closeEndpoint();
|
|
103
|
+
throw new AgentError("HARNESS_FAILED", "The private Cursor tool endpoint did not bind to loopback.");
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
async waitReady(timeoutMs) {
|
|
107
|
+
if (endpointFailure)
|
|
108
|
+
throw endpointFailure;
|
|
109
|
+
if (closing)
|
|
110
|
+
throw new AgentError("HARNESS_FAILED", "The private Cursor tool endpoint is closed.");
|
|
111
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
|
|
112
|
+
throw new AgentError("HARNESS_FAILED", "Cursor tool initialization requires a bounded timeout.");
|
|
113
|
+
const timer = setTimeout(failEndpoint, timeoutMs);
|
|
114
|
+
try {
|
|
115
|
+
await readiness;
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
descriptor: { type: "http", name: namespace, url: `http://127.0.0.1:${address.port}/mcp`, headers: [{ name: "Authorization", value: `Bearer ${token}` }] },
|
|
122
|
+
onFailure(handler) { failureHandler = handler; if (endpointFailure)
|
|
123
|
+
handler(endpointFailure); },
|
|
124
|
+
beginTurn() {
|
|
125
|
+
if (!initialized || closing || draining || endpointFailure)
|
|
126
|
+
throw new AgentError("HARNESS_FAILED", "The private Cursor tool endpoint is unavailable.");
|
|
127
|
+
accepting = true;
|
|
128
|
+
},
|
|
129
|
+
stopAdmission() { accepting = false; },
|
|
130
|
+
drain,
|
|
131
|
+
close: closeEndpoint,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
async function handleRequest(transport, request, response) {
|
|
135
|
+
if (request.method !== "POST")
|
|
136
|
+
return transport.handleRequest(request, response);
|
|
137
|
+
const chunks = [];
|
|
138
|
+
let bytes = 0;
|
|
139
|
+
for await (const chunk of request.iterator({ destroyOnReturn: false })) {
|
|
140
|
+
bytes += chunk.length;
|
|
141
|
+
if (bytes > MAX_MCP_BODY_BYTES) {
|
|
142
|
+
response.writeHead(413, { connection: "close" });
|
|
143
|
+
response.end();
|
|
144
|
+
request.resume();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
chunks.push(chunk);
|
|
148
|
+
}
|
|
149
|
+
let body;
|
|
150
|
+
try {
|
|
151
|
+
body = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
response.writeHead(400);
|
|
155
|
+
response.end();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
await transport.handleRequest(request, response, body);
|
|
159
|
+
}
|
|
160
|
+
function authorized(header, token) {
|
|
161
|
+
if (typeof header !== "string")
|
|
162
|
+
return false;
|
|
163
|
+
const expected = Buffer.from(`Bearer ${token}`);
|
|
164
|
+
const actual = Buffer.from(header);
|
|
165
|
+
return actual.length === expected.length && timingSafeEqual(actual, expected);
|
|
166
|
+
}
|
|
167
|
+
function toolError(text) {
|
|
168
|
+
return { isError: true, content: [{ type: "text", text }] };
|
|
169
|
+
}
|
|
170
|
+
function emptyEndpoint() {
|
|
171
|
+
return { async waitReady() { }, onFailure() { }, beginTurn() { }, stopAdmission() { }, async drain() { }, async close() { } };
|
|
172
|
+
}
|
|
173
|
+
async function closeHttp(server) {
|
|
174
|
+
if (!server.listening)
|
|
175
|
+
return;
|
|
176
|
+
await new Promise(resolve => {
|
|
177
|
+
server.close(() => resolve());
|
|
178
|
+
server.closeAllConnections();
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=cursor-cli-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-cli-tools.js","sourceRoot":"","sources":["../../src/adapters/cursor-cli-tools.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes, randomUUID, timingSafeEqual } from \"node:crypto\";\nimport { createServer, type IncomingMessage, type ServerResponse, type Server as HttpServer } from \"node:http\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { AgentError } from \"../errors.js\";\nimport { executeTool, toolJsonSchema } from \"../sdk/tools.js\";\nimport type { ExecutionDefinition } from \"./types.js\";\nimport { mcpToolContent } from \"./mcp-tool-content.js\";\n\nconst MAX_MCP_BODY_BYTES = 16 * 1024 * 1024;\n\nexport type CursorCliToolEndpoint = {\n readonly descriptor?: { readonly type: \"http\"; readonly name: string; readonly url: string; readonly headers: { name: string; value: string }[] };\n waitReady(timeoutMs: number): Promise<void>;\n onFailure(handler: (error: AgentError) => void): void;\n beginTurn(): void;\n stopAdmission(): void;\n drain(): Promise<void>;\n close(): Promise<void>;\n};\n\nexport async function startCursorCliTools(agent: Pick<ExecutionDefinition, \"tools\">): Promise<CursorCliToolEndpoint> {\n const definitions = Object.entries(agent.tools ?? {});\n if (definitions.length === 0) return emptyEndpoint();\n for (const [, definition] of definitions) toolJsonSchema(definition);\n\n const token = randomBytes(32).toString(\"base64url\");\n const namespace = `subharness_${randomUUID().replaceAll(\"-\", \"\")}`;\n const pending = new Set<Promise<unknown>>();\n let accepting = false;\n let endpointFailure: AgentError | undefined;\n let failureHandler: ((error: AgentError) => void) | undefined;\n let initialized = false;\n let resolveReady!: () => void;\n let rejectReady!: (error: AgentError) => void;\n const readiness = new Promise<void>((resolve, reject) => { resolveReady = resolve; rejectReady = reject; });\n void readiness.catch(() => {});\n const mcp = new McpServer({ name: namespace, version: \"1.0.0\" });\n mcp.server.oninitialized = () => { initialized = true; resolveReady(); };\n for (const [name, definition] of definitions) {\n mcp.registerTool(name, { description: definition.description, inputSchema: definition.inputSchema }, async (input: unknown) => {\n if (!accepting) return toolError(\"The task is not accepting tool calls.\");\n const execution = executeTool(definition, input);\n pending.add(execution);\n try {\n return { content: mcpToolContent(await execution) };\n } catch (error) {\n return toolError(error instanceof AgentError ? `Tool failed (${error.code}).` : \"The custom tool failed.\");\n } finally {\n pending.delete(execution);\n }\n });\n }\n\n const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: randomUUID, enableJsonResponse: true });\n await mcp.connect(transport);\n let http: HttpServer | undefined;\n let closing: Promise<void> | undefined;\n let draining: Promise<void> | undefined;\n const drain = (): Promise<void> => {\n accepting = false;\n return draining ??= Promise.allSettled([...pending]).then(() => {}).finally(() => { draining = undefined; });\n };\n const closeEndpoint = (): Promise<void> => closing ??= (async () => {\n accepting = false;\n rejectReady(new AgentError(\"HARNESS_FAILED\", \"The private Cursor tool endpoint closed before initialization.\"));\n await drain();\n await mcp.close().catch(() => {});\n if (http) await closeHttp(http);\n })();\n const failEndpoint = () => {\n if (endpointFailure || closing) return;\n endpointFailure = new AgentError(\"HARNESS_FAILED\", \"The private Cursor tool endpoint failed.\");\n accepting = false;\n rejectReady(endpointFailure);\n failureHandler?.(endpointFailure);\n void closeEndpoint();\n };\n mcp.server.onerror = failEndpoint;\n mcp.server.onclose = failEndpoint;\n try {\n http = createServer({ maxHeaderSize: 16 * 1024 }, (request, response) => {\n if (request.url !== \"/mcp\" || !authorized(request.headers.authorization, token)) {\n response.writeHead(401, { \"content-type\": \"application/json\" });\n response.end(JSON.stringify({ error: \"unauthorized\" }));\n return;\n }\n void handleRequest(transport, request, response).catch(() => {\n if (!response.headersSent) response.writeHead(500);\n response.end();\n failEndpoint();\n });\n });\n await new Promise<void>((resolve, reject) => {\n http!.once(\"error\", reject);\n http!.listen(0, \"127.0.0.1\", () => {\n http!.off(\"error\", reject);\n http!.on(\"error\", failEndpoint);\n resolve();\n });\n });\n } catch {\n await closeEndpoint();\n throw new AgentError(\"HARNESS_FAILED\", \"The private Cursor tool endpoint could not start.\");\n }\n const address = http.address();\n if (!address || typeof address === \"string\") {\n await closeEndpoint();\n throw new AgentError(\"HARNESS_FAILED\", \"The private Cursor tool endpoint did not bind to loopback.\");\n }\n\n return {\n async waitReady(timeoutMs) {\n if (endpointFailure) throw endpointFailure;\n if (closing) throw new AgentError(\"HARNESS_FAILED\", \"The private Cursor tool endpoint is closed.\");\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) throw new AgentError(\"HARNESS_FAILED\", \"Cursor tool initialization requires a bounded timeout.\");\n const timer = setTimeout(failEndpoint, timeoutMs);\n try { await readiness; }\n finally { clearTimeout(timer); }\n },\n descriptor: { type: \"http\", name: namespace, url: `http://127.0.0.1:${address.port}/mcp`, headers: [{ name: \"Authorization\", value: `Bearer ${token}` }] },\n onFailure(handler) { failureHandler = handler; if (endpointFailure) handler(endpointFailure); },\n beginTurn() {\n if (!initialized || closing || draining || endpointFailure) throw new AgentError(\"HARNESS_FAILED\", \"The private Cursor tool endpoint is unavailable.\");\n accepting = true;\n },\n stopAdmission() { accepting = false; },\n drain,\n close: closeEndpoint,\n };\n}\n\nasync function handleRequest(transport: StreamableHTTPServerTransport, request: IncomingMessage, response: ServerResponse): Promise<void> {\n if (request.method !== \"POST\") return transport.handleRequest(request, response);\n const chunks: Buffer[] = [];\n let bytes = 0;\n for await (const chunk of request.iterator({ destroyOnReturn: false })) {\n bytes += chunk.length;\n if (bytes > MAX_MCP_BODY_BYTES) {\n response.writeHead(413, { connection: \"close\" });\n response.end();\n request.resume();\n return;\n }\n chunks.push(chunk);\n }\n let body: unknown;\n try { body = JSON.parse(Buffer.concat(chunks).toString(\"utf8\")); }\n catch { response.writeHead(400); response.end(); return; }\n await transport.handleRequest(request, response, body);\n}\n\nfunction authorized(header: string | undefined, token: string): boolean {\n if (typeof header !== \"string\") return false;\n const expected = Buffer.from(`Bearer ${token}`);\n const actual = Buffer.from(header);\n return actual.length === expected.length && timingSafeEqual(actual, expected);\n}\n\nfunction toolError(text: string) {\n return { isError: true, content: [{ type: \"text\" as const, text }] };\n}\n\nfunction emptyEndpoint(): CursorCliToolEndpoint {\n return { async waitReady() {}, onFailure() {}, beginTurn() {}, stopAdmission() {}, async drain() {}, async close() {} };\n}\n\nasync function closeHttp(server: HttpServer): Promise<void> {\n if (!server.listening) return;\n await new Promise<void>(resolve => {\n server.close(() => resolve());\n server.closeAllConnections();\n });\n}\n"]}
|