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,22 @@
|
|
|
1
|
+
import { concatBytes } from "../proto-wire";
|
|
2
|
+
import { CursorProtocolError } from "./errors";
|
|
3
|
+
function encodeVarint(value) {
|
|
4
|
+
const output = [];
|
|
5
|
+
let remaining = value >>> 0;
|
|
6
|
+
while (remaining > 0x7f) {
|
|
7
|
+
output.push((remaining & 0x7f) | 0x80);
|
|
8
|
+
remaining >>>= 7;
|
|
9
|
+
}
|
|
10
|
+
output.push(remaining);
|
|
11
|
+
return Uint8Array.from(output);
|
|
12
|
+
}
|
|
13
|
+
export function encodeUnknownField(field, wire, bytes) {
|
|
14
|
+
const tag = encodeVarint((field << 3) | wire);
|
|
15
|
+
if (wire === 2) {
|
|
16
|
+
return concatBytes([tag, encodeVarint(bytes.length), bytes]);
|
|
17
|
+
}
|
|
18
|
+
if (wire === 0 || wire === 1 || wire === 5) {
|
|
19
|
+
return concatBytes([tag, bytes]);
|
|
20
|
+
}
|
|
21
|
+
throw new CursorProtocolError("wrong-wire", "protobuf field", `unsupported wire type ${wire}`);
|
|
22
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Derived from Rahularya01/pi-cursor protobuf Value handling. Licensed under MIT.
|
|
2
|
+
// See THIRD_PARTY_NOTICES.md.
|
|
3
|
+
import { CursorProtocolDriftError, CursorProtocolError } from "./proto/errors";
|
|
4
|
+
import { assertKnownFields, repeatedFields, requiredField, requiredString } from "./proto/fields";
|
|
5
|
+
import { concatBytes, decodeFields, decodeFieldsStrict, decodeUtf8, decodeUtf8Strict, decodeVarintNumber, decodeVarintNumberStrict, encodeBoolField, encodeBytesField, encodeDoubleField, encodeStringField, } from "./proto-wire";
|
|
6
|
+
function encodeStruct(value) {
|
|
7
|
+
const entries = [];
|
|
8
|
+
for (const key of Object.keys(value)) {
|
|
9
|
+
entries.push(encodeBytesField(1, concatBytes([
|
|
10
|
+
encodeStringField(1, key),
|
|
11
|
+
encodeBytesField(2, encodeProtobufValue(value[key])),
|
|
12
|
+
])));
|
|
13
|
+
}
|
|
14
|
+
return concatBytes(entries);
|
|
15
|
+
}
|
|
16
|
+
function decodeStruct(bytes) {
|
|
17
|
+
const result = {};
|
|
18
|
+
for (const entry of decodeFields(bytes)) {
|
|
19
|
+
if (entry.field !== 1) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const fields = decodeFields(entry.bytes);
|
|
23
|
+
let key = "";
|
|
24
|
+
let value = null;
|
|
25
|
+
for (const inner of fields) {
|
|
26
|
+
if (inner.field === 1) {
|
|
27
|
+
key = decodeUtf8(inner.bytes);
|
|
28
|
+
}
|
|
29
|
+
if (inner.field === 2) {
|
|
30
|
+
value = decodeProtobufValue(inner.bytes);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (key.length > 0) {
|
|
34
|
+
result[key] = value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
export function decodeProtobufValue(bytes) {
|
|
40
|
+
const [entry] = decodeFields(bytes);
|
|
41
|
+
if (entry === undefined) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
switch (entry.field) {
|
|
45
|
+
case 1:
|
|
46
|
+
return null;
|
|
47
|
+
case 2:
|
|
48
|
+
if (entry.bytes.length < 8) {
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
return new DataView(entry.bytes.buffer, entry.bytes.byteOffset, 8).getFloat64(0, true);
|
|
52
|
+
case 3:
|
|
53
|
+
return decodeUtf8(entry.bytes);
|
|
54
|
+
case 4:
|
|
55
|
+
return decodeVarintNumber(entry.bytes) !== 0;
|
|
56
|
+
case 5:
|
|
57
|
+
return decodeStruct(entry.bytes);
|
|
58
|
+
case 6: {
|
|
59
|
+
const values = [];
|
|
60
|
+
for (const item of decodeFields(entry.bytes)) {
|
|
61
|
+
if (item.field === 1) {
|
|
62
|
+
values.push(decodeProtobufValue(item.bytes));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return values;
|
|
66
|
+
}
|
|
67
|
+
default:
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function encodeProtobufValue(value) {
|
|
72
|
+
if (value === null || value === undefined) {
|
|
73
|
+
return encodeBoolField(1, false);
|
|
74
|
+
}
|
|
75
|
+
if (typeof value === "number") {
|
|
76
|
+
return encodeDoubleField(2, value);
|
|
77
|
+
}
|
|
78
|
+
if (typeof value === "string") {
|
|
79
|
+
return encodeStringField(3, value);
|
|
80
|
+
}
|
|
81
|
+
if (typeof value === "boolean") {
|
|
82
|
+
return encodeBoolField(4, value);
|
|
83
|
+
}
|
|
84
|
+
if (Array.isArray(value)) {
|
|
85
|
+
return encodeBytesField(6, concatBytes(value.map((item) => encodeBytesField(1, encodeProtobufValue(item)))));
|
|
86
|
+
}
|
|
87
|
+
if (typeof value === "object") {
|
|
88
|
+
const record = {};
|
|
89
|
+
for (const key of Object.keys(value)) {
|
|
90
|
+
record[key] = Reflect.get(value, key);
|
|
91
|
+
}
|
|
92
|
+
return encodeBytesField(5, encodeStruct(record));
|
|
93
|
+
}
|
|
94
|
+
return encodeStringField(3, String(value));
|
|
95
|
+
}
|
|
96
|
+
function decodeStructStrict(bytes) {
|
|
97
|
+
const context = "google.protobuf.Struct";
|
|
98
|
+
const fields = decodeFieldsStrict(bytes, { context });
|
|
99
|
+
assertKnownFields(fields, [1], context);
|
|
100
|
+
const result = {};
|
|
101
|
+
for (const entry of repeatedFields(fields, { context, field: 1, wire: 2 })) {
|
|
102
|
+
const entryContext = "google.protobuf.Struct.FieldsEntry";
|
|
103
|
+
const mapFields = decodeFieldsStrict(entry.bytes, { context: entryContext });
|
|
104
|
+
assertKnownFields(mapFields, [1, 2], entryContext);
|
|
105
|
+
const key = requiredString(mapFields, { context: entryContext, field: 1, wire: 2 });
|
|
106
|
+
const value = requiredField(mapFields, { context: entryContext, field: 2, wire: 2 });
|
|
107
|
+
if (Object.hasOwn(result, key)) {
|
|
108
|
+
throw new CursorProtocolError("malformed", entryContext, `duplicate key ${key}`);
|
|
109
|
+
}
|
|
110
|
+
result[key] = decodeProtobufValueStrict(value.bytes);
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
function decodeListStrict(bytes) {
|
|
115
|
+
const context = "google.protobuf.ListValue";
|
|
116
|
+
const fields = decodeFieldsStrict(bytes, { context });
|
|
117
|
+
assertKnownFields(fields, [1], context);
|
|
118
|
+
return repeatedFields(fields, { context, field: 1, wire: 2 }).map((entry) => decodeProtobufValueStrict(entry.bytes));
|
|
119
|
+
}
|
|
120
|
+
export function decodeProtobufValueStrict(bytes) {
|
|
121
|
+
const context = "google.protobuf.Value";
|
|
122
|
+
const fields = decodeFieldsStrict(bytes, { context });
|
|
123
|
+
assertKnownFields(fields, [1, 2, 3, 4, 5, 6], context);
|
|
124
|
+
if (fields.length !== 1) {
|
|
125
|
+
throw new CursorProtocolError("malformed", context, `expected one variant, received ${fields.length}`);
|
|
126
|
+
}
|
|
127
|
+
const entry = fields.at(0);
|
|
128
|
+
if (entry === undefined) {
|
|
129
|
+
throw new CursorProtocolError("malformed", context, "Value variant is absent");
|
|
130
|
+
}
|
|
131
|
+
switch (entry.field) {
|
|
132
|
+
case 1: {
|
|
133
|
+
if (entry.wire !== 0 || decodeVarintNumberStrict(entry.bytes, context) !== 0) {
|
|
134
|
+
throw new CursorProtocolError("malformed", context, "invalid null variant");
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
case 2: {
|
|
139
|
+
if (entry.wire !== 1 || entry.bytes.length !== 8) {
|
|
140
|
+
throw new CursorProtocolError("wrong-wire", context, "invalid number variant");
|
|
141
|
+
}
|
|
142
|
+
const value = new DataView(entry.bytes.buffer, entry.bytes.byteOffset, 8).getFloat64(0, true);
|
|
143
|
+
if (!Number.isFinite(value)) {
|
|
144
|
+
throw new CursorProtocolError("malformed", context, "number is not finite");
|
|
145
|
+
}
|
|
146
|
+
return value;
|
|
147
|
+
}
|
|
148
|
+
case 3:
|
|
149
|
+
if (entry.wire !== 2) {
|
|
150
|
+
throw new CursorProtocolError("wrong-wire", context, "invalid string variant");
|
|
151
|
+
}
|
|
152
|
+
return decodeUtf8Strict(entry.bytes, context);
|
|
153
|
+
case 4: {
|
|
154
|
+
if (entry.wire !== 0) {
|
|
155
|
+
throw new CursorProtocolError("wrong-wire", context, "invalid boolean variant");
|
|
156
|
+
}
|
|
157
|
+
const value = decodeVarintNumberStrict(entry.bytes, context);
|
|
158
|
+
if (value !== 0 && value !== 1) {
|
|
159
|
+
throw new CursorProtocolError("malformed", context, "invalid boolean value");
|
|
160
|
+
}
|
|
161
|
+
return value === 1;
|
|
162
|
+
}
|
|
163
|
+
case 5:
|
|
164
|
+
return decodeStructStrict(entry.bytes);
|
|
165
|
+
case 6:
|
|
166
|
+
return decodeListStrict(entry.bytes);
|
|
167
|
+
default:
|
|
168
|
+
throw new CursorProtocolDriftError(context, entry.field, "unsupported Value variant");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const MAX_PROTO_MESSAGE_BYTES: number;
|
|
2
|
+
export type ProtoField = {
|
|
3
|
+
readonly field: number;
|
|
4
|
+
readonly wire: number;
|
|
5
|
+
readonly bytes: Uint8Array;
|
|
6
|
+
};
|
|
7
|
+
export type ProtoDecodeOptions = {
|
|
8
|
+
readonly context?: string;
|
|
9
|
+
readonly maxBytes?: number;
|
|
10
|
+
};
|
|
11
|
+
export declare function concatBytes(parts: readonly Uint8Array[]): Uint8Array;
|
|
12
|
+
export declare function encodeBytesField(field: number, value: Uint8Array): Uint8Array;
|
|
13
|
+
export declare function encodeBoolField(field: number, value: boolean): Uint8Array;
|
|
14
|
+
export declare function encodeInt32Field(field: number, value: number): Uint8Array;
|
|
15
|
+
export declare function encodeDoubleField(field: number, value: number): Uint8Array;
|
|
16
|
+
export declare function encodeStringField(field: number, value: string): Uint8Array;
|
|
17
|
+
export declare function decodeUtf8(bytes: Uint8Array): string;
|
|
18
|
+
export declare function decodeVarintNumber(bytes: Uint8Array): number;
|
|
19
|
+
export declare function decodeFields(buffer: Uint8Array): readonly ProtoField[];
|
|
20
|
+
export declare function decodeUtf8Strict(bytes: Uint8Array, context?: string): string;
|
|
21
|
+
export declare function decodeVarintNumberStrict(bytes: Uint8Array, context?: string): number;
|
|
22
|
+
export declare function decodeFieldsStrict(buffer: Uint8Array, options?: ProtoDecodeOptions): readonly ProtoField[];
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { CursorProtocolError } from "./proto/errors";
|
|
2
|
+
export const MAX_PROTO_MESSAGE_BYTES = 64 * 1024 * 1024;
|
|
3
|
+
function encodeVarint(value) {
|
|
4
|
+
const out = [];
|
|
5
|
+
let remaining = value >>> 0;
|
|
6
|
+
while (remaining > 0x7f) {
|
|
7
|
+
out.push((remaining & 0x7f) | 0x80);
|
|
8
|
+
remaining >>>= 7;
|
|
9
|
+
}
|
|
10
|
+
out.push(remaining);
|
|
11
|
+
return Uint8Array.from(out);
|
|
12
|
+
}
|
|
13
|
+
function decodeVarint(buffer, offset) {
|
|
14
|
+
let value = 0;
|
|
15
|
+
let shift = 0;
|
|
16
|
+
let index = offset;
|
|
17
|
+
while (index < buffer.length) {
|
|
18
|
+
const byte = buffer[index];
|
|
19
|
+
if (byte === undefined) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
index += 1;
|
|
23
|
+
value |= (byte & 0x7f) << shift;
|
|
24
|
+
if ((byte & 0x80) === 0) {
|
|
25
|
+
return { value, next: index };
|
|
26
|
+
}
|
|
27
|
+
shift += 7;
|
|
28
|
+
if (shift > 35) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
export function concatBytes(parts) {
|
|
35
|
+
let length = 0;
|
|
36
|
+
for (const part of parts) {
|
|
37
|
+
length += part.length;
|
|
38
|
+
}
|
|
39
|
+
const out = new Uint8Array(length);
|
|
40
|
+
let offset = 0;
|
|
41
|
+
for (const part of parts) {
|
|
42
|
+
out.set(part, offset);
|
|
43
|
+
offset += part.length;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
export function encodeBytesField(field, value) {
|
|
48
|
+
return concatBytes([encodeVarint((field << 3) | 2), encodeVarint(value.length), value]);
|
|
49
|
+
}
|
|
50
|
+
export function encodeBoolField(field, value) {
|
|
51
|
+
return concatBytes([encodeVarint((field << 3) | 0), encodeVarint(value ? 1 : 0)]);
|
|
52
|
+
}
|
|
53
|
+
export function encodeInt32Field(field, value) {
|
|
54
|
+
return concatBytes([encodeVarint((field << 3) | 0), encodeVarint(value >>> 0)]);
|
|
55
|
+
}
|
|
56
|
+
export function encodeDoubleField(field, value) {
|
|
57
|
+
const raw = new Uint8Array(8);
|
|
58
|
+
new DataView(raw.buffer).setFloat64(0, value, true);
|
|
59
|
+
return concatBytes([encodeVarint((field << 3) | 1), raw]);
|
|
60
|
+
}
|
|
61
|
+
export function encodeStringField(field, value) {
|
|
62
|
+
return encodeBytesField(field, new TextEncoder().encode(value));
|
|
63
|
+
}
|
|
64
|
+
export function decodeUtf8(bytes) {
|
|
65
|
+
return new TextDecoder().decode(bytes);
|
|
66
|
+
}
|
|
67
|
+
export function decodeVarintNumber(bytes) {
|
|
68
|
+
const decoded = decodeVarint(bytes, 0);
|
|
69
|
+
return decoded === null ? 0 : decoded.value;
|
|
70
|
+
}
|
|
71
|
+
export function decodeFields(buffer) {
|
|
72
|
+
const fields = [];
|
|
73
|
+
let offset = 0;
|
|
74
|
+
while (offset < buffer.length) {
|
|
75
|
+
const tag = decodeVarint(buffer, offset);
|
|
76
|
+
if (tag === null) {
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
const field = tag.value >>> 3;
|
|
80
|
+
const wire = tag.value & 7;
|
|
81
|
+
offset = tag.next;
|
|
82
|
+
if (wire === 0) {
|
|
83
|
+
const varint = decodeVarint(buffer, offset);
|
|
84
|
+
if (varint === null) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
fields.push({ field, wire, bytes: buffer.subarray(tag.next, varint.next) });
|
|
88
|
+
offset = varint.next;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (wire === 1) {
|
|
92
|
+
const end = offset + 8;
|
|
93
|
+
if (end > buffer.length) {
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
fields.push({ field, wire, bytes: buffer.subarray(offset, end) });
|
|
97
|
+
offset = end;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (wire === 2) {
|
|
101
|
+
const length = decodeVarint(buffer, offset);
|
|
102
|
+
if (length === null) {
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
const start = length.next;
|
|
106
|
+
const end = start + length.value;
|
|
107
|
+
if (end > buffer.length) {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
fields.push({ field, wire, bytes: buffer.subarray(start, end) });
|
|
111
|
+
offset = end;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
return fields;
|
|
117
|
+
}
|
|
118
|
+
function decodeRawVarintStrict(buffer, offset, context) {
|
|
119
|
+
for (let index = offset; index < offset + 10; index += 1) {
|
|
120
|
+
const byte = buffer[index];
|
|
121
|
+
if (byte === undefined) {
|
|
122
|
+
throw new CursorProtocolError("truncated", context, "incomplete varint");
|
|
123
|
+
}
|
|
124
|
+
if (index === offset + 9 && byte > 1) {
|
|
125
|
+
throw new CursorProtocolError("malformed", context, "uint64 varint overflow");
|
|
126
|
+
}
|
|
127
|
+
if ((byte & 0x80) === 0) {
|
|
128
|
+
return { next: index + 1, raw: buffer.subarray(offset, index + 1) };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
throw new CursorProtocolError("malformed", context, "varint exceeds ten bytes");
|
|
132
|
+
}
|
|
133
|
+
function decodeVarintStrict(buffer, offset, context) {
|
|
134
|
+
const decoded = decodeRawVarintStrict(buffer, offset, context);
|
|
135
|
+
if (decoded.raw.length > 5 || (decoded.raw.length === 5 && (decoded.raw[4] ?? 0) > 0x0f)) {
|
|
136
|
+
throw new CursorProtocolError("malformed", context, "uint32 varint overflow");
|
|
137
|
+
}
|
|
138
|
+
let value = 0;
|
|
139
|
+
let factor = 1;
|
|
140
|
+
for (const byte of decoded.raw) {
|
|
141
|
+
value += (byte & 0x7f) * factor;
|
|
142
|
+
factor *= 0x80;
|
|
143
|
+
}
|
|
144
|
+
return { value, next: decoded.next };
|
|
145
|
+
}
|
|
146
|
+
function fixedFieldEnd(offset, width, length, context) {
|
|
147
|
+
const end = offset + width;
|
|
148
|
+
if (end > length) {
|
|
149
|
+
throw new CursorProtocolError("truncated", context, `fixed${width * 8} field is incomplete`);
|
|
150
|
+
}
|
|
151
|
+
return end;
|
|
152
|
+
}
|
|
153
|
+
export function decodeUtf8Strict(bytes, context = "protobuf string") {
|
|
154
|
+
try {
|
|
155
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (error instanceof TypeError) {
|
|
159
|
+
throw new CursorProtocolError("malformed", context, "invalid UTF-8");
|
|
160
|
+
}
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
export function decodeVarintNumberStrict(bytes, context = "protobuf uint32") {
|
|
165
|
+
const decoded = decodeVarintStrict(bytes, 0, context);
|
|
166
|
+
if (decoded.next !== bytes.length) {
|
|
167
|
+
throw new CursorProtocolError("malformed", context, "varint has trailing bytes");
|
|
168
|
+
}
|
|
169
|
+
return decoded.value;
|
|
170
|
+
}
|
|
171
|
+
export function decodeFieldsStrict(buffer, options = {}) {
|
|
172
|
+
const context = options.context ?? "protobuf message";
|
|
173
|
+
const maxBytes = options.maxBytes ?? MAX_PROTO_MESSAGE_BYTES;
|
|
174
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
|
|
175
|
+
throw new CursorProtocolError("malformed", context, "invalid message size bound");
|
|
176
|
+
}
|
|
177
|
+
if (buffer.length > maxBytes) {
|
|
178
|
+
throw new CursorProtocolError("oversized", context, `message exceeds ${maxBytes} bytes`);
|
|
179
|
+
}
|
|
180
|
+
const fields = [];
|
|
181
|
+
let offset = 0;
|
|
182
|
+
while (offset < buffer.length) {
|
|
183
|
+
const tag = decodeVarintStrict(buffer, offset, `${context} tag`);
|
|
184
|
+
const field = Math.floor(tag.value / 8);
|
|
185
|
+
const wire = tag.value % 8;
|
|
186
|
+
if (field === 0) {
|
|
187
|
+
throw new CursorProtocolError("malformed", context, "field number zero");
|
|
188
|
+
}
|
|
189
|
+
offset = tag.next;
|
|
190
|
+
switch (wire) {
|
|
191
|
+
case 0: {
|
|
192
|
+
const value = decodeRawVarintStrict(buffer, offset, `${context} field ${field}`);
|
|
193
|
+
fields.push({ field, wire, bytes: buffer.subarray(offset, value.next) });
|
|
194
|
+
offset = value.next;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
case 1: {
|
|
198
|
+
const end = fixedFieldEnd(offset, 8, buffer.length, `${context} field ${field}`);
|
|
199
|
+
fields.push({ field, wire, bytes: buffer.subarray(offset, end) });
|
|
200
|
+
offset = end;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
case 2: {
|
|
204
|
+
const length = decodeVarintStrict(buffer, offset, `${context} field ${field} length`);
|
|
205
|
+
const end = length.next + length.value;
|
|
206
|
+
if (end > buffer.length) {
|
|
207
|
+
throw new CursorProtocolError("truncated", context, `field ${field} payload is incomplete`);
|
|
208
|
+
}
|
|
209
|
+
fields.push({ field, wire, bytes: buffer.subarray(length.next, end) });
|
|
210
|
+
offset = end;
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
case 5: {
|
|
214
|
+
const end = fixedFieldEnd(offset, 4, buffer.length, `${context} field ${field}`);
|
|
215
|
+
fields.push({ field, wire, bytes: buffer.subarray(offset, end) });
|
|
216
|
+
offset = end;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
default:
|
|
220
|
+
throw new CursorProtocolError("wrong-wire", context, `unsupported wire type ${wire}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return fields;
|
|
224
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CursorProtocolError } from "./proto/errors";
|
|
2
|
+
export type CursorProtocolFailureStage = "connect-frame" | "server-dispatch";
|
|
3
|
+
export type CursorProtocolDiagnosticContext = "AgentServerMessage" | "Connect end-stream" | "Connect stream" | "HeartbeatUpdate" | "InteractionUpdate" | "TextDeltaUpdate" | "ThinkingDeltaUpdate" | "TokenDeltaUpdate" | "TurnEndedUpdate" | "other";
|
|
4
|
+
export type CursorProtocolFailureProjection = {
|
|
5
|
+
readonly context: CursorProtocolDiagnosticContext;
|
|
6
|
+
readonly reason: CursorProtocolError["reason"];
|
|
7
|
+
readonly stage: CursorProtocolFailureStage;
|
|
8
|
+
};
|
|
9
|
+
export declare class CursorProtocolFailure extends Error {
|
|
10
|
+
readonly stage: CursorProtocolFailureStage;
|
|
11
|
+
readonly name = "CursorProtocolFailure";
|
|
12
|
+
readonly code = "CURSOR_PROTOCOL_FAILURE";
|
|
13
|
+
constructor(error: CursorProtocolError, stage: CursorProtocolFailureStage);
|
|
14
|
+
readonly reason: CursorProtocolError["reason"];
|
|
15
|
+
readonly context: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function annotateCursorProtocolFailure(error: unknown, stage: CursorProtocolFailureStage): never;
|
|
18
|
+
export declare function projectCursorProtocolFailure(error: unknown): CursorProtocolFailureProjection | null;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { CursorProtocolError } from "./proto/errors";
|
|
2
|
+
function diagnosticContext(context) {
|
|
3
|
+
switch (context) {
|
|
4
|
+
case "AgentServerMessage":
|
|
5
|
+
case "Connect end-stream":
|
|
6
|
+
case "Connect stream":
|
|
7
|
+
case "HeartbeatUpdate":
|
|
8
|
+
case "InteractionUpdate":
|
|
9
|
+
case "TextDeltaUpdate":
|
|
10
|
+
case "ThinkingDeltaUpdate":
|
|
11
|
+
case "TokenDeltaUpdate":
|
|
12
|
+
case "TurnEndedUpdate":
|
|
13
|
+
return context;
|
|
14
|
+
default:
|
|
15
|
+
return "other";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class CursorProtocolFailure extends Error {
|
|
19
|
+
stage;
|
|
20
|
+
name = "CursorProtocolFailure";
|
|
21
|
+
code = "CURSOR_PROTOCOL_FAILURE";
|
|
22
|
+
constructor(error, stage) {
|
|
23
|
+
super(error.message, { cause: error });
|
|
24
|
+
this.stage = stage;
|
|
25
|
+
this.context = error.context;
|
|
26
|
+
this.reason = error.reason;
|
|
27
|
+
}
|
|
28
|
+
reason;
|
|
29
|
+
context;
|
|
30
|
+
}
|
|
31
|
+
export function annotateCursorProtocolFailure(error, stage) {
|
|
32
|
+
if (error instanceof CursorProtocolError) {
|
|
33
|
+
throw new CursorProtocolFailure(error, stage);
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
export function projectCursorProtocolFailure(error) {
|
|
38
|
+
if (!(error instanceof CursorProtocolFailure))
|
|
39
|
+
return null;
|
|
40
|
+
return {
|
|
41
|
+
context: diagnosticContext(error.context),
|
|
42
|
+
reason: error.reason,
|
|
43
|
+
stage: error.stage,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Clock } from "../../core/clock";
|
|
2
|
+
import { type CursorProtocolFailureProjection } from "./protocol-failure";
|
|
3
|
+
export type CursorRecoveryAttemptMode = "initial" | "checkpoint" | "history";
|
|
4
|
+
export type CursorReplayState = {
|
|
5
|
+
readonly outputEpoch: number;
|
|
6
|
+
readonly checkpointEpoch: number | null;
|
|
7
|
+
readonly toolBoundary: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type CursorRecoveryDecision = {
|
|
10
|
+
readonly kind: "retry";
|
|
11
|
+
readonly mode: "checkpoint" | "history";
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: "fail";
|
|
14
|
+
readonly reason: "exhausted" | "replay-unsafe" | "non-retryable";
|
|
15
|
+
};
|
|
16
|
+
export declare class CursorRecoveryError extends Error {
|
|
17
|
+
readonly reason: "idle" | "exhausted" | "replay-unsafe" | "non-retryable";
|
|
18
|
+
readonly attemptedModes: readonly CursorRecoveryAttemptMode[];
|
|
19
|
+
readonly retryable: boolean;
|
|
20
|
+
readonly cause: unknown;
|
|
21
|
+
readonly name = "CursorRecoveryError";
|
|
22
|
+
readonly code = "CURSOR_RECOVERY_ERROR";
|
|
23
|
+
readonly protocolFailure: CursorProtocolFailureProjection | null;
|
|
24
|
+
constructor(reason: "idle" | "exhausted" | "replay-unsafe" | "non-retryable", attemptedModes: readonly CursorRecoveryAttemptMode[], retryable: boolean, cause?: unknown);
|
|
25
|
+
}
|
|
26
|
+
export type CursorRecoveryPlanner = {
|
|
27
|
+
readonly next: (input: {
|
|
28
|
+
readonly checkpointAvailable: boolean;
|
|
29
|
+
readonly replay: CursorReplayState;
|
|
30
|
+
readonly retryable: boolean;
|
|
31
|
+
}) => CursorRecoveryDecision;
|
|
32
|
+
readonly requireRetry: (decision: CursorRecoveryDecision, cause: unknown) => "checkpoint" | "history";
|
|
33
|
+
readonly attemptedModes: () => readonly CursorRecoveryAttemptMode[];
|
|
34
|
+
};
|
|
35
|
+
export declare function createCursorRecoveryPlanner(): CursorRecoveryPlanner;
|
|
36
|
+
export type CursorIdleWatchdog = {
|
|
37
|
+
readonly signal: AbortSignal;
|
|
38
|
+
readonly expired: () => boolean;
|
|
39
|
+
readonly progress: () => void;
|
|
40
|
+
readonly heartbeat: () => void;
|
|
41
|
+
readonly park: () => void;
|
|
42
|
+
readonly dispose: () => void;
|
|
43
|
+
};
|
|
44
|
+
export declare function createCursorIdleWatchdog(options: {
|
|
45
|
+
readonly clock: Clock;
|
|
46
|
+
readonly idleTimeoutMs?: number;
|
|
47
|
+
readonly parentSignal: AbortSignal;
|
|
48
|
+
}): CursorIdleWatchdog;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { InvalidArgumentError, OperationCancelledError } from "../../core/errors";
|
|
2
|
+
import { projectCursorProtocolFailure, } from "./protocol-failure";
|
|
3
|
+
export class CursorRecoveryError extends Error {
|
|
4
|
+
reason;
|
|
5
|
+
attemptedModes;
|
|
6
|
+
retryable;
|
|
7
|
+
cause;
|
|
8
|
+
name = "CursorRecoveryError";
|
|
9
|
+
code = "CURSOR_RECOVERY_ERROR";
|
|
10
|
+
protocolFailure;
|
|
11
|
+
constructor(reason, attemptedModes, retryable, cause = null) {
|
|
12
|
+
super(`Cursor recovery failed: ${reason}`, { cause });
|
|
13
|
+
this.reason = reason;
|
|
14
|
+
this.attemptedModes = attemptedModes;
|
|
15
|
+
this.retryable = retryable;
|
|
16
|
+
this.cause = cause;
|
|
17
|
+
this.protocolFailure = projectCursorProtocolFailure(cause);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function createCursorRecoveryPlanner() {
|
|
21
|
+
const attempted = ["initial"];
|
|
22
|
+
let checkpointTried = false;
|
|
23
|
+
let historyTried = false;
|
|
24
|
+
const next = (input) => {
|
|
25
|
+
if (!input.retryable || input.replay.toolBoundary) {
|
|
26
|
+
return { kind: "fail", reason: "non-retryable" };
|
|
27
|
+
}
|
|
28
|
+
const checkpointCoversOutput = input.replay.checkpointEpoch !== null &&
|
|
29
|
+
input.replay.checkpointEpoch === input.replay.outputEpoch;
|
|
30
|
+
if (!checkpointTried && input.checkpointAvailable && checkpointCoversOutput) {
|
|
31
|
+
checkpointTried = true;
|
|
32
|
+
attempted.push("checkpoint");
|
|
33
|
+
return { kind: "retry", mode: "checkpoint" };
|
|
34
|
+
}
|
|
35
|
+
if (input.replay.outputEpoch > 0) {
|
|
36
|
+
return { kind: "fail", reason: "replay-unsafe" };
|
|
37
|
+
}
|
|
38
|
+
if (!historyTried) {
|
|
39
|
+
historyTried = true;
|
|
40
|
+
attempted.push("history");
|
|
41
|
+
return { kind: "retry", mode: "history" };
|
|
42
|
+
}
|
|
43
|
+
return { kind: "fail", reason: "exhausted" };
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
next,
|
|
47
|
+
requireRetry: (decision, cause) => {
|
|
48
|
+
if (decision.kind === "retry")
|
|
49
|
+
return decision.mode;
|
|
50
|
+
throw new CursorRecoveryError(decision.reason, [...attempted], decision.reason === "exhausted", cause);
|
|
51
|
+
},
|
|
52
|
+
attemptedModes: () => [...attempted],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function createCursorIdleWatchdog(options) {
|
|
56
|
+
const idleTimeoutMs = options.idleTimeoutMs ?? 60_000;
|
|
57
|
+
if (!Number.isSafeInteger(idleTimeoutMs) || idleTimeoutMs < 0) {
|
|
58
|
+
throw new InvalidArgumentError("idleTimeoutMs");
|
|
59
|
+
}
|
|
60
|
+
const controller = new AbortController();
|
|
61
|
+
let timer = null;
|
|
62
|
+
let disposed = false;
|
|
63
|
+
const cancelTimer = () => {
|
|
64
|
+
timer?.cancel();
|
|
65
|
+
timer = null;
|
|
66
|
+
};
|
|
67
|
+
const expire = () => {
|
|
68
|
+
timer = null;
|
|
69
|
+
if (!controller.signal.aborted) {
|
|
70
|
+
controller.abort(new CursorRecoveryError("idle", ["initial"], true));
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const arm = () => {
|
|
74
|
+
cancelTimer();
|
|
75
|
+
if (!disposed && !controller.signal.aborted) {
|
|
76
|
+
timer = options.clock.schedule(idleTimeoutMs, expire);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const onParentAbort = () => {
|
|
80
|
+
cancelTimer();
|
|
81
|
+
if (!controller.signal.aborted) {
|
|
82
|
+
controller.abort(new OperationCancelledError("cursor-direct-stream"));
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
options.parentSignal.addEventListener("abort", onParentAbort, { once: true });
|
|
86
|
+
if (options.parentSignal.aborted)
|
|
87
|
+
onParentAbort();
|
|
88
|
+
else
|
|
89
|
+
arm();
|
|
90
|
+
return {
|
|
91
|
+
signal: controller.signal,
|
|
92
|
+
expired: () => controller.signal.aborted && controller.signal.reason instanceof CursorRecoveryError,
|
|
93
|
+
progress: arm,
|
|
94
|
+
heartbeat: () => undefined,
|
|
95
|
+
park: cancelTimer,
|
|
96
|
+
dispose: () => {
|
|
97
|
+
disposed = true;
|
|
98
|
+
cancelTimer();
|
|
99
|
+
options.parentSignal.removeEventListener("abort", onParentAbort);
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CursorBlobId, CursorBlobStore } from "./blob-store";
|
|
2
|
+
export type CursorRequestBlobOwnership = {
|
|
3
|
+
readonly blobIds: readonly CursorBlobId[];
|
|
4
|
+
readonly release: () => void;
|
|
5
|
+
readonly [Symbol.dispose]: () => void;
|
|
6
|
+
};
|
|
7
|
+
export type CursorRequestBlobCollector = {
|
|
8
|
+
readonly acquire: (blobId: CursorBlobId) => void;
|
|
9
|
+
readonly finish: () => CursorRequestBlobOwnership;
|
|
10
|
+
readonly rollback: () => void;
|
|
11
|
+
};
|
|
12
|
+
export declare class CursorRequestBlobOwnershipError extends Error {
|
|
13
|
+
constructor();
|
|
14
|
+
}
|
|
15
|
+
export declare function createCursorRequestBlobCollector(store: CursorBlobStore): CursorRequestBlobCollector;
|