@bitkyc08/opencodex 2.28.0 → 2.29.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/README.md +9 -1
- package/gui/dist/assets/index-BNESwCzn.js +102 -0
- package/gui/dist/assets/index-CH7ncHCC.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/base.ts +3 -1
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/discovery.ts +25 -0
- package/src/adapters/cursor/live-transport.ts +19 -2
- package/src/adapters/cursor/native-exec.ts +47 -2
- package/src/adapters/cursor/protobuf-request.ts +80 -23
- package/src/adapters/cursor/request-builder.ts +119 -5
- package/src/adapters/cursor/transport.ts +5 -0
- package/src/adapters/cursor/types.ts +13 -0
- package/src/adapters/cursor.ts +109 -3
- package/src/adapters/google-antigravity-replay.ts +31 -4
- package/src/adapters/google.ts +323 -38
- package/src/adapters/openai-chat.ts +19 -0
- package/src/adapters/openai-responses.ts +218 -37
- package/src/bridge.ts +38 -20
- package/src/claude/desktop-3p.ts +15 -6
- package/src/cli/agent.ts +44 -1
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/dispatch.ts +24 -1
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/help.ts +1 -1
- package/src/cli/index.ts +40 -38
- package/src/cli/integrations.ts +1 -1
- package/src/cli/registry.ts +2 -2
- package/src/clients/config-export.ts +119 -20
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/inject.ts +46 -14
- package/src/codex/journal.ts +22 -8
- package/src/config.ts +1 -0
- package/src/generated/compatibility-version.json +134 -66
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/registry.ts +25 -2
- package/src/integrations/writer.ts +32 -1
- package/src/lab/public/signature.ts +25 -1
- package/src/lab/subject/behavior-fingerprint.ts +1 -1
- package/src/lib/redact.ts +2 -2
- package/src/oauth/log.ts +3 -1
- package/src/providers/derive.ts +9 -0
- package/src/providers/fastwire.ts +24 -18
- package/src/providers/openai-tiers.ts +60 -1
- package/src/providers/registry.ts +14 -7
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/responses/compaction.ts +18 -0
- package/src/responses/custom-tool-compat.ts +70 -5
- package/src/responses/namespace-tool-compat.ts +356 -0
- package/src/responses/parser.ts +2 -2
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/reasoning-replay-cache.ts +125 -7
- package/src/responses/spill-store.ts +6 -1
- package/src/responses/state.ts +11 -0
- package/src/router.ts +14 -0
- package/src/routing/compatibility/behavior.ts +1 -0
- package/src/server/auth-cors.ts +4 -0
- package/src/server/management/agent-settings-routes.ts +57 -9
- package/src/server/management/config-routes.ts +134 -15
- package/src/server/management/integration-routes.ts +8 -55
- package/src/server/management/model-routes.ts +23 -1
- package/src/server/management/provider-routes.ts +22 -0
- package/src/server/management/vision-sidecar-options.ts +18 -11
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/responses/core.ts +588 -78
- package/src/server/responses/responses-field-backfill.ts +7 -11
- package/src/server/responses/terminal-guard.ts +22 -11
- package/src/server/responses-custom-tool-repair.ts +3 -1
- package/src/server/responses-reasoning-summary-rewrite.ts +7 -0
- package/src/server/responses-tool-search-repair.ts +64 -14
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/types/config.ts +26 -5
- package/src/types/provider.ts +18 -0
- package/src/types/request.ts +26 -0
- package/src/types.ts +1 -0
- package/src/usage/log.ts +2 -0
- package/src/vision/index.ts +8 -9
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +139 -12
- package/src/web-search/loop.ts +45 -5
- package/src/web-search/parse.ts +34 -23
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/xai-executor.ts +219 -0
- package/gui/dist/assets/index-D2sP-biU.js +0 -102
- package/gui/dist/assets/index-DQsMZzI5.css +0 -1
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
} from "../../types";
|
|
10
10
|
import { isAllowedToolChoice, namespacedToolName, toolChoiceAliases, type OcxTool, type OcxToolChoice } from "../../types";
|
|
11
11
|
import type { CursorRequestMessage, CursorRequestedModelParameter, CursorRunRequest } from "./types";
|
|
12
|
-
import { cursorWireModelSelection, type CursorRoutingLevel } from "./discovery";
|
|
12
|
+
import { cursorCheckpointModelAffinityId, cursorWireModelSelection, type CursorRoutingLevel } from "./discovery";
|
|
13
13
|
import { cursorEffortSuffix, cursorRequestWireModelIdWithEffort } from "./effort-map";
|
|
14
14
|
import {
|
|
15
15
|
cursorMcpToolEncodedSize,
|
|
@@ -25,6 +25,12 @@ import {
|
|
|
25
25
|
isCursorWaitTool,
|
|
26
26
|
} from "./tool-definitions";
|
|
27
27
|
import { lookupCursorThreadConversation } from "./thread-continuity";
|
|
28
|
+
import {
|
|
29
|
+
getCursorCheckpoint,
|
|
30
|
+
getCursorCheckpointForPrefix,
|
|
31
|
+
type CursorCheckpointInvalidationReason,
|
|
32
|
+
type CursorCheckpointSnapshot,
|
|
33
|
+
} from "./checkpoint-store";
|
|
28
34
|
|
|
29
35
|
/** Probe-verified Cursor Connect boundaries, with byte headroom for the enclosing field. */
|
|
30
36
|
export const CURSOR_TOOL_COUNT_LIMIT = 330;
|
|
@@ -285,24 +291,120 @@ export function resolveCursorConversationId(
|
|
|
285
291
|
options: CreateCursorRequestOptions = {},
|
|
286
292
|
): string {
|
|
287
293
|
if (options.forceFreshConversation === true) return generatedCursorConversationId();
|
|
288
|
-
// Helper/shadow/compaction turns must not append into the parent's Cursor conversation,
|
|
289
|
-
// even when previous_response_id restored the parent's remembered id.
|
|
290
294
|
if (parsed._cursorIsolateConversation === true) return generatedCursorConversationId();
|
|
291
295
|
if (parsed._cursorConversationId) return parsed._cursorConversationId;
|
|
292
296
|
const threadId = parsed._clientThreadId?.trim();
|
|
293
297
|
if (threadId) {
|
|
294
298
|
const recovered = lookupCursorThreadConversation(threadId, parsed._cursorIdentityScope);
|
|
295
299
|
if (recovered) return recovered;
|
|
296
|
-
return cursorConversationIdFromClientThread(threadId
|
|
300
|
+
return cursorConversationIdFromClientThread(`thread:${threadId}`, parsed._cursorIdentityScope);
|
|
297
301
|
}
|
|
298
302
|
return generatedCursorConversationId();
|
|
299
303
|
}
|
|
300
304
|
|
|
305
|
+
function updateFramed(hash: ReturnType<typeof createHash>, value: string): void {
|
|
306
|
+
const bytes = Buffer.from(value, "utf8");
|
|
307
|
+
const length = Buffer.allocUnsafe(4);
|
|
308
|
+
length.writeUInt32BE(bytes.byteLength);
|
|
309
|
+
hash.update(length);
|
|
310
|
+
hash.update(bytes);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function cursorInstructionDigest(parsed: OcxParsedRequest): string {
|
|
314
|
+
const hash = createHash("sha256").update("ocx:cursor:sys:");
|
|
315
|
+
for (const line of parsed.context.systemPrompt ?? []) updateFramed(hash, line);
|
|
316
|
+
for (const message of parsed.context.messages) {
|
|
317
|
+
if (message.role !== "developer") continue;
|
|
318
|
+
updateFramed(hash, contentToText(message.content));
|
|
319
|
+
}
|
|
320
|
+
return hash.digest("hex");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function cursorCoveredPrefixDigest(parsed: OcxParsedRequest, coveredMessageCount: number): string {
|
|
324
|
+
const hash = createHash("sha256").update("ocx:cursor:prefix:");
|
|
325
|
+
updateFramed(hash, cursorInstructionDigest(parsed));
|
|
326
|
+
for (const message of parsed.context.messages.slice(0, coveredMessageCount)) {
|
|
327
|
+
updateFramed(hash, message.role);
|
|
328
|
+
updateFramed(hash, contentToText(message.content));
|
|
329
|
+
}
|
|
330
|
+
return hash.digest("hex");
|
|
331
|
+
}
|
|
332
|
+
|
|
301
333
|
export interface CreateCursorRequestOptions {
|
|
302
334
|
/** Force a brand-new Cursor conversation id even when remembered state exists. */
|
|
303
335
|
forceFreshConversation?: boolean;
|
|
304
336
|
}
|
|
305
337
|
|
|
338
|
+
function lookupPrefixSnapshot(
|
|
339
|
+
parsed: OcxParsedRequest,
|
|
340
|
+
request: CursorRunRequest,
|
|
341
|
+
identityScope: string,
|
|
342
|
+
): CursorCheckpointSnapshot | undefined {
|
|
343
|
+
const systemDigest = cursorInstructionDigest(parsed);
|
|
344
|
+
const modelId = cursorCheckpointModelAffinityId(request.modelId);
|
|
345
|
+
for (let covered = parsed.context.messages.length; covered >= 1; covered--) {
|
|
346
|
+
const snapshot = getCursorCheckpointForPrefix({
|
|
347
|
+
prefixDigest: cursorCoveredPrefixDigest(parsed, covered),
|
|
348
|
+
systemDigest,
|
|
349
|
+
coveredMessageCount: covered,
|
|
350
|
+
identityScope,
|
|
351
|
+
modelId,
|
|
352
|
+
});
|
|
353
|
+
if (snapshot) return snapshot;
|
|
354
|
+
}
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function lineageMismatch(
|
|
359
|
+
parsed: OcxParsedRequest,
|
|
360
|
+
snapshot: CursorCheckpointSnapshot,
|
|
361
|
+
): CursorCheckpointInvalidationReason | undefined {
|
|
362
|
+
const covered = snapshot.coveredMessageCount;
|
|
363
|
+
if (covered === undefined || covered < 0 || covered > parsed.context.messages.length) {
|
|
364
|
+
return "lineage_mismatch";
|
|
365
|
+
}
|
|
366
|
+
if (!snapshot.prefixDigest || !snapshot.systemDigest) return "lineage_mismatch";
|
|
367
|
+
if (snapshot.systemDigest !== cursorInstructionDigest(parsed)) return "lineage_mismatch";
|
|
368
|
+
if (snapshot.prefixDigest !== cursorCoveredPrefixDigest(parsed, covered)) return "lineage_mismatch";
|
|
369
|
+
const lastRole = parsed.context.messages.at(-1)?.role;
|
|
370
|
+
if (lastRole === "toolResult" && covered >= parsed.context.messages.length) return "trailing_tool_result";
|
|
371
|
+
return undefined;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function resolveCursorCheckpoint(
|
|
375
|
+
parsed: OcxParsedRequest,
|
|
376
|
+
request: CursorRunRequest,
|
|
377
|
+
options: CreateCursorRequestOptions,
|
|
378
|
+
): { snapshot: CursorCheckpointSnapshot } | { reason: CursorCheckpointInvalidationReason } {
|
|
379
|
+
if (options.forceFreshConversation === true) return { reason: "force_fresh" };
|
|
380
|
+
if (parsed._compactionRequest === true || parsed._contextCompactionBoundary === true) return { reason: "compaction" };
|
|
381
|
+
const isolated = parsed._cursorIsolateConversation === true;
|
|
382
|
+
const cursorState = parsed._providerContinuation?.cursor;
|
|
383
|
+
const ref = isolated ? undefined : cursorState?.checkpointRef;
|
|
384
|
+
const identityScope = parsed._cursorIdentityScope?.trim() || "local";
|
|
385
|
+
let snapshot: CursorCheckpointSnapshot | undefined;
|
|
386
|
+
if (ref) {
|
|
387
|
+
snapshot = getCursorCheckpoint(ref);
|
|
388
|
+
if (!snapshot) return { reason: "expired" };
|
|
389
|
+
} else {
|
|
390
|
+
snapshot = lookupPrefixSnapshot(parsed, request, identityScope);
|
|
391
|
+
if (!snapshot) return { reason: "missing_ref" };
|
|
392
|
+
}
|
|
393
|
+
if (!isolated && snapshot.conversationId !== request.conversationId && ref) {
|
|
394
|
+
return { reason: "conversation_changed" };
|
|
395
|
+
}
|
|
396
|
+
if (snapshot.identityScope !== identityScope) return { reason: "identity_changed" };
|
|
397
|
+
if (cursorCheckpointModelAffinityId(snapshot.modelId) !== cursorCheckpointModelAffinityId(request.modelId)) {
|
|
398
|
+
return { reason: "model_changed" };
|
|
399
|
+
}
|
|
400
|
+
if (parsed.context.messages.at(-1)?.role !== "toolResult" && cursorState?.checkpointUsable === false) {
|
|
401
|
+
return { reason: "trailing_tool_result" };
|
|
402
|
+
}
|
|
403
|
+
const lineage = lineageMismatch(parsed, snapshot);
|
|
404
|
+
if (lineage) return { reason: lineage };
|
|
405
|
+
return { snapshot };
|
|
406
|
+
}
|
|
407
|
+
|
|
306
408
|
export function createCursorRequest(
|
|
307
409
|
parsed: OcxParsedRequest,
|
|
308
410
|
options: CreateCursorRequestOptions = {},
|
|
@@ -315,7 +417,7 @@ export function createCursorRequest(
|
|
|
315
417
|
const budget = applyCursorToolBudget(visibleTools, parsed.options.toolChoice);
|
|
316
418
|
const limitNote = catalogLimitNote(budget.tools, budget.omitted);
|
|
317
419
|
const model = normalizeCursorModelId(parsed.modelId, parsed.options.reasoning);
|
|
318
|
-
|
|
420
|
+
const request: CursorRunRequest = {
|
|
319
421
|
modelId: model.modelId,
|
|
320
422
|
...(model.requestedModelParameters ? { requestedModelParameters: model.requestedModelParameters } : {}),
|
|
321
423
|
...(model.routingLevel ? { routingLevel: model.routingLevel } : {}),
|
|
@@ -329,4 +431,16 @@ export function createCursorRequest(
|
|
|
329
431
|
...(parsed.options.toolChoice ? { toolChoice: parsed.options.toolChoice } : {}),
|
|
330
432
|
...(parsed.options.parallelToolCalls !== undefined ? { parallelToolCalls: parsed.options.parallelToolCalls } : {}),
|
|
331
433
|
};
|
|
434
|
+
const resolved = resolveCursorCheckpoint(parsed, request, options);
|
|
435
|
+
if ("reason" in resolved) {
|
|
436
|
+
request.continuationMode = "full-replay";
|
|
437
|
+
request.checkpointInvalidationReason = resolved.reason;
|
|
438
|
+
return request;
|
|
439
|
+
}
|
|
440
|
+
request.checkpointBytes = resolved.snapshot.checkpointBytes;
|
|
441
|
+
request.continuationMode = "checkpoint";
|
|
442
|
+
if (parsed.context.messages.at(-1)?.role === "toolResult" && resolved.snapshot.coveredMessageCount !== undefined) {
|
|
443
|
+
request.checkpointSuffixStart = resolved.snapshot.coveredMessageCount;
|
|
444
|
+
}
|
|
445
|
+
return request;
|
|
332
446
|
}
|
|
@@ -12,6 +12,11 @@ export interface CursorTransport {
|
|
|
12
12
|
* accepted is never replayed. Absent (undefined) is treated as "committed" — safe by default.
|
|
13
13
|
*/
|
|
14
14
|
requestCommitted?(): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Last ConversationStateStructure captured from conversationCheckpointUpdate on this transport.
|
|
17
|
+
* Test and adapter seams use this instead of reaching into LiveCursorTransport.
|
|
18
|
+
*/
|
|
19
|
+
capturedConversationCheckpoint?(): Uint8Array | undefined;
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
export interface CursorTransportFactoryInput {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { OcxUsage } from "../../types";
|
|
2
2
|
import type { OcxMessage, OcxRequestOptions, OcxTool } from "../../types";
|
|
3
3
|
import type { CursorRoutingLevel } from "./discovery";
|
|
4
|
+
import type { CursorCheckpointInvalidationReason } from "./checkpoint-store";
|
|
4
5
|
|
|
5
6
|
export interface CursorRequestedModelParameter {
|
|
6
7
|
id: string;
|
|
@@ -31,6 +32,18 @@ export interface CursorRunRequest {
|
|
|
31
32
|
* pre-compaction history being summarized and must not become the next turn's carry-forward total.
|
|
32
33
|
*/
|
|
33
34
|
contextUsageStoreCheckpoints?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Reuse a previously captured ConversationStateStructure instead of rebuilding historical
|
|
37
|
+
* root/turn blobs. Absent means the existing full-replay path.
|
|
38
|
+
*/
|
|
39
|
+
checkpointBytes?: Uint8Array;
|
|
40
|
+
continuationMode?: "full-replay" | "checkpoint";
|
|
41
|
+
checkpointInvalidationReason?: CursorCheckpointInvalidationReason;
|
|
42
|
+
/**
|
|
43
|
+
* When set with checkpointBytes, only this suffix of rawMessages is replayed onto the
|
|
44
|
+
* decoded ConversationStateStructure. Used for tool-result continuations.
|
|
45
|
+
*/
|
|
46
|
+
checkpointSuffixStart?: number;
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
export interface CursorRequestMessage {
|
package/src/adapters/cursor.ts
CHANGED
|
@@ -4,16 +4,27 @@ import type { ProviderAdapter } from "./base";
|
|
|
4
4
|
import { isTranslatorBudgetExceededError } from "../lib/translator-budget";
|
|
5
5
|
import { cursorExecDeniedMessage, cursorRequestDeclaresFullAccess } from "./cursor/exec-policy";
|
|
6
6
|
import { isCursorBenignCancelError, isCursorInvalidArgumentError, safeCursorErrorMessage } from "./cursor/cursor-errors";
|
|
7
|
-
import { isCursorExternalWireModel } from "./cursor/discovery";
|
|
7
|
+
import { cursorCheckpointModelAffinityId, isCursorExternalWireModel } from "./cursor/discovery";
|
|
8
8
|
import { createCursorKvStore, type CursorKvStore } from "./cursor/kv-store";
|
|
9
9
|
import { mapCursorServerMessage } from "./cursor/message-mapper";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
createCursorRequest,
|
|
12
|
+
cursorCoveredPrefixDigest,
|
|
13
|
+
cursorInstructionDigest,
|
|
14
|
+
} from "./cursor/request-builder";
|
|
11
15
|
import {
|
|
12
16
|
createLiveCursorTransport,
|
|
13
17
|
CursorMissingCredentialError,
|
|
14
18
|
rekeyCursorContextUsage,
|
|
15
19
|
resolveCursorToken,
|
|
20
|
+
capturedCursorCheckpointBytes,
|
|
16
21
|
} from "./cursor/live-transport";
|
|
22
|
+
import {
|
|
23
|
+
commitCursorCheckpoint,
|
|
24
|
+
cursorCheckpointRefHash,
|
|
25
|
+
invalidateCursorCheckpoint,
|
|
26
|
+
} from "./cursor/checkpoint-store";
|
|
27
|
+
import { debugProviderDiagnostic } from "../lib/debug";
|
|
17
28
|
import { rememberCursorThreadConversation } from "./cursor/thread-continuity";
|
|
18
29
|
import { runCursorTurnWithRetry } from "./cursor/transport-retry";
|
|
19
30
|
import {
|
|
@@ -96,6 +107,7 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
|
|
|
96
107
|
/* Missing credential is handled by the live transport path below. */
|
|
97
108
|
}
|
|
98
109
|
}
|
|
110
|
+
const inheritedCheckpointRef = _parsed._providerContinuation?.cursor?.checkpointRef;
|
|
99
111
|
const previousConversationId = _parsed._cursorConversationId;
|
|
100
112
|
let request = createCursorRequest(_parsed);
|
|
101
113
|
// The builder may derive a stable provider id from the client thread when Responses state
|
|
@@ -112,6 +124,48 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
|
|
|
112
124
|
let emittedOutput = false;
|
|
113
125
|
let replayUnsafe = false;
|
|
114
126
|
const lastRawIsToolResult = _parsed.context.messages.at(-1)?.role === "toolResult";
|
|
127
|
+
let completedNormally = false;
|
|
128
|
+
let lastTransport: { captured?: Uint8Array } | undefined;
|
|
129
|
+
let emittedClientTool = false;
|
|
130
|
+
|
|
131
|
+
const commitCapturedCheckpoint = (activeRequest: ReturnType<typeof createCursorRequest>): void => {
|
|
132
|
+
if (
|
|
133
|
+
replayUnsafe
|
|
134
|
+
|| emittedClientTool
|
|
135
|
+
|| activeRequest.contextUsageStoreCheckpoints === false
|
|
136
|
+
|| !lastTransport?.captured
|
|
137
|
+
|| lastTransport.captured.byteLength === 0
|
|
138
|
+
) return;
|
|
139
|
+
const previousRef = _parsed._providerContinuation?.cursor?.checkpointRef;
|
|
140
|
+
const coveredMessageCount = _parsed.context.messages.length;
|
|
141
|
+
const checkpointRef = commitCursorCheckpoint({
|
|
142
|
+
conversationId: activeRequest.conversationId,
|
|
143
|
+
identityScope: _parsed._cursorIdentityScope,
|
|
144
|
+
modelId: cursorCheckpointModelAffinityId(activeRequest.modelId),
|
|
145
|
+
checkpointBytes: lastTransport.captured,
|
|
146
|
+
coveredMessageCount,
|
|
147
|
+
prefixDigest: cursorCoveredPrefixDigest(_parsed, coveredMessageCount),
|
|
148
|
+
systemDigest: cursorInstructionDigest(_parsed),
|
|
149
|
+
});
|
|
150
|
+
if (!checkpointRef) return;
|
|
151
|
+
if (previousRef && previousRef !== checkpointRef) invalidateCursorCheckpoint(previousRef);
|
|
152
|
+
_parsed._providerContinuation = {
|
|
153
|
+
...(_parsed._providerContinuation ?? {}),
|
|
154
|
+
cursor: {
|
|
155
|
+
...(_parsed._providerContinuation?.cursor ?? {}),
|
|
156
|
+
conversationId: activeRequest.conversationId,
|
|
157
|
+
checkpointUsable: true,
|
|
158
|
+
checkpointRef,
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
debugProviderDiagnostic("cursor", "checkpoint-continuation", {
|
|
162
|
+
mode: activeRequest.continuationMode ?? "full-replay",
|
|
163
|
+
conversationHash: activeRequest.conversationId.slice(0, 16),
|
|
164
|
+
checkpointRefHash: cursorCheckpointRefHash(checkpointRef),
|
|
165
|
+
checkpointBytes: lastTransport.captured.byteLength,
|
|
166
|
+
wireModel: activeRequest.modelId,
|
|
167
|
+
});
|
|
168
|
+
};
|
|
115
169
|
|
|
116
170
|
const runOnce = async (activeRequest: ReturnType<typeof createCursorRequest>) => {
|
|
117
171
|
await runCursorTurnWithRetry(
|
|
@@ -132,6 +186,10 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
|
|
|
132
186
|
return;
|
|
133
187
|
}
|
|
134
188
|
if (message.type === "local_side_effect") replayUnsafe = true;
|
|
189
|
+
if (message.type === "done") completedNormally = true;
|
|
190
|
+
if (message.type === "tool_call_end") emittedClientTool = true;
|
|
191
|
+
const captured = capturedCursorCheckpointBytes(activeTransport);
|
|
192
|
+
if (captured) lastTransport = { captured };
|
|
135
193
|
const events = mapCursorServerMessage(message, {
|
|
136
194
|
kv,
|
|
137
195
|
writeClient: clientMessage => {
|
|
@@ -140,7 +198,28 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
|
|
|
140
198
|
});
|
|
141
199
|
for (const event of events) {
|
|
142
200
|
if (event.type !== "heartbeat") emittedOutput = true;
|
|
143
|
-
|
|
201
|
+
if (event.type === "done") {
|
|
202
|
+
commitCapturedCheckpoint(activeRequest);
|
|
203
|
+
const inheritedCursor = _parsed._providerContinuation?.cursor;
|
|
204
|
+
const isolatedOrCompaction =
|
|
205
|
+
_parsed._cursorIsolateConversation === true
|
|
206
|
+
|| activeRequest.contextUsageStoreCheckpoints === false;
|
|
207
|
+
const providerState = inheritedCursor
|
|
208
|
+
? {
|
|
209
|
+
cursor: isolatedOrCompaction
|
|
210
|
+
? {
|
|
211
|
+
conversationId: activeRequest.conversationId,
|
|
212
|
+
...(inheritedCursor.checkpointUsable !== undefined
|
|
213
|
+
? { checkpointUsable: inheritedCursor.checkpointUsable }
|
|
214
|
+
: {}),
|
|
215
|
+
}
|
|
216
|
+
: { ...inheritedCursor, conversationId: activeRequest.conversationId },
|
|
217
|
+
}
|
|
218
|
+
: undefined;
|
|
219
|
+
emit(providerState ? { ...event, providerState } : event);
|
|
220
|
+
} else {
|
|
221
|
+
emit(event);
|
|
222
|
+
}
|
|
144
223
|
}
|
|
145
224
|
},
|
|
146
225
|
);
|
|
@@ -163,6 +242,7 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
|
|
|
163
242
|
throw err;
|
|
164
243
|
}
|
|
165
244
|
const failedConversationId = request.conversationId;
|
|
245
|
+
lastTransport = undefined;
|
|
166
246
|
_parsed._cursorConversationId = undefined;
|
|
167
247
|
request = createCursorRequest(_parsed, { forceFreshConversation: true });
|
|
168
248
|
rekeyContextUsage(failedConversationId, request.conversationId);
|
|
@@ -179,6 +259,32 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
|
|
|
179
259
|
}
|
|
180
260
|
await runOnce(request);
|
|
181
261
|
}
|
|
262
|
+
if (
|
|
263
|
+
request.checkpointInvalidationReason
|
|
264
|
+
&& request.checkpointInvalidationReason !== "missing_ref"
|
|
265
|
+
) {
|
|
266
|
+
invalidateCursorCheckpoint(inheritedCheckpointRef);
|
|
267
|
+
debugProviderDiagnostic("cursor", "checkpoint-invalidated", {
|
|
268
|
+
reason: request.checkpointInvalidationReason,
|
|
269
|
+
});
|
|
270
|
+
} else if (!completedNormally && request.checkpointInvalidationReason) {
|
|
271
|
+
debugProviderDiagnostic("cursor", "checkpoint-invalidated", {
|
|
272
|
+
reason: request.checkpointInvalidationReason,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
if (
|
|
276
|
+
_parsed._cursorIsolateConversation === true
|
|
277
|
+
|| request.contextUsageStoreCheckpoints === false
|
|
278
|
+
) {
|
|
279
|
+
const inherited = _parsed._providerContinuation?.cursor;
|
|
280
|
+
if (inherited) {
|
|
281
|
+
const { checkpointRef: _ignoredCheckpointRef, ...cursorWithoutCheckpointRef } = inherited;
|
|
282
|
+
_parsed._providerContinuation = {
|
|
283
|
+
...(_parsed._providerContinuation ?? {}),
|
|
284
|
+
cursor: cursorWithoutCheckpointRef,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
182
288
|
} catch (err) {
|
|
183
289
|
if (isCursorBenignCancelError(err)) return;
|
|
184
290
|
const partialUsage = (err as { partialUsage?: import("../types").OcxUsage }).partialUsage;
|
|
@@ -702,11 +702,38 @@ export function applyAntigravityReplay(model: string, sessionId: string, content
|
|
|
702
702
|
if (!fc) continue;
|
|
703
703
|
if (part.thoughtSignature !== undefined || part.thought_signature !== undefined) continue;
|
|
704
704
|
const ck = functionCallKey(fc.name, fc.args);
|
|
705
|
-
|
|
706
|
-
|
|
705
|
+
let call = ck ? entry.byCall.get(ck) : undefined;
|
|
706
|
+
let matchedKey = ck;
|
|
707
|
+
if (!call && typeof fc.name === "string" && typeof fc.args === "object" && fc.args !== null) {
|
|
708
|
+
// Freeform / custom tool replay unwrap:
|
|
709
|
+
// The client replays custom_tool_call with arguments: { input: "..." }.
|
|
710
|
+
// Upstream was invoked with args: { input: "..." } or raw string or parsed JSON.
|
|
711
|
+
const argsObj = fc.args as Record<string, unknown>;
|
|
712
|
+
if (typeof argsObj.input === "string") {
|
|
713
|
+
const trimmedInput = argsObj.input.trim();
|
|
714
|
+
if (
|
|
715
|
+
trimmedInput.length <= REPLAY_MAX_CANONICAL_ARGS_BYTES
|
|
716
|
+
&& utf8.encode(trimmedInput).byteLength <= REPLAY_MAX_CANONICAL_ARGS_BYTES
|
|
717
|
+
) {
|
|
718
|
+
try {
|
|
719
|
+
const parsedInput = JSON.parse(trimmedInput);
|
|
720
|
+
if (parsedInput && typeof parsedInput === "object") {
|
|
721
|
+
const altKey = functionCallKey(fc.name, parsedInput);
|
|
722
|
+
if (altKey && entry.byCall.has(altKey)) {
|
|
723
|
+
call = entry.byCall.get(altKey);
|
|
724
|
+
matchedKey = altKey;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
} catch {
|
|
728
|
+
// not JSON, keep default
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
if (call && matchedKey) {
|
|
707
734
|
part.thoughtSignature = call.signature;
|
|
708
|
-
entry.byCall.delete(
|
|
709
|
-
entry.byCall.set(
|
|
735
|
+
entry.byCall.delete(matchedKey);
|
|
736
|
+
entry.byCall.set(matchedKey, { ...call, touchedAtMs: now });
|
|
710
737
|
touched = true;
|
|
711
738
|
}
|
|
712
739
|
}
|