cursor-opencode-provider 0.8.1 → 0.8.3
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 +95 -0
- package/README.md +204 -37
- package/SECURITY.md +20 -8
- package/dist/activity.d.ts +73 -1
- package/dist/activity.js +231 -5
- package/dist/agent-url.js +37 -9
- package/dist/auth-renewal.d.ts +1 -1
- package/dist/auth-renewal.js +58 -26
- package/dist/billing.d.ts +105 -0
- package/dist/billing.js +212 -0
- package/dist/classic-tool-results.d.ts +1 -0
- package/dist/classic-tool-results.js +42 -0
- package/dist/context/build.d.ts +1 -0
- package/dist/context/build.js +32 -5
- package/dist/context/env.js +27 -3
- package/dist/context/epoch.js +9 -5
- package/dist/context/git.d.ts +5 -2
- package/dist/context/git.js +17 -7
- package/dist/context/paths.d.ts +1 -1
- package/dist/context/paths.js +7 -4
- package/dist/debug.js +13 -2
- package/dist/early-tool-rows.d.ts +60 -0
- package/dist/early-tool-rows.js +96 -0
- package/dist/errors.d.ts +49 -7
- package/dist/errors.js +163 -8
- package/dist/host-interrupt.d.ts +12 -0
- package/dist/host-interrupt.js +18 -0
- package/dist/host-steer.d.ts +71 -0
- package/dist/host-steer.js +232 -0
- package/dist/image-input.js +4 -3
- package/dist/index.d.ts +2 -2
- package/dist/language-model.d.ts +114 -23
- package/dist/language-model.js +2290 -469
- package/dist/lost-request.d.ts +39 -0
- package/dist/lost-request.js +111 -0
- package/dist/model-config.d.ts +3 -1
- package/dist/model-config.js +50 -29
- package/dist/models.d.ts +26 -3
- package/dist/models.js +101 -45
- package/dist/opencode2/tool-results.d.ts +18 -0
- package/dist/opencode2/tool-results.js +66 -0
- package/dist/plugin-opencode2.js +185 -25
- package/dist/plugin.js +28 -5
- package/dist/pricing-data.d.ts +26 -2
- package/dist/pricing-data.js +26 -2
- package/dist/pricing.d.ts +1 -1
- package/dist/pricing.js +7 -4
- package/dist/process-shared.d.ts +14 -0
- package/dist/process-shared.js +18 -0
- package/dist/protocol/apply-patch.d.ts +25 -13
- package/dist/protocol/apply-patch.js +79 -30
- package/dist/protocol/blob-store.d.ts +5 -1
- package/dist/protocol/blob-store.js +10 -6
- package/dist/protocol/client-version.js +6 -4
- package/dist/protocol/conversation-cache-header.d.ts +23 -0
- package/dist/protocol/conversation-cache-header.js +149 -0
- package/dist/protocol/conversation-persistence.d.ts +27 -1
- package/dist/protocol/conversation-persistence.js +628 -126
- package/dist/protocol/conversation-state.d.ts +8 -1
- package/dist/protocol/conversation-state.js +28 -7
- package/dist/protocol/host-notes.d.ts +31 -0
- package/dist/protocol/host-notes.js +85 -0
- package/dist/protocol/messages.js +91 -8
- package/dist/protocol/request.d.ts +35 -19
- package/dist/protocol/request.js +53 -28
- package/dist/protocol/sent-history-images.d.ts +49 -0
- package/dist/protocol/sent-history-images.js +102 -0
- package/dist/protocol/tool-call-bridge.d.ts +2 -0
- package/dist/protocol/tool-call-bridge.js +10 -0
- package/dist/protocol/tools.d.ts +74 -8
- package/dist/protocol/tools.js +431 -141
- package/dist/protocol/turn-provenance.d.ts +19 -4
- package/dist/protocol/turn-provenance.js +77 -20
- package/dist/replay-safety.d.ts +16 -4
- package/dist/replay-safety.js +34 -25
- package/dist/retry-budget.d.ts +21 -0
- package/dist/retry-budget.js +94 -0
- package/dist/runtime-files.d.ts +23 -0
- package/dist/runtime-files.js +581 -0
- package/dist/session.d.ts +94 -9
- package/dist/session.js +153 -34
- package/dist/shared.d.ts +1 -1
- package/dist/shared.js +1 -1
- package/dist/shell-timeout.d.ts +54 -10
- package/dist/shell-timeout.js +362 -92
- package/dist/stream-write.d.ts +20 -0
- package/dist/stream-write.js +87 -0
- package/dist/terminal-runner.d.ts +15 -0
- package/dist/terminal-runner.js +528 -0
- package/dist/transport/connect.d.ts +8 -1
- package/dist/transport/connect.js +103 -23
- package/dist/usage.d.ts +37 -17
- package/dist/usage.js +63 -29
- package/package.json +3 -2
- package/dist/protocol/thinking.d.ts +0 -15
- package/dist/protocol/thinking.js +0 -17
|
@@ -6,10 +6,15 @@ export declare function hydrateConversationState(cacheDir: string, sessionKey: s
|
|
|
6
6
|
toolCatalog: OpencodeToolDef[];
|
|
7
7
|
hostAgent?: string;
|
|
8
8
|
systemPromptHash?: string;
|
|
9
|
+
hostNote?: string;
|
|
9
10
|
} | undefined>;
|
|
10
11
|
/** Restore only turn provenance when its in-memory entry was evicted. */
|
|
11
12
|
export declare function hydrateTurnProvenance(cacheDir: string, sessionKey: string): Promise<void>;
|
|
12
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Persist the resumable state at Cursor's TurnEnded, or before it
|
|
15
|
+
* (`runInProgress`) so a Run the host interrupts, or a restart, resumes from
|
|
16
|
+
* the latest checkpoint and still recognizes the step in progress.
|
|
17
|
+
*/
|
|
13
18
|
export declare function persistConversationState(cacheDir: string, input: {
|
|
14
19
|
sessionKey: string;
|
|
15
20
|
conversationId: string;
|
|
@@ -18,5 +23,7 @@ export declare function persistConversationState(cacheDir: string, input: {
|
|
|
18
23
|
postCompactionRebase?: boolean;
|
|
19
24
|
hostAgent?: string;
|
|
20
25
|
systemPromptHash?: string;
|
|
26
|
+
hostNote?: string;
|
|
27
|
+
runInProgress?: boolean;
|
|
21
28
|
}): Promise<void>;
|
|
22
29
|
export declare function clearPersistedConversationState(cacheDir: string, sessionKey: string, expectedConversationId?: string): Promise<void>;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { trace } from "../debug.js";
|
|
2
|
+
import { restoreEarlySteers, serializeEarlySteers } from "../host-steer.js";
|
|
3
|
+
import { restoreLostRequest, serializeLostRequest } from "../lost-request.js";
|
|
2
4
|
import { getFrozenRequestContext, setFrozenRequestContext, } from "../context/frozen.js";
|
|
3
5
|
import { hasConversationBinding, isActiveConversationBinding, restoreConversationBinding, } from "./conversation-bind.js";
|
|
4
6
|
import { compactConversationBlobs, restoreConversationBlobs, } from "./blob-store.js";
|
|
5
7
|
import { getCheckpoint, setCheckpoint } from "./checkpoint.js";
|
|
6
8
|
import { getTurnProvenance, parseTurnProvenance, restoreTurnProvenance, serializeTurnProvenance, } from "./turn-provenance.js";
|
|
7
|
-
import {
|
|
9
|
+
import { heldHistoryImagesSnapshot, restoreHeldHistoryImages } from "./sent-history-images.js";
|
|
10
|
+
import { deletePersistedConversation, getPersistedTurnProvenance, loadPersistedConversation, persistConversation, } from "./conversation-persistence.js";
|
|
8
11
|
/** Restore one OpenCode session before its conversation binding is resolved. */
|
|
9
12
|
export async function hydrateConversationState(cacheDir, sessionKey) {
|
|
10
13
|
if (hasConversationBinding(sessionKey))
|
|
@@ -17,6 +20,8 @@ export async function hydrateConversationState(cacheDir, sessionKey) {
|
|
|
17
20
|
return undefined;
|
|
18
21
|
}
|
|
19
22
|
restoreConversationBinding(sessionKey, persisted.conversationId);
|
|
23
|
+
restoreEarlySteers(sessionKey, persisted.earlySteers);
|
|
24
|
+
restoreLostRequest(sessionKey, persisted.lostRequest);
|
|
20
25
|
if (persisted.checkpoint)
|
|
21
26
|
setCheckpoint(persisted.conversationId, persisted.checkpoint);
|
|
22
27
|
restoreConversationBlobs(persisted.conversationId, persisted.blobs);
|
|
@@ -24,29 +29,38 @@ export async function hydrateConversationState(cacheDir, sessionKey) {
|
|
|
24
29
|
const provenance = persisted.turnProvenance ? parseTurnProvenance(persisted.turnProvenance) : undefined;
|
|
25
30
|
if (provenance?.conversationId === persisted.conversationId)
|
|
26
31
|
restoreTurnProvenance(sessionKey, provenance);
|
|
32
|
+
restoreHeldHistoryImages(sessionKey, persisted.conversationId, {
|
|
33
|
+
checkpoint: persisted.sentImagesCheckpoint,
|
|
34
|
+
hashes: persisted.sentImageHashes,
|
|
35
|
+
});
|
|
27
36
|
trace(`conversation persistence: restored sessionKey=${sessionKey} ` +
|
|
28
37
|
`conversationId=${persisted.conversationId} checkpoint=${persisted.checkpoint?.length ?? 0}B ` +
|
|
29
|
-
`blobs=${persisted.blobs.length}`);
|
|
38
|
+
`blobs=${persisted.blobs.length} heldImages=${persisted.sentImageHashes?.length ?? 0}`);
|
|
30
39
|
return {
|
|
31
40
|
conversationId: persisted.conversationId,
|
|
32
41
|
postCompactionRebase: persisted.postCompactionRebase,
|
|
33
42
|
toolCatalog: structuredClone(persisted.toolCatalog),
|
|
34
43
|
...(persisted.hostAgent ? { hostAgent: persisted.hostAgent } : {}),
|
|
35
44
|
...(persisted.systemPromptHash ? { systemPromptHash: persisted.systemPromptHash } : {}),
|
|
45
|
+
...(persisted.hostNote ? { hostNote: persisted.hostNote } : {}),
|
|
36
46
|
};
|
|
37
47
|
}
|
|
38
48
|
/** Restore only turn provenance when its in-memory entry was evicted. */
|
|
39
49
|
export async function hydrateTurnProvenance(cacheDir, sessionKey) {
|
|
40
50
|
if (getTurnProvenance(sessionKey))
|
|
41
51
|
return;
|
|
42
|
-
const persisted =
|
|
43
|
-
if (!persisted
|
|
52
|
+
const persisted = await getPersistedTurnProvenance(cacheDir, sessionKey);
|
|
53
|
+
if (!persisted)
|
|
44
54
|
return;
|
|
45
55
|
const provenance = parseTurnProvenance(persisted.turnProvenance);
|
|
46
56
|
if (provenance?.conversationId === persisted.conversationId)
|
|
47
57
|
restoreTurnProvenance(sessionKey, provenance);
|
|
48
58
|
}
|
|
49
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Persist the resumable state at Cursor's TurnEnded, or before it
|
|
61
|
+
* (`runInProgress`) so a Run the host interrupts, or a restart, resumes from
|
|
62
|
+
* the latest checkpoint and still recognizes the step in progress.
|
|
63
|
+
*/
|
|
50
64
|
export async function persistConversationState(cacheDir, input) {
|
|
51
65
|
// A newer Run may have reset/superseded this conversation while its final
|
|
52
66
|
// frame was still in flight. Never let that late TurnEnded resurrect it.
|
|
@@ -56,10 +70,13 @@ export async function persistConversationState(cacheDir, input) {
|
|
|
56
70
|
return;
|
|
57
71
|
}
|
|
58
72
|
const checkpoint = getCheckpoint(input.conversationId);
|
|
59
|
-
const blobCompaction = compactConversationBlobs(input.conversationId, checkpoint
|
|
73
|
+
const blobCompaction = compactConversationBlobs(input.conversationId, checkpoint, {
|
|
74
|
+
keepInMemory: input.runInProgress,
|
|
75
|
+
});
|
|
60
76
|
const blobs = blobCompaction.blobs;
|
|
61
77
|
const requestContext = getFrozenRequestContext(input.conversationId) ?? input.requestContext;
|
|
62
78
|
const provenance = getTurnProvenance(input.sessionKey);
|
|
79
|
+
const heldImages = heldHistoryImagesSnapshot(input.sessionKey, input.conversationId, checkpoint);
|
|
63
80
|
await persistConversation(cacheDir, {
|
|
64
81
|
sessionKey: input.sessionKey,
|
|
65
82
|
conversationId: input.conversationId,
|
|
@@ -70,11 +87,15 @@ export async function persistConversationState(cacheDir, input) {
|
|
|
70
87
|
postCompactionRebase: input.postCompactionRebase,
|
|
71
88
|
hostAgent: input.hostAgent,
|
|
72
89
|
systemPromptHash: input.systemPromptHash,
|
|
90
|
+
hostNote: input.hostNote,
|
|
91
|
+
earlySteers: serializeEarlySteers(input.sessionKey),
|
|
92
|
+
lostRequest: serializeLostRequest(input.sessionKey),
|
|
93
|
+
...(heldImages ? { sentImageHashes: heldImages.hashes, sentImagesCheckpoint: heldImages.checkpoint } : {}),
|
|
73
94
|
...(provenance?.conversationId === input.conversationId
|
|
74
95
|
? { turnProvenance: serializeTurnProvenance(provenance) }
|
|
75
96
|
: {}),
|
|
76
97
|
});
|
|
77
|
-
trace(`conversation persistence: saved sessionKey=${input.sessionKey} ` +
|
|
98
|
+
trace(`conversation persistence: saved${input.runInProgress ? " in-progress" : ""} sessionKey=${input.sessionKey} ` +
|
|
78
99
|
`conversationId=${input.conversationId} checkpoint=${checkpoint?.length ?? 0}B ` +
|
|
79
100
|
`blobs=${blobCompaction.beforeCount}->${blobCompaction.afterCount} ` +
|
|
80
101
|
`blobBytes=${blobCompaction.beforeBytes}->${blobCompaction.afterBytes}` +
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host notes (OpenCode `<system-update>` and read-tool instructions) in the
|
|
3
|
+
* shapes Cursor's own clients give model-visible context:
|
|
4
|
+
* - mid-turn, plain text in a `user_context` injection, as Cursor SDK
|
|
5
|
+
* `Run.steer` sends it (no wrapper: wrapped text was refused live);
|
|
6
|
+
* - on a later user turn, a `<system_reminder>` block in the message text;
|
|
7
|
+
* - provider notices about one exec result, as postToolUse hook context.
|
|
8
|
+
*/
|
|
9
|
+
/** Cursor CLI rejects a hook additional_context longer than this. */
|
|
10
|
+
export declare const HOOK_ADDITIONAL_CONTEXT_MAX_CHARS = 10000;
|
|
11
|
+
/** Stable delivery order survives asynchronous acknowledgements and Run recovery. */
|
|
12
|
+
export type HostNoteBatch = {
|
|
13
|
+
notes: string[];
|
|
14
|
+
order: number;
|
|
15
|
+
};
|
|
16
|
+
export type HookAdditionalContext = {
|
|
17
|
+
hook_event_name: string;
|
|
18
|
+
content: string;
|
|
19
|
+
};
|
|
20
|
+
/** Plain note text for one mid-turn injection, in host order. */
|
|
21
|
+
export declare function hostNoteText(notes: readonly string[]): string;
|
|
22
|
+
/**
|
|
23
|
+
* One postToolUse hook context per note, trimmed like Cursor CLI's carrier.
|
|
24
|
+
* A note over the CLI limit is split rather than dropped, so no text is lost.
|
|
25
|
+
*/
|
|
26
|
+
export declare function hostNoteHookContexts(notes: readonly string[]): HookAdditionalContext[];
|
|
27
|
+
/** One `<system_reminder>` block per note, for a user turn's text. */
|
|
28
|
+
export declare function wrapHostNoteForCursor(text: string): string;
|
|
29
|
+
export declare function wrapHostNotesForCursor(notes: readonly string[]): string[];
|
|
30
|
+
export declare function encodePersistedHostNotes(notes: readonly string[]): string | undefined;
|
|
31
|
+
export declare function decodePersistedHostNotes(blob: string | undefined): string[];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host notes (OpenCode `<system-update>` and read-tool instructions) in the
|
|
3
|
+
* shapes Cursor's own clients give model-visible context:
|
|
4
|
+
* - mid-turn, plain text in a `user_context` injection, as Cursor SDK
|
|
5
|
+
* `Run.steer` sends it (no wrapper: wrapped text was refused live);
|
|
6
|
+
* - on a later user turn, a `<system_reminder>` block in the message text;
|
|
7
|
+
* - provider notices about one exec result, as postToolUse hook context.
|
|
8
|
+
*/
|
|
9
|
+
const SYSTEM_UPDATE_BLOCK = /<system-update>([\s\S]*?)<\/system-update>/g;
|
|
10
|
+
const SYSTEM_REMINDER_OPEN = "<system_reminder>";
|
|
11
|
+
const SYSTEM_REMINDER_CLOSE = "</system_reminder>";
|
|
12
|
+
/** Cursor CLI's hook event name for context attached to a tool result. */
|
|
13
|
+
const POST_TOOL_USE_HOOK_EVENT = "postToolUse";
|
|
14
|
+
/** Cursor CLI rejects a hook additional_context longer than this. */
|
|
15
|
+
export const HOOK_ADDITIONAL_CONTEXT_MAX_CHARS = 10_000;
|
|
16
|
+
/** Delimiter for ordered note bodies in the restart-snapshot `hostNote` string. */
|
|
17
|
+
const HOST_NOTE_PERSIST_SEP = "\n\u001e\n";
|
|
18
|
+
/** Plain note text for one mid-turn injection, in host order. */
|
|
19
|
+
export function hostNoteText(notes) {
|
|
20
|
+
return notes.map(hostNoteBody).filter(Boolean).join("\n\n");
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* One postToolUse hook context per note, trimmed like Cursor CLI's carrier.
|
|
24
|
+
* A note over the CLI limit is split rather than dropped, so no text is lost.
|
|
25
|
+
*/
|
|
26
|
+
export function hostNoteHookContexts(notes) {
|
|
27
|
+
return notes.flatMap((note) => {
|
|
28
|
+
const body = hostNoteBody(note);
|
|
29
|
+
const chunks = [];
|
|
30
|
+
for (let start = 0; start < body.length;) {
|
|
31
|
+
let end = Math.min(body.length, start + HOOK_ADDITIONAL_CONTEXT_MAX_CHARS);
|
|
32
|
+
// Never split a surrogate pair.
|
|
33
|
+
if (end < body.length && /[\uD800-\uDBFF]/.test(body[end - 1]))
|
|
34
|
+
end--;
|
|
35
|
+
chunks.push(body.slice(start, end));
|
|
36
|
+
start = end;
|
|
37
|
+
}
|
|
38
|
+
return chunks.map((content) => ({ hook_event_name: POST_TOOL_USE_HOOK_EVENT, content }));
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/** One `<system_reminder>` block per note, for a user turn's text. */
|
|
42
|
+
export function wrapHostNoteForCursor(text) {
|
|
43
|
+
return `${SYSTEM_REMINDER_OPEN}\n${escapeReminderTags(hostNoteBody(text))}\n${SYSTEM_REMINDER_CLOSE}`;
|
|
44
|
+
}
|
|
45
|
+
export function wrapHostNotesForCursor(notes) {
|
|
46
|
+
return notes.filter((note) => hostNoteBody(note).length > 0).map(wrapHostNoteForCursor);
|
|
47
|
+
}
|
|
48
|
+
export function encodePersistedHostNotes(notes) {
|
|
49
|
+
const present = notes.filter((note) => note.length > 0);
|
|
50
|
+
if (present.length === 0)
|
|
51
|
+
return undefined;
|
|
52
|
+
return present.join(HOST_NOTE_PERSIST_SEP);
|
|
53
|
+
}
|
|
54
|
+
export function decodePersistedHostNotes(blob) {
|
|
55
|
+
if (!blob)
|
|
56
|
+
return [];
|
|
57
|
+
return blob.split(HOST_NOTE_PERSIST_SEP).filter((note) => note.length > 0);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Unwrap OpenCode `<system-update>` blocks. One host message may hold several
|
|
61
|
+
* (joined by `hostTailNote`); text that is not only such blocks stays as is.
|
|
62
|
+
*/
|
|
63
|
+
function hostNoteBody(text) {
|
|
64
|
+
const trimmed = text.trim();
|
|
65
|
+
const bodies = [];
|
|
66
|
+
let rest = trimmed;
|
|
67
|
+
for (const match of trimmed.matchAll(SYSTEM_UPDATE_BLOCK)) {
|
|
68
|
+
bodies.push(unescapeSystemUpdateText(match[1].trim()));
|
|
69
|
+
rest = rest.replace(match[0], "");
|
|
70
|
+
}
|
|
71
|
+
return bodies.length > 0 && rest.trim() === ""
|
|
72
|
+
? bodies.filter(Boolean).join("\n\n")
|
|
73
|
+
: trimmed;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Inverse of OpenCode's `escapeSystemUpdateText` (`packages/llm/src/protocols/
|
|
77
|
+
* shared.ts`): it escapes `&`, `<`, `>` inside the wrapper. `&` goes last.
|
|
78
|
+
*/
|
|
79
|
+
function unescapeSystemUpdateText(text) {
|
|
80
|
+
return text.replaceAll("<", "<").replaceAll(">", ">").replaceAll("&", "&");
|
|
81
|
+
}
|
|
82
|
+
/** Same neutralization Cursor CLI applies to text placed inside its reminders. */
|
|
83
|
+
function escapeReminderTags(text) {
|
|
84
|
+
return text.replace(/system_reminder/gi, "system_reminder_");
|
|
85
|
+
}
|
|
@@ -135,6 +135,7 @@ export function createMessageTypes() {
|
|
|
135
135
|
{ id: 4, name: "model", type: "string" },
|
|
136
136
|
{ id: 5, name: "resume", type: "string" },
|
|
137
137
|
{ id: 6, name: "agent_id", type: "string" },
|
|
138
|
+
{ id: 7, name: "attachments", type: "string", repeated: true },
|
|
138
139
|
]);
|
|
139
140
|
addType(root, "TaskToolCall", [{ id: 1, name: "args", type: "TaskToolArgs" }]);
|
|
140
141
|
// Native subagent execution is separate from the display-only TaskToolCall.
|
|
@@ -427,8 +428,33 @@ export function createMessageTypes() {
|
|
|
427
428
|
{ id: 15, name: "tool_call_delta", type: "ToolCallDeltaUpdate" },
|
|
428
429
|
{ id: 16, name: "step_started", type: "StepStarted" },
|
|
429
430
|
{ id: 17, name: "step_completed", type: "StepCompleted" },
|
|
431
|
+
{ id: 23, name: "context_injection_state", type: "ContextInjectionStateUpdate" },
|
|
430
432
|
{ id: 27, name: "tool_requests_listed", type: "ToolRequestsListedUpdate" },
|
|
431
|
-
], [{ name: "update", fields: ["text_delta", "tool_call_started", "tool_call_completed", "thinking_delta", "partial_tool_call", "heartbeat", "turn_ended", "tool_call_delta", "step_started", "step_completed", "tool_requests_listed"] }]);
|
|
433
|
+
], [{ name: "update", fields: ["text_delta", "tool_call_started", "tool_call_completed", "thinking_delta", "partial_tool_call", "heartbeat", "turn_ended", "tool_call_delta", "step_started", "step_completed", "context_injection_state", "tool_requests_listed"] }]);
|
|
434
|
+
// Server status for an `inject_context_action` (InteractionUpdate #23).
|
|
435
|
+
// Cursor CLI only tracks it to show queued steer delivery; it carries no output.
|
|
436
|
+
addType(root, "ContextInjectionQueued", []);
|
|
437
|
+
addType(root, "ContextInjectionQueuedForNextTurn", []);
|
|
438
|
+
addType(root, "ContextInjectionCancelled", []);
|
|
439
|
+
addType(root, "ContextInjectionDelivered", [
|
|
440
|
+
{ id: 1, name: "step", type: "int32" },
|
|
441
|
+
{ id: 2, name: "delivery_batch_id", type: "string" },
|
|
442
|
+
{ id: 3, name: "delivered_at_ms", type: "int64" },
|
|
443
|
+
]);
|
|
444
|
+
addType(root, "ContextInjectionRejected", [
|
|
445
|
+
{ id: 1, name: "reason", type: "string" },
|
|
446
|
+
]);
|
|
447
|
+
addType(root, "ContextInjectionState", [
|
|
448
|
+
{ id: 1, name: "queued", type: "ContextInjectionQueued" },
|
|
449
|
+
{ id: 2, name: "delivered", type: "ContextInjectionDelivered" },
|
|
450
|
+
{ id: 3, name: "queued_for_next_turn", type: "ContextInjectionQueuedForNextTurn" },
|
|
451
|
+
{ id: 4, name: "cancelled", type: "ContextInjectionCancelled" },
|
|
452
|
+
{ id: 5, name: "rejected", type: "ContextInjectionRejected" },
|
|
453
|
+
], [{ name: "state", fields: ["queued", "delivered", "queued_for_next_turn", "cancelled", "rejected"] }]);
|
|
454
|
+
addType(root, "ContextInjectionStateUpdate", [
|
|
455
|
+
{ id: 1, name: "injection_id", type: "string" },
|
|
456
|
+
{ id: 2, name: "state", type: "ContextInjectionState" },
|
|
457
|
+
]);
|
|
432
458
|
// ── Exec channel ──
|
|
433
459
|
// Field numbers match agent.v1. Extra fields we don't use are still declared
|
|
434
460
|
// so protobufjs doesn't drop them on decode.
|
|
@@ -465,16 +491,21 @@ export function createMessageTypes() {
|
|
|
465
491
|
{ id: 1, name: "path", type: "string" },
|
|
466
492
|
{ id: 2, name: "reason", type: "string" },
|
|
467
493
|
]);
|
|
494
|
+
addType(root, "ReadRejected", [
|
|
495
|
+
{ id: 1, name: "path", type: "string" },
|
|
496
|
+
{ id: 2, name: "reason", type: "string" },
|
|
497
|
+
]);
|
|
468
498
|
addType(root, "ReadResult", [
|
|
469
499
|
{ id: 1, name: "success", type: "ReadSuccess" },
|
|
470
500
|
{ id: 2, name: "error", type: "ReadError" },
|
|
501
|
+
{ id: 3, name: "rejected", type: "ReadRejected" },
|
|
471
502
|
{ id: 4, name: "file_not_found", type: "ReadFileNotFound" },
|
|
472
503
|
{ id: 5, name: "permission_denied", type: "ReadPermissionDenied" },
|
|
473
504
|
{ id: 6, name: "invalid_file", type: "ReadInvalidFile" },
|
|
474
505
|
], [
|
|
475
506
|
{
|
|
476
507
|
name: "result",
|
|
477
|
-
fields: ["success", "error", "file_not_found", "permission_denied", "invalid_file"],
|
|
508
|
+
fields: ["success", "error", "rejected", "file_not_found", "permission_denied", "invalid_file"],
|
|
478
509
|
},
|
|
479
510
|
]);
|
|
480
511
|
addType(root, "GrepArgs", [
|
|
@@ -554,11 +585,16 @@ export function createMessageTypes() {
|
|
|
554
585
|
{ id: 4, name: "error", type: "string" },
|
|
555
586
|
{ id: 5, name: "is_readonly", type: "bool" },
|
|
556
587
|
]);
|
|
588
|
+
addType(root, "WriteRejected", [
|
|
589
|
+
{ id: 1, name: "path", type: "string" },
|
|
590
|
+
{ id: 2, name: "reason", type: "string" },
|
|
591
|
+
]);
|
|
557
592
|
addType(root, "WriteResult", [
|
|
558
593
|
{ id: 1, name: "success", type: "WriteSuccess" },
|
|
559
594
|
{ id: 3, name: "permission_denied", type: "WritePermissionDenied" },
|
|
560
595
|
{ id: 5, name: "error", type: "WriteError" },
|
|
561
|
-
|
|
596
|
+
{ id: 6, name: "rejected", type: "WriteRejected" },
|
|
597
|
+
], [{ name: "result", fields: ["success", "permission_denied", "error", "rejected"] }]);
|
|
562
598
|
// agent.v1 Pi exec results. Cursor places Pi requests at ExecServerMessage
|
|
563
599
|
// fields #45..#51 and their corresponding ExecClientMessage results at
|
|
564
600
|
// #46..#52 (the result fields are deliberately offset by one).
|
|
@@ -634,10 +670,21 @@ export function createMessageTypes() {
|
|
|
634
670
|
{ id: 1, name: "path", type: "string" },
|
|
635
671
|
{ id: 2, name: "error", type: "string" },
|
|
636
672
|
]);
|
|
673
|
+
addType(root, "DeletePermissionDenied", [
|
|
674
|
+
{ id: 1, name: "path", type: "string" },
|
|
675
|
+
{ id: 2, name: "client_visible_error", type: "string" },
|
|
676
|
+
{ id: 3, name: "is_readonly", type: "bool" },
|
|
677
|
+
]);
|
|
678
|
+
addType(root, "DeleteRejected", [
|
|
679
|
+
{ id: 1, name: "path", type: "string" },
|
|
680
|
+
{ id: 2, name: "reason", type: "string" },
|
|
681
|
+
]);
|
|
637
682
|
addType(root, "DeleteResult", [
|
|
638
683
|
{ id: 1, name: "success", type: "DeleteSuccess" },
|
|
684
|
+
{ id: 4, name: "permission_denied", type: "DeletePermissionDenied" },
|
|
685
|
+
{ id: 6, name: "rejected", type: "DeleteRejected" },
|
|
639
686
|
{ id: 7, name: "error", type: "DeleteError" },
|
|
640
|
-
], [{ name: "result", fields: ["success", "error"] }]);
|
|
687
|
+
], [{ name: "result", fields: ["success", "permission_denied", "rejected", "error"] }]);
|
|
641
688
|
addType(root, "LsArgs", [
|
|
642
689
|
{ id: 1, name: "path", type: "string" },
|
|
643
690
|
{ id: 2, name: "ignore", type: "string", repeated: true },
|
|
@@ -657,10 +704,15 @@ export function createMessageTypes() {
|
|
|
657
704
|
{ id: 1, name: "path", type: "string" },
|
|
658
705
|
{ id: 2, name: "error", type: "string" },
|
|
659
706
|
]);
|
|
707
|
+
addType(root, "LsRejected", [
|
|
708
|
+
{ id: 1, name: "path", type: "string" },
|
|
709
|
+
{ id: 2, name: "reason", type: "string" },
|
|
710
|
+
]);
|
|
660
711
|
addType(root, "LsResult", [
|
|
661
712
|
{ id: 1, name: "success", type: "LsSuccess" },
|
|
662
713
|
{ id: 2, name: "error", type: "LsError" },
|
|
663
|
-
|
|
714
|
+
{ id: 3, name: "rejected", type: "LsRejected" },
|
|
715
|
+
], [{ name: "result", fields: ["success", "error", "rejected"] }]);
|
|
664
716
|
addType(root, "ShellArgs", [
|
|
665
717
|
{ id: 1, name: "command", type: "string" },
|
|
666
718
|
{ id: 2, name: "working_directory", type: "string" },
|
|
@@ -670,6 +722,7 @@ export function createMessageTypes() {
|
|
|
670
722
|
// foreground cancellation deadline from a soft background handoff.
|
|
671
723
|
{ id: 13, name: "timeout_behavior", type: "uint32" },
|
|
672
724
|
{ id: 14, name: "hard_timeout", type: "uint32" },
|
|
725
|
+
{ id: 15, name: "description", type: "string" },
|
|
673
726
|
]);
|
|
674
727
|
addType(root, "ShellStreamStart", []); // optional SandboxPolicy only; empty is valid
|
|
675
728
|
addType(root, "ShellStreamStdout", [{ id: 1, name: "data", type: "string" }]);
|
|
@@ -740,9 +793,10 @@ export function createMessageTypes() {
|
|
|
740
793
|
{ id: 2, name: "failure", type: "ShellFailure" },
|
|
741
794
|
{ id: 3, name: "timeout", type: "ShellTimeout" },
|
|
742
795
|
{ id: 4, name: "rejected", type: "ShellRejected" },
|
|
796
|
+
{ id: 7, name: "permission_denied", type: "ShellPermissionDenied" },
|
|
743
797
|
{ id: 102, name: "is_background", type: "bool" },
|
|
744
798
|
{ id: 104, name: "pid", type: "uint32" },
|
|
745
|
-
], [{ name: "result", fields: ["success", "failure", "timeout", "rejected"] }]);
|
|
799
|
+
], [{ name: "result", fields: ["success", "failure", "timeout", "rejected", "permission_denied"] }]);
|
|
746
800
|
addType(root, "DiagnosticsError", [
|
|
747
801
|
{ id: 1, name: "path", type: "string" },
|
|
748
802
|
{ id: 2, name: "error", type: "string" },
|
|
@@ -924,6 +978,15 @@ export function createMessageTypes() {
|
|
|
924
978
|
{ id: 3, name: "rejected", type: "ShellRejected" },
|
|
925
979
|
{ id: 4, name: "permission_denied", type: "ShellPermissionDenied" },
|
|
926
980
|
], [{ name: "result", fields: ["success", "error", "rejected", "permission_denied"] }]);
|
|
981
|
+
// Cursor CLI postToolUse hook context: ExecClientMessage #45 on a unary
|
|
982
|
+
// result, ShellStream #8 on a shell stream (agent.v1 hook_additional_context).
|
|
983
|
+
addType(root, "HookAdditionalContext", [
|
|
984
|
+
{ id: 1, name: "hook_event_name", type: "string" },
|
|
985
|
+
{ id: 2, name: "content", type: "string" },
|
|
986
|
+
]);
|
|
987
|
+
addType(root, "ShellStreamHookContext", [
|
|
988
|
+
{ id: 1, name: "hook_additional_contexts", type: "HookAdditionalContext", repeated: true },
|
|
989
|
+
]);
|
|
927
990
|
addType(root, "ShellStream", [
|
|
928
991
|
{ id: 1, name: "stdout", type: "ShellStreamStdout" },
|
|
929
992
|
{ id: 2, name: "stderr", type: "ShellStreamStderr" },
|
|
@@ -932,7 +995,8 @@ export function createMessageTypes() {
|
|
|
932
995
|
{ id: 5, name: "rejected", type: "ShellRejected" },
|
|
933
996
|
{ id: 6, name: "permission_denied", type: "ShellPermissionDenied" },
|
|
934
997
|
{ id: 7, name: "backgrounded", type: "ShellStreamBackgrounded" },
|
|
935
|
-
|
|
998
|
+
{ id: 8, name: "hook_context", type: "ShellStreamHookContext" },
|
|
999
|
+
], [{ name: "event", fields: ["stdout", "stderr", "exit", "start", "rejected", "permission_denied", "backgrounded", "hook_context"] }]);
|
|
936
1000
|
addType(root, "GlobArgs", [
|
|
937
1001
|
{ id: 1, name: "target_directory", type: "string" },
|
|
938
1002
|
{ id: 2, name: "glob_pattern", type: "string" },
|
|
@@ -962,6 +1026,14 @@ export function createMessageTypes() {
|
|
|
962
1026
|
{ id: 1, name: "text", type: "McpTextContent" },
|
|
963
1027
|
{ id: 2, name: "image", type: "McpImageContent" },
|
|
964
1028
|
], [{ name: "content", fields: ["text", "image"] }]);
|
|
1029
|
+
addType(root, "McpRejected", [
|
|
1030
|
+
{ id: 1, name: "reason", type: "string" },
|
|
1031
|
+
{ id: 2, name: "is_readonly", type: "bool" },
|
|
1032
|
+
]);
|
|
1033
|
+
addType(root, "McpPermissionDenied", [
|
|
1034
|
+
{ id: 1, name: "error", type: "string" },
|
|
1035
|
+
{ id: 2, name: "is_readonly", type: "bool" },
|
|
1036
|
+
]);
|
|
965
1037
|
addType(root, "McpSuccess", [
|
|
966
1038
|
{ id: 1, name: "content", type: "McpToolResultContentItem", repeated: true },
|
|
967
1039
|
{ id: 2, name: "is_error", type: "bool" },
|
|
@@ -974,7 +1046,9 @@ export function createMessageTypes() {
|
|
|
974
1046
|
addType(root, "McpResult", [
|
|
975
1047
|
{ id: 1, name: "success", type: "McpSuccess" },
|
|
976
1048
|
{ id: 2, name: "error", type: "McpError" },
|
|
977
|
-
|
|
1049
|
+
{ id: 3, name: "rejected", type: "McpRejected" },
|
|
1050
|
+
{ id: 4, name: "permission_denied", type: "McpPermissionDenied" },
|
|
1051
|
+
], [{ name: "result", fields: ["success", "error", "rejected", "permission_denied"] }]);
|
|
978
1052
|
// McpToolDefinition — agent.v1 shape for RequestContext.tools (#7) and
|
|
979
1053
|
// AgentRunRequest.mcp_tools (#4). Defined early so RequestContext* can
|
|
980
1054
|
// reference it.
|
|
@@ -1259,6 +1333,7 @@ export function createMessageTypes() {
|
|
|
1259
1333
|
{ id: 49, name: "pi_grep_args", type: "PiGrepToolArgs" },
|
|
1260
1334
|
{ id: 50, name: "pi_find_args", type: "PiFindToolArgs" },
|
|
1261
1335
|
{ id: 51, name: "pi_ls_args", type: "PiLsToolArgs" },
|
|
1336
|
+
{ id: 55, name: "accept_hook_additional_contexts", type: "bool" },
|
|
1262
1337
|
], [{ name: "args", fields: [
|
|
1263
1338
|
"shell_args", "write_args", "delete_args", "grep_args", "read_args", "ls_args",
|
|
1264
1339
|
"request_context_args", "mcp_args", "shell_stream_args", "background_shell_spawn_args",
|
|
@@ -1272,6 +1347,7 @@ export function createMessageTypes() {
|
|
|
1272
1347
|
{ id: 1, name: "id", type: "uint32" },
|
|
1273
1348
|
{ id: 15, name: "exec_id", type: "string" },
|
|
1274
1349
|
{ id: 39, name: "local_execution_time_ms", type: "uint64" },
|
|
1350
|
+
{ id: 45, name: "hook_additional_contexts", type: "HookAdditionalContext", repeated: true },
|
|
1275
1351
|
{ id: 2, name: "shell_result", type: "ShellResult" },
|
|
1276
1352
|
{ id: 3, name: "write_result", type: "WriteResult" },
|
|
1277
1353
|
{ id: 4, name: "delete_result", type: "DeleteResult" },
|
|
@@ -1452,9 +1528,16 @@ export function createMessageTypes() {
|
|
|
1452
1528
|
{ id: 1, name: "producer", type: "string" },
|
|
1453
1529
|
{ id: 2, name: "content", type: "string" },
|
|
1454
1530
|
]);
|
|
1531
|
+
// Cursor SDK `Run.steer` / CLI steering payload: a user message added to the
|
|
1532
|
+
// running turn. `request_context` is optional and unused by the SDK.
|
|
1533
|
+
addType(root, "UserContextInjection", [
|
|
1534
|
+
{ id: 1, name: "user_message", type: "UserMessage" },
|
|
1535
|
+
{ id: 2, name: "request_context", type: "RequestContext" },
|
|
1536
|
+
]);
|
|
1455
1537
|
addType(root, "InjectContextAction", [
|
|
1456
1538
|
{ id: 1, name: "injection_id", type: "string" },
|
|
1457
1539
|
{ id: 2, name: "expected_run_id", type: "string" },
|
|
1540
|
+
{ id: 3, name: "user_context", type: "UserContextInjection" },
|
|
1458
1541
|
{ id: 4, name: "system_context", type: "SystemContextInjection" },
|
|
1459
1542
|
]);
|
|
1460
1543
|
addType(root, "ConversationAction", [
|
|
@@ -12,16 +12,17 @@ export type RunRequestInput = {
|
|
|
12
12
|
/** Stable parent group; unlike conversationId, this survives compaction/rebase. */
|
|
13
13
|
conversationGroupId?: string;
|
|
14
14
|
/**
|
|
15
|
-
* Prior chat turns for a
|
|
16
|
-
* Tool outputs, when required for
|
|
17
|
-
* user-role OpenCode host
|
|
15
|
+
* Prior chat turns for a Run without a checkpoint, carried in the live user
|
|
16
|
+
* text (see {@link seedHistoryUserText}). Tool outputs, when required for
|
|
17
|
+
* compaction/recovery, are represented as user-role OpenCode host
|
|
18
|
+
* observations rather than assistant-authored prose.
|
|
18
19
|
*/
|
|
19
20
|
history?: SeedHistoryMessage[];
|
|
20
21
|
/**
|
|
21
22
|
* Opaque ConversationStateStructure bytes from the last
|
|
22
23
|
* conversation_checkpoint_update for this conversation_id. When set, echoed
|
|
23
|
-
* as AgentRunRequest.conversation_state (CLI parity). When absent,
|
|
24
|
-
* state
|
|
24
|
+
* as AgentRunRequest.conversation_state (CLI parity). When absent, an empty
|
|
25
|
+
* seed state is sent and `history` travels in the user text.
|
|
25
26
|
*/
|
|
26
27
|
conversationState?: Uint8Array;
|
|
27
28
|
parameterValues?: Array<{
|
|
@@ -44,26 +45,32 @@ export type RunRequestInput = {
|
|
|
44
45
|
mode?: number;
|
|
45
46
|
};
|
|
46
47
|
/**
|
|
47
|
-
*
|
|
48
|
+
* Empty ConversationStateStructure for a Run without a checkpoint (turn 1,
|
|
49
|
+
* compaction, rebase, reseed). After the first checkpoint arrives we stop
|
|
50
|
+
* inventing state and echo the server's opaque structure instead (CLI behavior).
|
|
48
51
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* so Cursor
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* `
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* `system` history entries are dropped here rather than duplicated.
|
|
52
|
+
* Nothing goes in `root_prompt_messages_json` (#1). Neither Cursor client
|
|
53
|
+
* writes it: Cursor checkpoints fill it with references to the conversation's
|
|
54
|
+
* own rendered root prompt. A client-written entry stands in for that prompt,
|
|
55
|
+
* so Cursor stops rendering RequestContext rules, skills and subagents — the
|
|
56
|
+
* host system-instructions rule included. Verified live 2026-10-09
|
|
57
|
+
* (claude-sonnet-5-5): two seeded history messages there gave checkpoint
|
|
58
|
+
* categories `rules:0, subagents:0` and a reply that ignored the rule; the same
|
|
59
|
+
* history in the user text kept both. History therefore travels in the user
|
|
60
|
+
* text ({@link seedHistoryUserText}).
|
|
59
61
|
*
|
|
60
62
|
* We deliberately do NOT use `AgentRunRequest.custom_system_prompt` (#8): that
|
|
61
63
|
* field is the internal `--system-prompt` CLI override and the server rejects
|
|
62
64
|
* it for normal accounts.
|
|
63
65
|
*/
|
|
64
|
-
export declare function buildSeedConversationState(
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
export declare function buildSeedConversationState(): Uint8Array;
|
|
67
|
+
/**
|
|
68
|
+
* Prior turns of a Run without a checkpoint, as a transcript ahead of the live
|
|
69
|
+
* user text. `system` entries are dropped: host system context travels as the
|
|
70
|
+
* system-instructions rule in RequestContext. Closing tags inside a message are
|
|
71
|
+
* escaped so a message cannot end its own block early.
|
|
72
|
+
*/
|
|
73
|
+
export declare function seedHistoryUserText(text: string, history: readonly SeedHistoryMessage[] | undefined): string;
|
|
67
74
|
/**
|
|
68
75
|
* Build an AgentClientMessage{run_request} for a conversation turn.
|
|
69
76
|
*
|
|
@@ -75,3 +82,12 @@ export declare function buildRunRequest(input: RunRequestInput): Uint8Array;
|
|
|
75
82
|
* Build a heartbeat message.
|
|
76
83
|
*/
|
|
77
84
|
export declare function buildHeartbeat(): Uint8Array;
|
|
85
|
+
/**
|
|
86
|
+
* ACM #5 `exec_client_control_message.heartbeat{id}`: Cursor CLI sends it every
|
|
87
|
+
* 3 s for each exec it is still running, keyed by the exec message id. The CLI
|
|
88
|
+
* leaves a zero id out, as proto3 does for a default scalar; protobufjs would
|
|
89
|
+
* write it explicitly, so it is omitted here to send the CLI's exact bytes.
|
|
90
|
+
*/
|
|
91
|
+
export declare function buildExecHeartbeat(execId: number): Uint8Array;
|
|
92
|
+
/** Stops the live Run, as Cursor CLI does on a user Stop before it closes the stream. */
|
|
93
|
+
export declare function buildCancelAction(reason: string): Uint8Array;
|