opencode-ext-connector 0.3.0
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/CHANGELOG.md +39 -0
- package/LICENSE +29 -0
- package/README.md +207 -0
- package/THIRD_PARTY_NOTICES.md +201 -0
- package/dist/catalog/parse-ids.d.ts +2 -0
- package/dist/catalog/parse-ids.js +49 -0
- package/dist/core/adapter.d.ts +16 -0
- package/dist/core/adapter.js +20 -0
- package/dist/core/clock.d.ts +7 -0
- package/dist/core/clock.js +1 -0
- package/dist/core/deadline.d.ts +13 -0
- package/dist/core/deadline.js +54 -0
- package/dist/core/errors.d.ts +57 -0
- package/dist/core/errors.js +81 -0
- package/dist/core/health.d.ts +22 -0
- package/dist/core/health.js +30 -0
- package/dist/core/http.d.ts +28 -0
- package/dist/core/http.js +1 -0
- package/dist/core/ids.d.ts +7 -0
- package/dist/core/ids.js +23 -0
- package/dist/core/lifecycle.d.ts +4 -0
- package/dist/core/lifecycle.js +16 -0
- package/dist/core/logger.d.ts +21 -0
- package/dist/core/logger.js +61 -0
- package/dist/core/models.d.ts +41 -0
- package/dist/core/models.js +59 -0
- package/dist/core/options.d.ts +31 -0
- package/dist/core/options.js +62 -0
- package/dist/core/process.d.ts +20 -0
- package/dist/core/process.js +1 -0
- package/dist/http/fetch-transport.d.ts +2 -0
- package/dist/http/fetch-transport.js +75 -0
- package/dist/http/package-version.d.ts +7 -0
- package/dist/http/package-version.js +46 -0
- package/dist/http/read-body.d.ts +9 -0
- package/dist/http/read-body.js +22 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/logging/logger.d.ts +4 -0
- package/dist/logging/logger.js +11 -0
- package/dist/opencode/auth-store.d.ts +19 -0
- package/dist/opencode/auth-store.js +107 -0
- package/dist/opencode/beta-api.d.ts +3 -0
- package/dist/opencode/beta-api.js +1 -0
- package/dist/opencode/catalog-bridge.d.ts +3 -0
- package/dist/opencode/catalog-bridge.js +85 -0
- package/dist/opencode/health-refresh.d.ts +16 -0
- package/dist/opencode/health-refresh.js +49 -0
- package/dist/opencode/host-options.d.ts +2 -0
- package/dist/opencode/host-options.js +49 -0
- package/dist/opencode/language-factory.d.ts +13 -0
- package/dist/opencode/language-factory.js +37 -0
- package/dist/opencode/ollama-probe.d.ts +2 -0
- package/dist/opencode/ollama-probe.js +15 -0
- package/dist/opencode/ollama-production.d.ts +4 -0
- package/dist/opencode/ollama-production.js +10 -0
- package/dist/opencode/plugin.d.ts +33 -0
- package/dist/opencode/plugin.js +40 -0
- package/dist/opencode/process-exit.d.ts +4 -0
- package/dist/opencode/process-exit.js +22 -0
- package/dist/opencode/provider-entry.d.ts +25 -0
- package/dist/opencode/provider-entry.js +1 -0
- package/dist/opencode/providers.d.ts +16 -0
- package/dist/opencode/providers.js +140 -0
- package/dist/opencode/register-integrations.d.ts +3 -0
- package/dist/opencode/register-integrations.js +8 -0
- package/dist/opencode/reload.d.ts +9 -0
- package/dist/opencode/reload.js +30 -0
- package/dist/opencode/v1-anthropic-auth.d.ts +9 -0
- package/dist/opencode/v1-anthropic-auth.js +55 -0
- package/dist/opencode/v1-catalog.d.ts +13 -0
- package/dist/opencode/v1-catalog.js +80 -0
- package/dist/opencode/v1-language.d.ts +3 -0
- package/dist/opencode/v1-language.js +64 -0
- package/dist/opencode/v1-module.d.ts +23 -0
- package/dist/opencode/v1-module.js +74 -0
- package/dist/opencode/v1-session-auth.d.ts +5 -0
- package/dist/opencode/v1-session-auth.js +98 -0
- package/dist/providers/claude/adapter.d.ts +7 -0
- package/dist/providers/claude/adapter.js +37 -0
- package/dist/providers/claude/atomic-private-file.d.ts +13 -0
- package/dist/providers/claude/atomic-private-file.js +94 -0
- package/dist/providers/claude/auth-json.d.ts +2 -0
- package/dist/providers/claude/auth-json.js +14 -0
- package/dist/providers/claude/auth.d.ts +24 -0
- package/dist/providers/claude/auth.js +200 -0
- package/dist/providers/claude/cli-version.d.ts +10 -0
- package/dist/providers/claude/cli-version.js +36 -0
- package/dist/providers/claude/compat-betas.d.ts +5 -0
- package/dist/providers/claude/compat-betas.js +54 -0
- package/dist/providers/claude/compat-config.d.ts +8 -0
- package/dist/providers/claude/compat-config.js +31 -0
- package/dist/providers/claude/compat-request.d.ts +12 -0
- package/dist/providers/claude/compat-request.js +151 -0
- package/dist/providers/claude/compat-response.d.ts +1 -0
- package/dist/providers/claude/compat-response.js +54 -0
- package/dist/providers/claude/compat-signing.d.ts +9 -0
- package/dist/providers/claude/compat-signing.js +24 -0
- package/dist/providers/claude/compat-transform.d.ts +3 -0
- package/dist/providers/claude/compat-transform.js +224 -0
- package/dist/providers/claude/credentials.d.ts +7 -0
- package/dist/providers/claude/credentials.js +37 -0
- package/dist/providers/claude/emit-stream.d.ts +2 -0
- package/dist/providers/claude/emit-stream.js +62 -0
- package/dist/providers/claude/keychain-account.d.ts +1 -0
- package/dist/providers/claude/keychain-account.js +14 -0
- package/dist/providers/claude/language-model.d.ts +8 -0
- package/dist/providers/claude/language-model.js +109 -0
- package/dist/providers/claude/models.d.ts +9 -0
- package/dist/providers/claude/models.js +24 -0
- package/dist/providers/claude/prompt.d.ts +42 -0
- package/dist/providers/claude/prompt.js +134 -0
- package/dist/providers/claude/refresh.d.ts +19 -0
- package/dist/providers/claude/refresh.js +85 -0
- package/dist/providers/claude/sse-convert.d.ts +6 -0
- package/dist/providers/claude/sse-convert.js +74 -0
- package/dist/providers/claude/sse.d.ts +71 -0
- package/dist/providers/claude/sse.js +123 -0
- package/dist/providers/claude/transform.d.ts +32 -0
- package/dist/providers/claude/transform.js +52 -0
- package/dist/providers/claude/writeback.d.ts +18 -0
- package/dist/providers/claude/writeback.js +140 -0
- package/dist/providers/command-code/adapter.d.ts +7 -0
- package/dist/providers/command-code/adapter.js +37 -0
- package/dist/providers/command-code/auth.d.ts +5 -0
- package/dist/providers/command-code/auth.js +88 -0
- package/dist/providers/command-code/cli-version.d.ts +10 -0
- package/dist/providers/command-code/cli-version.js +36 -0
- package/dist/providers/command-code/emit-stream.d.ts +2 -0
- package/dist/providers/command-code/emit-stream.js +226 -0
- package/dist/providers/command-code/errors.d.ts +33 -0
- package/dist/providers/command-code/errors.js +105 -0
- package/dist/providers/command-code/language-model.d.ts +15 -0
- package/dist/providers/command-code/language-model.js +203 -0
- package/dist/providers/command-code/models.d.ts +3 -0
- package/dist/providers/command-code/models.js +18 -0
- package/dist/providers/command-code/record-stream.d.ts +1 -0
- package/dist/providers/command-code/record-stream.js +61 -0
- package/dist/providers/command-code/request-lifecycle.d.ts +8 -0
- package/dist/providers/command-code/request-lifecycle.js +33 -0
- package/dist/providers/command-code/request.d.ts +14 -0
- package/dist/providers/command-code/request.js +167 -0
- package/dist/providers/command-code/session.d.ts +5 -0
- package/dist/providers/command-code/session.js +9 -0
- package/dist/providers/cursor/adapter.d.ts +7 -0
- package/dist/providers/cursor/adapter.js +37 -0
- package/dist/providers/cursor/auth.d.ts +6 -0
- package/dist/providers/cursor/auth.js +56 -0
- package/dist/providers/cursor/blob-store.d.ts +26 -0
- package/dist/providers/cursor/blob-store.js +119 -0
- package/dist/providers/cursor/bridge-client.d.ts +33 -0
- package/dist/providers/cursor/bridge-client.js +200 -0
- package/dist/providers/cursor/bridge-event-sanitize.d.ts +3 -0
- package/dist/providers/cursor/bridge-event-sanitize.js +49 -0
- package/dist/providers/cursor/bridge-limits.d.ts +3 -0
- package/dist/providers/cursor/bridge-limits.js +10 -0
- package/dist/providers/cursor/bridge-process.d.ts +45 -0
- package/dist/providers/cursor/bridge-process.js +211 -0
- package/dist/providers/cursor/bridge-protocol.d.ts +68 -0
- package/dist/providers/cursor/bridge-protocol.js +49 -0
- package/dist/providers/cursor/bridge-serialize.d.ts +3 -0
- package/dist/providers/cursor/bridge-serialize.js +53 -0
- package/dist/providers/cursor/bridge-wire.d.ts +3 -0
- package/dist/providers/cursor/bridge-wire.js +163 -0
- package/dist/providers/cursor/checkpoint-store.d.ts +28 -0
- package/dist/providers/cursor/checkpoint-store.js +94 -0
- package/dist/providers/cursor/connect-frame-stream.d.ts +19 -0
- package/dist/providers/cursor/connect-frame-stream.js +160 -0
- package/dist/providers/cursor/connect-frame.d.ts +31 -0
- package/dist/providers/cursor/connect-frame.js +121 -0
- package/dist/providers/cursor/credentials.d.ts +5 -0
- package/dist/providers/cursor/credentials.js +22 -0
- package/dist/providers/cursor/direct-run-types.d.ts +33 -0
- package/dist/providers/cursor/direct-run-types.js +1 -0
- package/dist/providers/cursor/direct-run.d.ts +5 -0
- package/dist/providers/cursor/direct-run.js +213 -0
- package/dist/providers/cursor/direct-runtime.d.ts +23 -0
- package/dist/providers/cursor/direct-runtime.js +111 -0
- package/dist/providers/cursor/direct-stream.d.ts +28 -0
- package/dist/providers/cursor/direct-stream.js +190 -0
- package/dist/providers/cursor/exec-reply.d.ts +13 -0
- package/dist/providers/cursor/exec-reply.js +102 -0
- package/dist/providers/cursor/h2-bridge-response.d.ts +9 -0
- package/dist/providers/cursor/h2-bridge-response.js +51 -0
- package/dist/providers/cursor/h2-bridge-session.d.ts +24 -0
- package/dist/providers/cursor/h2-bridge-session.js +197 -0
- package/dist/providers/cursor/h2-bridge.d.ts +2 -0
- package/dist/providers/cursor/h2-bridge.js +12865 -0
- package/dist/providers/cursor/http2.d.ts +18 -0
- package/dist/providers/cursor/http2.js +144 -0
- package/dist/providers/cursor/interaction-reply.d.ts +6 -0
- package/dist/providers/cursor/interaction-reply.js +52 -0
- package/dist/providers/cursor/language-generate.d.ts +2 -0
- package/dist/providers/cursor/language-generate.js +35 -0
- package/dist/providers/cursor/language-model.d.ts +13 -0
- package/dist/providers/cursor/language-model.js +77 -0
- package/dist/providers/cursor/legacy-stream.d.ts +11 -0
- package/dist/providers/cursor/legacy-stream.js +115 -0
- package/dist/providers/cursor/mcp-tools.d.ts +7 -0
- package/dist/providers/cursor/mcp-tools.js +15 -0
- package/dist/providers/cursor/models.d.ts +14 -0
- package/dist/providers/cursor/models.js +116 -0
- package/dist/providers/cursor/ndjson.d.ts +3 -0
- package/dist/providers/cursor/ndjson.js +100 -0
- package/dist/providers/cursor/prompt.d.ts +4 -0
- package/dist/providers/cursor/prompt.js +142 -0
- package/dist/providers/cursor/proto/checkpoint.d.ts +35 -0
- package/dist/providers/cursor/proto/checkpoint.js +114 -0
- package/dist/providers/cursor/proto/context.d.ts +64 -0
- package/dist/providers/cursor/proto/context.js +178 -0
- package/dist/providers/cursor/proto/errors.d.ts +16 -0
- package/dist/providers/cursor/proto/errors.js +26 -0
- package/dist/providers/cursor/proto/exec-control.d.ts +20 -0
- package/dist/providers/cursor/proto/exec-control.js +54 -0
- package/dist/providers/cursor/proto/exec.d.ts +47 -0
- package/dist/providers/cursor/proto/exec.js +154 -0
- package/dist/providers/cursor/proto/fields.d.ts +22 -0
- package/dist/providers/cursor/proto/fields.js +97 -0
- package/dist/providers/cursor/proto/interaction-query.d.ts +15 -0
- package/dist/providers/cursor/proto/interaction-query.js +79 -0
- package/dist/providers/cursor/proto/interaction-tool-update.d.ts +10 -0
- package/dist/providers/cursor/proto/interaction-tool-update.js +58 -0
- package/dist/providers/cursor/proto/interaction-turn-ended.d.ts +6 -0
- package/dist/providers/cursor/proto/interaction-turn-ended.js +18 -0
- package/dist/providers/cursor/proto/interaction.d.ts +65 -0
- package/dist/providers/cursor/proto/interaction.js +185 -0
- package/dist/providers/cursor/proto/kv.d.ts +25 -0
- package/dist/providers/cursor/proto/kv.js +109 -0
- package/dist/providers/cursor/proto/mcp-result.d.ts +40 -0
- package/dist/providers/cursor/proto/mcp-result.js +150 -0
- package/dist/providers/cursor/proto/mcp.d.ts +19 -0
- package/dist/providers/cursor/proto/mcp.js +78 -0
- package/dist/providers/cursor/proto/model.d.ts +11 -0
- package/dist/providers/cursor/proto/model.js +36 -0
- package/dist/providers/cursor/proto/output-location.d.ts +7 -0
- package/dist/providers/cursor/proto/output-location.js +53 -0
- package/dist/providers/cursor/proto/prewarm.d.ts +16 -0
- package/dist/providers/cursor/proto/prewarm.js +68 -0
- package/dist/providers/cursor/proto/request-context.d.ts +16 -0
- package/dist/providers/cursor/proto/request-context.js +58 -0
- package/dist/providers/cursor/proto/request.d.ts +44 -0
- package/dist/providers/cursor/proto/request.js +111 -0
- package/dist/providers/cursor/proto/run-request.d.ts +19 -0
- package/dist/providers/cursor/proto/run-request.js +68 -0
- package/dist/providers/cursor/proto/server.d.ts +35 -0
- package/dist/providers/cursor/proto/server.js +93 -0
- package/dist/providers/cursor/proto/unknown-field.d.ts +1 -0
- package/dist/providers/cursor/proto/unknown-field.js +22 -0
- package/dist/providers/cursor/proto-value.d.ts +3 -0
- package/dist/providers/cursor/proto-value.js +170 -0
- package/dist/providers/cursor/proto-wire.d.ts +22 -0
- package/dist/providers/cursor/proto-wire.js +224 -0
- package/dist/providers/cursor/protocol-failure.d.ts +18 -0
- package/dist/providers/cursor/protocol-failure.js +45 -0
- package/dist/providers/cursor/recovery.d.ts +48 -0
- package/dist/providers/cursor/recovery.js +102 -0
- package/dist/providers/cursor/request-blob-ownership.d.ts +15 -0
- package/dist/providers/cursor/request-blob-ownership.js +39 -0
- package/dist/providers/cursor/request-build.d.ts +18 -0
- package/dist/providers/cursor/request-build.js +123 -0
- package/dist/providers/cursor/request-history.d.ts +32 -0
- package/dist/providers/cursor/request-history.js +163 -0
- package/dist/providers/cursor/request-input.d.ts +75 -0
- package/dist/providers/cursor/request-input.js +123 -0
- package/dist/providers/cursor/run-events.d.ts +15 -0
- package/dist/providers/cursor/run-events.js +43 -0
- package/dist/providers/cursor/run-session-expiry.d.ts +15 -0
- package/dist/providers/cursor/run-session-expiry.js +21 -0
- package/dist/providers/cursor/run-session.d.ts +50 -0
- package/dist/providers/cursor/run-session.js +205 -0
- package/dist/providers/cursor/run.d.ts +9 -0
- package/dist/providers/cursor/run.js +151 -0
- package/dist/providers/cursor/runner.d.ts +1 -0
- package/dist/providers/cursor/runner.js +55 -0
- package/dist/providers/cursor/server-dispatch-types.d.ts +83 -0
- package/dist/providers/cursor/server-dispatch-types.js +1 -0
- package/dist/providers/cursor/server-dispatch.d.ts +3 -0
- package/dist/providers/cursor/server-dispatch.js +178 -0
- package/dist/providers/cursor/session-state.d.ts +21 -0
- package/dist/providers/cursor/session-state.js +118 -0
- package/dist/providers/cursor/session.d.ts +1 -0
- package/dist/providers/cursor/session.js +26 -0
- package/dist/providers/cursor/settle-cleanup.d.ts +1 -0
- package/dist/providers/cursor/settle-cleanup.js +11 -0
- package/dist/providers/cursor/stream-adapter.d.ts +17 -0
- package/dist/providers/cursor/stream-adapter.js +99 -0
- package/dist/providers/cursor/tool-continuation.d.ts +14 -0
- package/dist/providers/cursor/tool-continuation.js +134 -0
- package/dist/providers/cursor/tool-stream.d.ts +2 -0
- package/dist/providers/cursor/tool-stream.js +220 -0
- package/dist/providers/cursor/usage.d.ts +3 -0
- package/dist/providers/cursor/usage.js +35 -0
- package/dist/providers/ollama/adapter.d.ts +8 -0
- package/dist/providers/ollama/adapter.js +65 -0
- package/dist/providers/ollama/catalog-state.d.ts +17 -0
- package/dist/providers/ollama/catalog-state.js +40 -0
- package/dist/providers/ollama/cloud-catalog.d.ts +3 -0
- package/dist/providers/ollama/cloud-catalog.js +82 -0
- package/dist/providers/ollama/errors.d.ts +14 -0
- package/dist/providers/ollama/errors.js +19 -0
- package/dist/providers/ollama/generate.d.ts +2 -0
- package/dist/providers/ollama/generate.js +49 -0
- package/dist/providers/ollama/html-links.d.ts +1 -0
- package/dist/providers/ollama/html-links.js +30 -0
- package/dist/providers/ollama/http.d.ts +12 -0
- package/dist/providers/ollama/http.js +67 -0
- package/dist/providers/ollama/index.d.ts +8 -0
- package/dist/providers/ollama/index.js +8 -0
- package/dist/providers/ollama/language-model.d.ts +11 -0
- package/dist/providers/ollama/language-model.js +62 -0
- package/dist/providers/ollama/local-catalog.d.ts +3 -0
- package/dist/providers/ollama/local-catalog.js +38 -0
- package/dist/providers/ollama/ndjson.d.ts +3 -0
- package/dist/providers/ollama/ndjson.js +51 -0
- package/dist/providers/ollama/prompt.d.ts +8 -0
- package/dist/providers/ollama/prompt.js +141 -0
- package/dist/providers/ollama/protocol.d.ts +56 -0
- package/dist/providers/ollama/protocol.js +26 -0
- package/dist/providers/ollama/runtime.d.ts +11 -0
- package/dist/providers/ollama/runtime.js +128 -0
- package/dist/providers/ollama/stream.d.ts +8 -0
- package/dist/providers/ollama/stream.js +110 -0
- package/dist/sdk/command-code.d.ts +5 -0
- package/dist/sdk/command-code.js +6 -0
- package/dist/sdk/cursor.d.ts +5 -0
- package/dist/sdk/cursor.js +6 -0
- package/dist/sdk/ollama.d.ts +5 -0
- package/dist/sdk/ollama.js +6 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.js +94 -0
- package/docs/README.ko.md +189 -0
- package/package.json +75 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { parseCursorWireBlobId } from "./blob-store";
|
|
2
|
+
import { cursorMcpDefinitions, encodeCursorClientFrame, encodeNativeRejectFrame, encodeRequestContextFrame, } from "./exec-reply";
|
|
3
|
+
import { buildCursorInteractionReply } from "./interaction-reply";
|
|
4
|
+
import { encodeConversationCheckpoint } from "./proto/checkpoint";
|
|
5
|
+
import { decodeAgentServerMessage } from "./proto/server";
|
|
6
|
+
function result(outcome, replyFrames = []) {
|
|
7
|
+
return { outcome, replyFrames, closeStream: false };
|
|
8
|
+
}
|
|
9
|
+
function checkpointBlobIds(checkpoint) {
|
|
10
|
+
return [
|
|
11
|
+
...checkpoint.rootPromptMessageBlobIds,
|
|
12
|
+
...checkpoint.legacyTurnBlobIds,
|
|
13
|
+
...checkpoint.todoBlobIds,
|
|
14
|
+
...checkpoint.turnBlobIds,
|
|
15
|
+
...checkpoint.summaryArchiveBlobIds,
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
export function createCursorServerDispatcher(options) {
|
|
19
|
+
const parkedCalls = new Map();
|
|
20
|
+
const dispatchUpdate = (update) => {
|
|
21
|
+
switch (update.kind) {
|
|
22
|
+
case "text-delta":
|
|
23
|
+
return result({ kind: "text", text: update.text });
|
|
24
|
+
case "thinking-delta":
|
|
25
|
+
return result({ kind: "thinking", text: update.text });
|
|
26
|
+
case "token-delta":
|
|
27
|
+
return result({ kind: "tokens", tokens: update.tokens });
|
|
28
|
+
case "turn-ended":
|
|
29
|
+
return result({ kind: "turn-ended" });
|
|
30
|
+
case "heartbeat":
|
|
31
|
+
return result({ kind: "heartbeat" });
|
|
32
|
+
case "tool-call-started":
|
|
33
|
+
case "tool-call-completed":
|
|
34
|
+
case "thinking-completed":
|
|
35
|
+
case "user-message-appended":
|
|
36
|
+
case "partial-tool-call":
|
|
37
|
+
case "summary":
|
|
38
|
+
case "summary-started":
|
|
39
|
+
case "summary-completed":
|
|
40
|
+
case "shell-output-delta":
|
|
41
|
+
case "tool-call-delta":
|
|
42
|
+
case "step-started":
|
|
43
|
+
case "step-completed":
|
|
44
|
+
case "field-25":
|
|
45
|
+
return result({ kind: "telemetry", name: update.kind });
|
|
46
|
+
default:
|
|
47
|
+
return update;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const dispatchKv = (message) => {
|
|
51
|
+
const blobId = parseCursorWireBlobId(message.blobId);
|
|
52
|
+
switch (message.kind) {
|
|
53
|
+
case "get-blob": {
|
|
54
|
+
const blobData = options.blobStore.get(blobId);
|
|
55
|
+
const reply = encodeCursorClientFrame({
|
|
56
|
+
kind: "kv-client-message",
|
|
57
|
+
message: {
|
|
58
|
+
kind: "get-blob-result",
|
|
59
|
+
id: message.id,
|
|
60
|
+
...(blobData === null ? {} : { blobData }),
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
return result({ kind: "kv-get", id: message.id, found: blobData !== null }, [reply]);
|
|
64
|
+
}
|
|
65
|
+
case "set-blob": {
|
|
66
|
+
const matches = options.blobStore.hash(message.blobData) === blobId;
|
|
67
|
+
const stored = matches && options.blobStore.putVerified(blobId, message.blobData) !== null;
|
|
68
|
+
const reply = encodeCursorClientFrame({
|
|
69
|
+
kind: "kv-client-message",
|
|
70
|
+
message: {
|
|
71
|
+
kind: "set-blob-result",
|
|
72
|
+
id: message.id,
|
|
73
|
+
...(stored ? {} : { error: matches ? "blob-store-full" : "blob-id-mismatch" }),
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
return result({ kind: "kv-set", id: message.id, stored }, [reply]);
|
|
77
|
+
}
|
|
78
|
+
default:
|
|
79
|
+
return message;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const dispatchExec = (message) => {
|
|
83
|
+
switch (message.kind) {
|
|
84
|
+
case "metadata":
|
|
85
|
+
return result({ kind: "telemetry", name: message.kind });
|
|
86
|
+
case "request-context-args":
|
|
87
|
+
return result({ kind: "request-context-replied", id: message.id, execId: message.execId }, [
|
|
88
|
+
encodeRequestContextFrame(message, options.tools),
|
|
89
|
+
]);
|
|
90
|
+
case "mcp-args": {
|
|
91
|
+
const callId = message.args.toolCallId;
|
|
92
|
+
const toolNames = new Set(cursorMcpDefinitions(options.tools).flatMap((tool) => [tool.name, tool.toolName]));
|
|
93
|
+
if (callId === "" || parkedCalls.has(callId)) {
|
|
94
|
+
return result({
|
|
95
|
+
kind: "drift",
|
|
96
|
+
area: "mcp-call",
|
|
97
|
+
detail: callId === "" ? "missing-call-id" : "duplicate-call-id",
|
|
98
|
+
stranding: true,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const requestedName = message.args.toolName || message.args.name;
|
|
102
|
+
if (!toolNames.has(requestedName)) {
|
|
103
|
+
const reply = encodeCursorClientFrame({
|
|
104
|
+
kind: "exec-client-message",
|
|
105
|
+
message: {
|
|
106
|
+
kind: "mcp-result",
|
|
107
|
+
id: message.id,
|
|
108
|
+
execId: message.execId,
|
|
109
|
+
result: {
|
|
110
|
+
kind: "tool-not-found",
|
|
111
|
+
name: requestedName,
|
|
112
|
+
availableTools: [...toolNames],
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
return result({ kind: "mcp-rejected", callId }, [reply]);
|
|
117
|
+
}
|
|
118
|
+
const parked = {
|
|
119
|
+
callId,
|
|
120
|
+
execId: message.execId,
|
|
121
|
+
execMessageId: message.id,
|
|
122
|
+
args: message.args,
|
|
123
|
+
};
|
|
124
|
+
parkedCalls.set(callId, parked);
|
|
125
|
+
return result({ kind: "mcp-parked", parked });
|
|
126
|
+
}
|
|
127
|
+
case "native":
|
|
128
|
+
return result({ kind: "native-rejected", operation: message.operation }, [
|
|
129
|
+
encodeNativeRejectFrame(message),
|
|
130
|
+
]);
|
|
131
|
+
default:
|
|
132
|
+
return message;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
const dispatch = (message) => {
|
|
136
|
+
switch (message.kind) {
|
|
137
|
+
case "interaction-update":
|
|
138
|
+
return dispatchUpdate(message.update);
|
|
139
|
+
case "kv-server-message":
|
|
140
|
+
return dispatchKv(message.message);
|
|
141
|
+
case "exec-server-message":
|
|
142
|
+
return dispatchExec(message.message);
|
|
143
|
+
case "conversation-checkpoint-update": {
|
|
144
|
+
const blobIds = checkpointBlobIds(message.checkpoint).map(parseCursorWireBlobId);
|
|
145
|
+
const stored = options.checkpointStore.update({
|
|
146
|
+
sessionId: options.sessionId,
|
|
147
|
+
bytes: encodeConversationCheckpoint(message.checkpoint),
|
|
148
|
+
blobIds,
|
|
149
|
+
});
|
|
150
|
+
return result({ kind: "checkpoint", stored });
|
|
151
|
+
}
|
|
152
|
+
case "interaction-query": {
|
|
153
|
+
const interactionReply = buildCursorInteractionReply(message.query);
|
|
154
|
+
const reply = encodeCursorClientFrame({
|
|
155
|
+
kind: "interaction-response",
|
|
156
|
+
response: interactionReply.response,
|
|
157
|
+
});
|
|
158
|
+
return result({ kind: "interaction-replied", id: message.query.id, action: interactionReply.action }, [reply]);
|
|
159
|
+
}
|
|
160
|
+
case "exec-server-control":
|
|
161
|
+
return result({ kind: "control", control: message.control.kind, id: message.control.id });
|
|
162
|
+
case "unknown-oneof":
|
|
163
|
+
return result({
|
|
164
|
+
kind: "drift",
|
|
165
|
+
area: "server-message",
|
|
166
|
+
detail: `field-${message.field}`,
|
|
167
|
+
stranding: message.drift.stranding,
|
|
168
|
+
});
|
|
169
|
+
default:
|
|
170
|
+
return message;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
return {
|
|
174
|
+
dispatch,
|
|
175
|
+
dispatchBytes: (bytes) => dispatch(decodeAgentServerMessage(bytes)),
|
|
176
|
+
parkedCalls,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { Clock } from "../../core/clock";
|
|
3
|
+
import { type CursorBlobId, type CursorBlobStore } from "./blob-store";
|
|
4
|
+
export declare const CursorSessionIdSchema: z.core.$ZodBranded<z.ZodString, "CursorSessionId">;
|
|
5
|
+
export type CursorSessionId = z.output<typeof CursorSessionIdSchema>;
|
|
6
|
+
export type CursorSessionStateStoreOptions = {
|
|
7
|
+
readonly blobStore: CursorBlobStore;
|
|
8
|
+
readonly clock: Clock;
|
|
9
|
+
readonly maxKeysPerSession: number;
|
|
10
|
+
readonly maxSessions: number;
|
|
11
|
+
/** Logical milliseconds; zero expires sessions on the next store operation. */
|
|
12
|
+
readonly ttlMs: number;
|
|
13
|
+
};
|
|
14
|
+
export type CursorSessionStateStore = {
|
|
15
|
+
readonly get: (sessionId: CursorSessionId, key: string) => Uint8Array | null;
|
|
16
|
+
readonly invalidate: (sessionId: CursorSessionId) => void;
|
|
17
|
+
readonly set: (sessionId: CursorSessionId, key: string, value: Uint8Array) => CursorBlobId | null;
|
|
18
|
+
readonly size: () => number;
|
|
19
|
+
};
|
|
20
|
+
export declare function parseCursorSessionId(input: unknown): CursorSessionId;
|
|
21
|
+
export declare function createCursorSessionStateStore(options: CursorSessionStateStoreOptions): CursorSessionStateStore;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { requireCursorStoreTtl, requirePositiveCursorStoreBound, } from "./blob-store";
|
|
3
|
+
export const CursorSessionIdSchema = z
|
|
4
|
+
.string()
|
|
5
|
+
.min(1)
|
|
6
|
+
.max(256)
|
|
7
|
+
.refine((value) => value.trim() === value)
|
|
8
|
+
.refine((value) => [...value].every((character) => {
|
|
9
|
+
const codePoint = character.codePointAt(0);
|
|
10
|
+
return codePoint !== undefined && codePoint >= 32 && codePoint !== 127;
|
|
11
|
+
}))
|
|
12
|
+
.brand();
|
|
13
|
+
export function parseCursorSessionId(input) {
|
|
14
|
+
return CursorSessionIdSchema.parse(input);
|
|
15
|
+
}
|
|
16
|
+
export function createCursorSessionStateStore(options) {
|
|
17
|
+
requirePositiveCursorStoreBound(options.maxKeysPerSession, "maxKeysPerSession");
|
|
18
|
+
requirePositiveCursorStoreBound(options.maxSessions, "maxSessions");
|
|
19
|
+
requireCursorStoreTtl(options.ttlMs);
|
|
20
|
+
const sessions = new Map();
|
|
21
|
+
const remove = (sessionId) => {
|
|
22
|
+
const entry = sessions.get(sessionId);
|
|
23
|
+
if (entry !== undefined) {
|
|
24
|
+
sessions.delete(sessionId);
|
|
25
|
+
for (const blobId of entry.values.values()) {
|
|
26
|
+
options.blobStore.release(blobId);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const evictExpired = (nowMs) => {
|
|
31
|
+
for (const [sessionId, entry] of sessions) {
|
|
32
|
+
if (nowMs - entry.lastAccessMs >= options.ttlMs) {
|
|
33
|
+
remove(sessionId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const evictToBounds = () => {
|
|
38
|
+
while (sessions.size > options.maxSessions) {
|
|
39
|
+
const oldest = sessions.keys().next().value;
|
|
40
|
+
if (oldest === undefined) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
remove(oldest);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const touch = (sessionId, entry, nowMs) => {
|
|
47
|
+
const updated = { values: entry.values, lastAccessMs: nowMs };
|
|
48
|
+
sessions.delete(sessionId);
|
|
49
|
+
sessions.set(sessionId, updated);
|
|
50
|
+
return updated;
|
|
51
|
+
};
|
|
52
|
+
const invalidate = remove;
|
|
53
|
+
return {
|
|
54
|
+
get: (sessionId, key) => {
|
|
55
|
+
const nowMs = options.clock.nowMs();
|
|
56
|
+
evictExpired(nowMs);
|
|
57
|
+
const entry = sessions.get(sessionId);
|
|
58
|
+
const blobId = entry?.values.get(key);
|
|
59
|
+
if (entry === undefined || blobId === undefined) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const value = options.blobStore.get(blobId);
|
|
63
|
+
if (value === null) {
|
|
64
|
+
entry.values.delete(key);
|
|
65
|
+
options.blobStore.release(blobId);
|
|
66
|
+
if (entry.values.size === 0) {
|
|
67
|
+
invalidate(sessionId);
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
entry.values.delete(key);
|
|
72
|
+
entry.values.set(key, blobId);
|
|
73
|
+
touch(sessionId, entry, nowMs);
|
|
74
|
+
return value;
|
|
75
|
+
},
|
|
76
|
+
invalidate,
|
|
77
|
+
set: (sessionId, key, value) => {
|
|
78
|
+
const blobId = options.blobStore.put(value);
|
|
79
|
+
if (blobId === null || !options.blobStore.pin(blobId)) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const nowMs = options.clock.nowMs();
|
|
83
|
+
evictExpired(nowMs);
|
|
84
|
+
const existing = sessions.get(sessionId);
|
|
85
|
+
const entry = existing === undefined
|
|
86
|
+
? { values: new Map(), lastAccessMs: nowMs }
|
|
87
|
+
: touch(sessionId, existing, nowMs);
|
|
88
|
+
const replaced = entry.values.get(key);
|
|
89
|
+
entry.values.delete(key);
|
|
90
|
+
entry.values.set(key, blobId);
|
|
91
|
+
if (replaced !== undefined) {
|
|
92
|
+
options.blobStore.release(replaced);
|
|
93
|
+
}
|
|
94
|
+
while (entry.values.size > options.maxKeysPerSession) {
|
|
95
|
+
const oldestKey = entry.values.keys().next().value;
|
|
96
|
+
if (oldestKey === undefined) {
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
const evicted = entry.values.get(oldestKey);
|
|
100
|
+
entry.values.delete(oldestKey);
|
|
101
|
+
if (evicted !== undefined) {
|
|
102
|
+
options.blobStore.release(evicted);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
sessions.set(sessionId, entry);
|
|
106
|
+
evictToBounds();
|
|
107
|
+
if (!sessions.has(sessionId)) {
|
|
108
|
+
options.blobStore.release(blobId);
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return blobId;
|
|
112
|
+
},
|
|
113
|
+
size: () => {
|
|
114
|
+
evictExpired(options.clock.nowMs());
|
|
115
|
+
return sessions.size;
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function extractCursorSessionId(line: string): string | null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
function stringField(value, key) {
|
|
2
|
+
if (!(key in value)) {
|
|
3
|
+
return null;
|
|
4
|
+
}
|
|
5
|
+
const field = Reflect.get(value, key);
|
|
6
|
+
return typeof field === "string" && field.length > 0 ? field : null;
|
|
7
|
+
}
|
|
8
|
+
export function extractCursorSessionId(line) {
|
|
9
|
+
const trimmed = line.trim();
|
|
10
|
+
if (trimmed.length === 0) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
let parsed;
|
|
14
|
+
try {
|
|
15
|
+
parsed = JSON.parse(trimmed);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return (stringField(parsed, "session_id") ??
|
|
24
|
+
stringField(parsed, "chatId") ??
|
|
25
|
+
stringField(parsed, "resumeId"));
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function settleCursorCleanup(obligations: readonly (() => void | Promise<void>)[]): Promise<void>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export async function settleCursorCleanup(obligations) {
|
|
2
|
+
const failures = [];
|
|
3
|
+
for (const obligation of obligations) {
|
|
4
|
+
const [result] = await Promise.allSettled([Promise.resolve().then(obligation)]);
|
|
5
|
+
if (result?.status === "rejected")
|
|
6
|
+
failures.push(result.reason);
|
|
7
|
+
}
|
|
8
|
+
if (failures.length > 0) {
|
|
9
|
+
throw new AggregateError(failures, "Cursor cleanup failed");
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LanguageModelV3StreamPart } from "@ai-sdk/provider";
|
|
2
|
+
import type { CursorReplayState } from "./recovery";
|
|
3
|
+
import type { CursorDispatchOutcome } from "./server-dispatch";
|
|
4
|
+
type EmittableOutcome = Extract<CursorDispatchOutcome, {
|
|
5
|
+
readonly kind: "text" | "thinking" | "mcp-parked";
|
|
6
|
+
}>;
|
|
7
|
+
export type CursorStreamAdapter = {
|
|
8
|
+
readonly stream: ReadableStream<LanguageModelV3StreamPart>;
|
|
9
|
+
readonly emit: (outcome: EmittableOutcome) => void;
|
|
10
|
+
readonly noteCheckpoint: () => void;
|
|
11
|
+
readonly replayState: () => CursorReplayState;
|
|
12
|
+
readonly suspendForRetry: () => void;
|
|
13
|
+
readonly finish: (reason: "stop" | "tool-calls") => void;
|
|
14
|
+
readonly fail: (error: unknown) => void;
|
|
15
|
+
};
|
|
16
|
+
export declare function createCursorStreamAdapter(): CursorStreamAdapter;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { emptyCursorUsage } from "./usage";
|
|
2
|
+
export function createCursorStreamAdapter() {
|
|
3
|
+
let streamController = null;
|
|
4
|
+
let outputEpoch = 0;
|
|
5
|
+
let checkpointEpoch = null;
|
|
6
|
+
let toolBoundary = false;
|
|
7
|
+
let section = 1;
|
|
8
|
+
let textStarted = false;
|
|
9
|
+
let reasoningStarted = false;
|
|
10
|
+
let settled = false;
|
|
11
|
+
const stream = new ReadableStream({
|
|
12
|
+
start(controller) {
|
|
13
|
+
streamController = controller;
|
|
14
|
+
controller.enqueue({ type: "stream-start", warnings: [] });
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
const controller = () => {
|
|
18
|
+
if (streamController === null)
|
|
19
|
+
throw new TypeError("Cursor stream adapter is not initialized");
|
|
20
|
+
return streamController;
|
|
21
|
+
};
|
|
22
|
+
const closeSections = () => {
|
|
23
|
+
if (reasoningStarted) {
|
|
24
|
+
controller().enqueue({ type: "reasoning-end", id: `reasoning-${section}` });
|
|
25
|
+
reasoningStarted = false;
|
|
26
|
+
}
|
|
27
|
+
if (textStarted) {
|
|
28
|
+
controller().enqueue({ type: "text-end", id: `text-${section}` });
|
|
29
|
+
textStarted = false;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const emit = (outcome) => {
|
|
33
|
+
if (settled)
|
|
34
|
+
return;
|
|
35
|
+
switch (outcome.kind) {
|
|
36
|
+
case "text":
|
|
37
|
+
if (!textStarted)
|
|
38
|
+
controller().enqueue({ type: "text-start", id: `text-${section}` });
|
|
39
|
+
textStarted = true;
|
|
40
|
+
outputEpoch += 1;
|
|
41
|
+
controller().enqueue({ type: "text-delta", id: `text-${section}`, delta: outcome.text });
|
|
42
|
+
return;
|
|
43
|
+
case "thinking":
|
|
44
|
+
if (!reasoningStarted) {
|
|
45
|
+
controller().enqueue({ type: "reasoning-start", id: `reasoning-${section}` });
|
|
46
|
+
}
|
|
47
|
+
reasoningStarted = true;
|
|
48
|
+
outputEpoch += 1;
|
|
49
|
+
controller().enqueue({
|
|
50
|
+
type: "reasoning-delta",
|
|
51
|
+
id: `reasoning-${section}`,
|
|
52
|
+
delta: outcome.text,
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
case "mcp-parked":
|
|
56
|
+
toolBoundary = true;
|
|
57
|
+
controller().enqueue({
|
|
58
|
+
type: "tool-call",
|
|
59
|
+
toolCallId: outcome.parked.args.toolCallId,
|
|
60
|
+
toolName: outcome.parked.args.toolName || outcome.parked.args.name,
|
|
61
|
+
input: JSON.stringify(outcome.parked.args.args),
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const finish = (reason) => {
|
|
67
|
+
if (settled)
|
|
68
|
+
return;
|
|
69
|
+
settled = true;
|
|
70
|
+
closeSections();
|
|
71
|
+
controller().enqueue({
|
|
72
|
+
type: "finish",
|
|
73
|
+
finishReason: reason === "tool-calls"
|
|
74
|
+
? { unified: "tool-calls", raw: "tool_calls" }
|
|
75
|
+
: { unified: "stop", raw: "stop" },
|
|
76
|
+
usage: emptyCursorUsage(),
|
|
77
|
+
});
|
|
78
|
+
controller().close();
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
stream,
|
|
82
|
+
emit,
|
|
83
|
+
noteCheckpoint: () => {
|
|
84
|
+
checkpointEpoch = outputEpoch;
|
|
85
|
+
},
|
|
86
|
+
replayState: () => ({ outputEpoch, checkpointEpoch, toolBoundary }),
|
|
87
|
+
suspendForRetry: () => {
|
|
88
|
+
closeSections();
|
|
89
|
+
section += 1;
|
|
90
|
+
},
|
|
91
|
+
finish,
|
|
92
|
+
fail: (error) => {
|
|
93
|
+
if (settled)
|
|
94
|
+
return;
|
|
95
|
+
settled = true;
|
|
96
|
+
controller().error(error);
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LanguageModelV3Prompt } from "@ai-sdk/provider";
|
|
2
|
+
import type { ParkedMcpCall } from "./server-dispatch";
|
|
3
|
+
export type CursorToolContinuation = {
|
|
4
|
+
readonly callId: string;
|
|
5
|
+
readonly frame: Uint8Array;
|
|
6
|
+
};
|
|
7
|
+
export type CursorToolContinuationErrorReason = "duplicate-result" | "malformed-result" | "mismatched-result" | "missing-result";
|
|
8
|
+
export declare class CursorToolContinuationError extends Error {
|
|
9
|
+
readonly reason: CursorToolContinuationErrorReason;
|
|
10
|
+
readonly name = "CursorToolContinuationError";
|
|
11
|
+
readonly code = "CURSOR_TOOL_CONTINUATION_ERROR";
|
|
12
|
+
constructor(reason: CursorToolContinuationErrorReason);
|
|
13
|
+
}
|
|
14
|
+
export declare function buildCursorToolContinuations(prompt: LanguageModelV3Prompt, parkedCalls: ReadonlyMap<string, ParkedMcpCall>): readonly CursorToolContinuation[];
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { encodeCursorClientFrame } from "./exec-reply";
|
|
2
|
+
export class CursorToolContinuationError extends Error {
|
|
3
|
+
reason;
|
|
4
|
+
name = "CursorToolContinuationError";
|
|
5
|
+
code = "CURSOR_TOOL_CONTINUATION_ERROR";
|
|
6
|
+
constructor(reason) {
|
|
7
|
+
super("Cursor tool continuation is invalid");
|
|
8
|
+
this.reason = reason;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function jsonText(value) {
|
|
12
|
+
let encoded;
|
|
13
|
+
try {
|
|
14
|
+
encoded = JSON.stringify(value);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new CursorToolContinuationError("malformed-result");
|
|
18
|
+
}
|
|
19
|
+
if (encoded === undefined)
|
|
20
|
+
throw new CursorToolContinuationError("malformed-result");
|
|
21
|
+
return encoded;
|
|
22
|
+
}
|
|
23
|
+
function decodeBase64(value) {
|
|
24
|
+
if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) {
|
|
25
|
+
throw new CursorToolContinuationError("malformed-result");
|
|
26
|
+
}
|
|
27
|
+
return new Uint8Array(Buffer.from(value, "base64"));
|
|
28
|
+
}
|
|
29
|
+
function successResult(content, isError) {
|
|
30
|
+
return {
|
|
31
|
+
kind: "success",
|
|
32
|
+
content: content.length === 0 ? [{ kind: "text", text: "" }] : content,
|
|
33
|
+
isError,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function contentResult(output) {
|
|
37
|
+
const content = [];
|
|
38
|
+
for (const part of output.value) {
|
|
39
|
+
switch (part.type) {
|
|
40
|
+
case "text":
|
|
41
|
+
content.push({ kind: "text", text: part.text });
|
|
42
|
+
break;
|
|
43
|
+
case "image-data":
|
|
44
|
+
content.push({ kind: "image", data: decodeBase64(part.data), mimeType: part.mediaType });
|
|
45
|
+
break;
|
|
46
|
+
case "file-data":
|
|
47
|
+
if (!part.mediaType.startsWith("image/")) {
|
|
48
|
+
throw new CursorToolContinuationError("malformed-result");
|
|
49
|
+
}
|
|
50
|
+
content.push({ kind: "image", data: decodeBase64(part.data), mimeType: part.mediaType });
|
|
51
|
+
break;
|
|
52
|
+
case "file-url":
|
|
53
|
+
case "file-id":
|
|
54
|
+
case "image-url":
|
|
55
|
+
case "image-file-id":
|
|
56
|
+
case "custom":
|
|
57
|
+
throw new CursorToolContinuationError("malformed-result");
|
|
58
|
+
default:
|
|
59
|
+
return part;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return successResult(content, false);
|
|
63
|
+
}
|
|
64
|
+
function mcpResult(output) {
|
|
65
|
+
switch (output.type) {
|
|
66
|
+
case "text":
|
|
67
|
+
return successResult([{ kind: "text", text: output.value }], false);
|
|
68
|
+
case "json":
|
|
69
|
+
return successResult([{ kind: "text", text: jsonText(output.value) }], false);
|
|
70
|
+
case "content":
|
|
71
|
+
return contentResult(output);
|
|
72
|
+
case "error-text":
|
|
73
|
+
return successResult([{ kind: "text", text: output.value }], true);
|
|
74
|
+
case "error-json":
|
|
75
|
+
return successResult([{ kind: "text", text: jsonText(output.value) }], true);
|
|
76
|
+
case "execution-denied":
|
|
77
|
+
return { kind: "rejected", reason: output.reason ?? "Execution denied", isReadonly: false };
|
|
78
|
+
default:
|
|
79
|
+
return output;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function trailingResults(prompt) {
|
|
83
|
+
const results = [];
|
|
84
|
+
for (let index = prompt.length - 1; index >= 0; index -= 1) {
|
|
85
|
+
const message = prompt[index];
|
|
86
|
+
if (message?.role !== "tool")
|
|
87
|
+
break;
|
|
88
|
+
results.unshift(...message.content.filter((part) => part.type === "tool-result"));
|
|
89
|
+
}
|
|
90
|
+
return results;
|
|
91
|
+
}
|
|
92
|
+
function validateResults(results, parkedCalls) {
|
|
93
|
+
if (results.length === 0)
|
|
94
|
+
throw new CursorToolContinuationError("missing-result");
|
|
95
|
+
const resultsByCallId = new Map();
|
|
96
|
+
for (const result of results) {
|
|
97
|
+
if (resultsByCallId.has(result.toolCallId)) {
|
|
98
|
+
throw new CursorToolContinuationError("duplicate-result");
|
|
99
|
+
}
|
|
100
|
+
const parked = parkedCalls.get(result.toolCallId);
|
|
101
|
+
if (parked === undefined ||
|
|
102
|
+
(result.toolName !== parked.args.toolName && result.toolName !== parked.args.name)) {
|
|
103
|
+
throw new CursorToolContinuationError("mismatched-result");
|
|
104
|
+
}
|
|
105
|
+
resultsByCallId.set(result.toolCallId, result);
|
|
106
|
+
}
|
|
107
|
+
if (resultsByCallId.size !== parkedCalls.size) {
|
|
108
|
+
throw new CursorToolContinuationError("missing-result");
|
|
109
|
+
}
|
|
110
|
+
const validated = [];
|
|
111
|
+
for (const [callId, parked] of parkedCalls) {
|
|
112
|
+
const result = resultsByCallId.get(callId);
|
|
113
|
+
if (result === undefined)
|
|
114
|
+
throw new CursorToolContinuationError("missing-result");
|
|
115
|
+
validated.push({ callId, parked, result: mcpResult(result.output) });
|
|
116
|
+
}
|
|
117
|
+
return validated;
|
|
118
|
+
}
|
|
119
|
+
export function buildCursorToolContinuations(prompt, parkedCalls) {
|
|
120
|
+
return validateResults(trailingResults(prompt), parkedCalls).map(({ callId, parked, result }) => {
|
|
121
|
+
return {
|
|
122
|
+
callId,
|
|
123
|
+
frame: encodeCursorClientFrame({
|
|
124
|
+
kind: "exec-client-message",
|
|
125
|
+
message: {
|
|
126
|
+
kind: "mcp-result",
|
|
127
|
+
id: parked.execMessageId,
|
|
128
|
+
execId: parked.execId,
|
|
129
|
+
result,
|
|
130
|
+
},
|
|
131
|
+
}),
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
}
|