@openclaw/codex 2026.8.2 → 2026.9.1
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/dist/api.js +3 -3
- package/dist/{attempt-client-cleanup-CpVfwctT.js → attempt-client-cleanup-DYHcRQAa.js} +6 -43
- package/dist/attempt-context-DmQUNWwT.js +634 -0
- package/dist/{binding-connection-CF7BmrDk.js → binding-connection-E6uDsPlx.js} +2 -2
- package/dist/{bounded-turn-DRC3hcjm.js → bounded-turn-wzJrwKKe.js} +8 -9
- package/dist/{capabilities-D6m_TdVy.js → capabilities-dIjNVCYH.js} +1 -1
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters--UToCokS.js → command-formatters-CLHhEB1Q.js} +3 -3
- package/dist/{command-handlers-CLFx2Ypa.js → command-handlers-CTGb2csu.js} +54 -55
- package/dist/{compact-5xqW1oCh.js → compact-_t0HQrUS.js} +33 -25
- package/dist/{config-CcYL77O6.js → config-BhmfC1s6.js} +12 -8
- package/dist/{context-compaction-activity-BOeJf7yo.js → context-compaction-activity-CMla5FPJ.js} +4 -1
- package/dist/doctor-contract-api.js +31 -4
- package/dist/{dynamic-tools-BmeXjwNZ.js → dynamic-tools-BU9tmlYm.js} +448 -457
- package/dist/{effective-mcp-catalog-XXQKgK1U.js → effective-mcp-catalog-DzYBW4h1.js} +2 -2
- package/dist/{event-projector-assistant-message-C5-Ds8mA.js → event-projector-assistant-message-DHRFXKbS.js} +4 -4
- package/dist/{event-projector-usage-B2r5vhWH.js → event-projector-usage-C5mcrGjp.js} +9 -137
- package/dist/{harness-BgeB7LnD.js → harness-DXVZ5II5.js} +13 -13
- package/dist/harness.js +1 -1
- package/dist/index.js +323 -313
- package/dist/{isolated-completion-CxTus3vZ.js → isolated-completion-kg9kv4iH.js} +4 -4
- package/dist/{launch-args-sXOAg0SX.js → launch-args-RXQwn8zV.js} +7 -1
- package/dist/{managed-binary-ZkGodW19.js → managed-binary-4-Vjz5GH.js} +14 -9
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{model-catalog-DULBcniB.js → model-catalog-CUM68Hkl.js} +11 -11
- package/dist/{models-DaWfETQk.js → models-sYnEc0gd.js} +3 -3
- package/dist/{native-subagent-monitor-vXYUZH37.js → native-subagent-monitor-CnH147jH.js} +3 -3
- package/dist/{node-exec-server.runtime-DirKVZ5h.js → node-exec-server.runtime-CstNHCmC.js} +2 -2
- package/dist/{plugin-approval-roundtrip-CCWQGlAI.js → plugin-approval-roundtrip-CNe-Xrjx.js} +29 -3
- package/dist/{plugin-marketplace-discovery-BXjIFEiw.js → plugin-marketplace-discovery-CfFlKqDl.js} +6 -6
- package/dist/{plugin-metadata-cache-DQQm4gWT.js → plugin-metadata-cache-BFrPhb2c.js} +5 -5
- package/dist/provider-capabilities-DW5p4k9w.js +101 -0
- package/dist/{rate-limits-BqHEHXQJ.js → rate-limits-CawQJfUS.js} +1 -1
- package/dist/{request-BWxweEdK.js → request-DYAI5FQE.js} +18 -6
- package/dist/{run-attempt-BVLE4Gbi.js → run-attempt-D1hJO4fX.js} +483 -1865
- package/dist/{runtime-artifact-D9pYp4zL.js → runtime-artifact-C3jVimFn.js} +19 -12
- package/dist/{session-binding-D0zinCP1.js → session-binding-BHuTCRZQ.js} +8 -10
- package/dist/{session-catalog-eMhVm0Ql.js → session-catalog-yyvTWFOw.js} +138 -116
- package/dist/{session-cli-B2lyjJA0.js → session-cli-CmFEXMAM.js} +1 -1
- package/dist/session-initialization-BeWzjGiR.js +74 -0
- package/dist/{session-permission-policy-ScnAyG90.js → session-permission-policy-DeWVTCMj.js} +2 -2
- package/dist/{session-retirement-CtUtUTr9.js → session-retirement-BujDjXVF.js} +16 -9
- package/dist/{session-upstream-marker-gqrYIRHk.js → session-upstream-marker-C351-5Qw.js} +18 -2
- package/dist/{settled-turn-context-Bk3IUwod.js → settled-turn-context-D3SNWtUn.js} +6 -28
- package/dist/{settled-turn-finalizer-BXLN9PfJ.js → settled-turn-finalizer-BVJx4yoc.js} +5 -5
- package/dist/{shared-client-B1tT5U6r.js → shared-client-DxcfF9KX.js} +2748 -101
- package/dist/{side-question-CZ-mCw-t.js → side-question-BTZAII4l.js} +97 -63
- package/dist/{thread-lifecycle-CGGKP720.js → thread-lifecycle-DAcEJ_Om.js} +2039 -1487
- package/dist/{thread-ownership-RkaoZCmQ.js → thread-ownership-zGokd8i1.js} +10 -9
- package/dist/thread-policy-g4KbJPzp.js +54 -0
- package/dist/{thread-resume-BFt1-ZDa.js → thread-resume-C7pyVKeW.js} +15 -7
- package/dist/{transcript-mirror-1ep4XjzF.js → transcript-mirror-DTUxFWa_.js} +47 -10
- package/dist/{transport-stdio-NT_Dpd9V.js → transport-stdio-CyPldczm.js} +3 -1
- package/dist/upstream-session-fork-CYlBIGVt.js +966 -0
- package/dist/{usage-DUhd58gI.js → usage-Bj-rrzkR.js} +3 -3
- package/dist/{web-search-provider.runtime-BEmlE7ov.js → web-search-provider.runtime-D2FWSK4w.js} +3 -3
- package/openclaw.plugin.json +0 -30
- package/package.json +5 -5
- package/dist/protocol-validators-Bg5rpNxk.js +0 -2542
- package/dist/session-catalog-home-id-Dt2GiG5o.js +0 -112
- package/dist/upstream-session-fork-Dc8BFExw.js +0 -312
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { Ht as isJsonObject } from "./shared-client-B1tT5U6r.js";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { createHash } from "node:crypto";
|
|
4
|
-
import fsSync from "node:fs";
|
|
5
|
-
import { root } from "openclaw/plugin-sdk/file-access-runtime";
|
|
6
|
-
import { createZstdDecompress } from "node:zlib";
|
|
7
|
-
//#region extensions/codex/src/session-catalog-provenance.ts
|
|
8
|
-
const MAX_SESSION_META_BYTES = 1048576;
|
|
9
|
-
const SESSION_META_READ_CHUNK_BYTES = 65536;
|
|
10
|
-
const MAX_PROVENANCE_CACHE_ENTRIES = 2e4;
|
|
11
|
-
const provenanceByPath = /* @__PURE__ */ new Map();
|
|
12
|
-
function cacheProvenance(key, value) {
|
|
13
|
-
provenanceByPath.delete(key);
|
|
14
|
-
provenanceByPath.set(key, value);
|
|
15
|
-
while (provenanceByPath.size > MAX_PROVENANCE_CACHE_ENTRIES) {
|
|
16
|
-
const oldest = provenanceByPath.keys().next().value;
|
|
17
|
-
if (oldest === void 0) break;
|
|
18
|
-
provenanceByPath.delete(oldest);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
/** Undefined means the metadata line is not durable enough to cache yet. */
|
|
22
|
-
async function readCodexSessionMeta(sessionsRoot, rolloutPath, threadId) {
|
|
23
|
-
let safeRoot;
|
|
24
|
-
try {
|
|
25
|
-
safeRoot = await root(sessionsRoot, {
|
|
26
|
-
hardlinks: "reject",
|
|
27
|
-
maxBytes: Number.MAX_SAFE_INTEGER,
|
|
28
|
-
symlinks: "reject"
|
|
29
|
-
});
|
|
30
|
-
} catch {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
const candidates = rolloutPath.endsWith(".zst") ? [rolloutPath, rolloutPath.slice(0, -4)] : [rolloutPath, `${rolloutPath}.zst`];
|
|
34
|
-
for (const candidate of candidates) {
|
|
35
|
-
let opened;
|
|
36
|
-
try {
|
|
37
|
-
opened = await safeRoot.open(path.relative(sessionsRoot, candidate));
|
|
38
|
-
} catch {
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
const input = opened.handle.createReadStream({
|
|
42
|
-
autoClose: false,
|
|
43
|
-
highWaterMark: SESSION_META_READ_CHUNK_BYTES
|
|
44
|
-
});
|
|
45
|
-
const reader = candidate.endsWith(".zst") ? input.pipe(createZstdDecompress()) : input;
|
|
46
|
-
try {
|
|
47
|
-
const chunks = [];
|
|
48
|
-
let bytesReadTotal = 0;
|
|
49
|
-
let line;
|
|
50
|
-
for await (const value of reader) {
|
|
51
|
-
const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
52
|
-
const remaining = MAX_SESSION_META_BYTES - bytesReadTotal;
|
|
53
|
-
if (remaining <= 0) break;
|
|
54
|
-
const bounded = chunk.subarray(0, remaining);
|
|
55
|
-
bytesReadTotal += bounded.length;
|
|
56
|
-
const newline = bounded.indexOf(10);
|
|
57
|
-
chunks.push(newline >= 0 ? bounded.subarray(0, newline) : bounded);
|
|
58
|
-
if (newline >= 0) {
|
|
59
|
-
line = Buffer.concat(chunks).toString("utf8");
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (!line) continue;
|
|
64
|
-
let parsed;
|
|
65
|
-
try {
|
|
66
|
-
parsed = JSON.parse(line);
|
|
67
|
-
} catch {
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
if (!isJsonObject(parsed) || parsed.type !== "session_meta" || !isJsonObject(parsed.payload)) return null;
|
|
71
|
-
const payload = parsed.payload;
|
|
72
|
-
return payload.id === threadId ? payload : null;
|
|
73
|
-
} catch {
|
|
74
|
-
continue;
|
|
75
|
-
} finally {
|
|
76
|
-
reader.destroy();
|
|
77
|
-
input.destroy();
|
|
78
|
-
await opened.handle.close().catch(() => void 0);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Codex 0.147 reports OpenClaw app-server rollouts as `vscode`, so the rollout's
|
|
84
|
-
* immutable session metadata is the authoritative historical provenance.
|
|
85
|
-
*/
|
|
86
|
-
async function isOpenClawManagedCodexThread(thread, localSessionsRoot) {
|
|
87
|
-
const rolloutPath = typeof thread.path === "string" ? thread.path.trim() : "";
|
|
88
|
-
if (!localSessionsRoot || !rolloutPath) return false;
|
|
89
|
-
const cacheKey = `${localSessionsRoot}\0${rolloutPath}`;
|
|
90
|
-
const cached = provenanceByPath.get(cacheKey);
|
|
91
|
-
if (cached !== void 0) return cached;
|
|
92
|
-
const metadata = await readCodexSessionMeta(localSessionsRoot, rolloutPath, thread.id);
|
|
93
|
-
const managed = metadata === void 0 ? void 0 : metadata?.originator === "openclaw";
|
|
94
|
-
if (managed !== void 0) cacheProvenance(cacheKey, managed);
|
|
95
|
-
return managed ?? false;
|
|
96
|
-
}
|
|
97
|
-
//#endregion
|
|
98
|
-
//#region extensions/codex/src/session-catalog-home-id.ts
|
|
99
|
-
function canonicalCodexCatalogHome(value) {
|
|
100
|
-
const resolved = path.resolve(value);
|
|
101
|
-
try {
|
|
102
|
-
return fsSync.realpathSync.native(resolved);
|
|
103
|
-
} catch {
|
|
104
|
-
return resolved;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
/** One canonical identity for catalog discovery and durable ownership rows. */
|
|
108
|
-
function codexCatalogHomeId(codexHome) {
|
|
109
|
-
return createHash("sha256").update("openclaw:codex-session-catalog-home:v1\0").update(canonicalCodexCatalogHome(codexHome)).digest("hex");
|
|
110
|
-
}
|
|
111
|
-
//#endregion
|
|
112
|
-
export { readCodexSessionMeta as i, codexCatalogHomeId as n, isOpenClawManagedCodexThread as r, canonicalCodexCatalogHome as t };
|
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
import { d as sessionBindingIdentity } from "./session-binding-D0zinCP1.js";
|
|
2
|
-
import { a as assertCodexThreadForkResponse } from "./protocol-validators-Bg5rpNxk.js";
|
|
3
|
-
import { i as readUpstreamUserText, r as readMirrorIdentity } from "./upstream-prompt-provenance-LphB8slG.js";
|
|
4
|
-
import { c as fingerprintCodexMirrorSourceMessage, m as projectCodexUserItemText, u as readCodexMirrorSourceFingerprint } from "./transcript-mirror-1ep4XjzF.js";
|
|
5
|
-
import { i as createImportedCodexSession, n as codexUpstreamBaseline, t as codexLastTerminalTurnId } from "./session-upstream-marker-gqrYIRHk.js";
|
|
6
|
-
import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
7
|
-
import { readVisibleSessionTranscriptMessageEntries } from "openclaw/plugin-sdk/session-transcript-runtime";
|
|
8
|
-
import { deleteSessionUpstreamLink, upsertSessionUpstreamLink } from "openclaw/plugin-sdk/session-catalog";
|
|
9
|
-
//#region extensions/codex/src/app-server/upstream-fork-boundary.ts
|
|
10
|
-
const TURN_PAGE_LIMIT = 100;
|
|
11
|
-
function failure(code, message) {
|
|
12
|
-
return {
|
|
13
|
-
ok: false,
|
|
14
|
-
code,
|
|
15
|
-
message
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
function textOnlyMessage(content) {
|
|
19
|
-
if (typeof content === "string") return content;
|
|
20
|
-
if (!Array.isArray(content)) return;
|
|
21
|
-
const texts = [];
|
|
22
|
-
for (const block of content) {
|
|
23
|
-
if (!block || typeof block !== "object" || Array.isArray(block)) return;
|
|
24
|
-
const typed = block;
|
|
25
|
-
if (typed.type !== "text" || typeof typed.text !== "string") return;
|
|
26
|
-
texts.push(typed.text);
|
|
27
|
-
}
|
|
28
|
-
return texts.join("\n");
|
|
29
|
-
}
|
|
30
|
-
function resolveCodexUpstreamForkBoundaryFromTurns(params) {
|
|
31
|
-
let localIndex = 0;
|
|
32
|
-
let matchedPrefix = false;
|
|
33
|
-
for (const [turnIndex, turn] of params.turns.entries()) {
|
|
34
|
-
let userMessagesInTurn = 0;
|
|
35
|
-
for (const item of turn.items) {
|
|
36
|
-
if (item.type !== "userMessage") continue;
|
|
37
|
-
const isSteer = userMessagesInTurn > 0;
|
|
38
|
-
userMessagesInTurn += 1;
|
|
39
|
-
const nativeText = textOnlyMessage(item.content);
|
|
40
|
-
if (nativeText === void 0) return failure("drift-mismatch", "A message before the fork point contains images or attachments that cannot be verified across OpenClaw and Codex. Fork from a text-only span instead.");
|
|
41
|
-
const local = params.localPrefix[localIndex];
|
|
42
|
-
const upstreamText = local && readUpstreamUserText(local.message);
|
|
43
|
-
const text = upstreamText ? nativeText : projectCodexUserItemText(item);
|
|
44
|
-
if (!text) continue;
|
|
45
|
-
const identity = local && readMirrorIdentity(local.message);
|
|
46
|
-
const matchesIdentity = identity === `${turn.id}:${item.id}` || !isSteer && identity === `${turn.id}:prompt`;
|
|
47
|
-
if (!matchedPrefix && !matchesIdentity) continue;
|
|
48
|
-
matchedPrefix = true;
|
|
49
|
-
const localText = textOnlyMessage(local && "content" in local.message ? local.message.content : void 0);
|
|
50
|
-
const upstreamPromptVerified = !upstreamText || local?.message.role === "user" && readCodexMirrorSourceFingerprint(local.message) === fingerprintCodexMirrorSourceMessage(local.message);
|
|
51
|
-
if (!matchesIdentity || !upstreamPromptVerified || localText === void 0 || text !== (upstreamText ?? localText)) return failure("drift-mismatch", "The local conversation no longer matches the Codex thread. Refresh the session and try again.");
|
|
52
|
-
if (localIndex < params.localPrefix.length - 1) {
|
|
53
|
-
localIndex += 1;
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (isSteer) return failure("steer-message", "This message steered an existing Codex turn and cannot be forked independently. Fork from the turn's first message instead.");
|
|
57
|
-
if (turn.status === "inProgress") return failure("in-progress-turn", "This Codex turn is still in progress. Wait for it to finish, then try forking again.");
|
|
58
|
-
const retained = turnIndex > 0 ? params.turns[turnIndex - 1] : void 0;
|
|
59
|
-
return {
|
|
60
|
-
ok: true,
|
|
61
|
-
boundary: {
|
|
62
|
-
beforeTurnId: turn.id,
|
|
63
|
-
targetTurnId: turn.id,
|
|
64
|
-
retainedMarker: retained ? {
|
|
65
|
-
turnId: retained.id,
|
|
66
|
-
userMessageCount: retained.items.filter((retainedItem) => retainedItem.type === "userMessage").length
|
|
67
|
-
} : {
|
|
68
|
-
turnId: null,
|
|
69
|
-
userMessageCount: 0
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return failure("drift-mismatch", "The local history has no verified boundary in this Codex thread. Use native Codex to fork this conversation.");
|
|
76
|
-
}
|
|
77
|
-
async function listCodexUpstreamTurns(control, threadId) {
|
|
78
|
-
const turns = [];
|
|
79
|
-
const seenCursors = /* @__PURE__ */ new Set();
|
|
80
|
-
let cursor;
|
|
81
|
-
for (;;) {
|
|
82
|
-
const page = await control.listTurnPage({
|
|
83
|
-
threadId,
|
|
84
|
-
limit: TURN_PAGE_LIMIT,
|
|
85
|
-
sortDirection: "asc",
|
|
86
|
-
itemsView: "full",
|
|
87
|
-
...cursor ? { cursor } : {}
|
|
88
|
-
});
|
|
89
|
-
turns.push(...page.data);
|
|
90
|
-
const nextCursor = page.nextCursor?.trim() || void 0;
|
|
91
|
-
if (!nextCursor) return turns;
|
|
92
|
-
if (seenCursors.has(nextCursor)) throw new Error("Codex returned a repeated thread/turns/list cursor");
|
|
93
|
-
seenCursors.add(nextCursor);
|
|
94
|
-
cursor = nextCursor;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
async function resolveCodexUpstreamForkBoundary(params) {
|
|
98
|
-
try {
|
|
99
|
-
const visibleUserEntries = (await readVisibleSessionTranscriptMessageEntries({
|
|
100
|
-
agentId: params.agentId,
|
|
101
|
-
sessionId: params.sessionId,
|
|
102
|
-
sessionKey: params.sessionKey,
|
|
103
|
-
storePath: params.storePath
|
|
104
|
-
})).filter((entry) => entry.role === "user");
|
|
105
|
-
const targetIndex = visibleUserEntries.findIndex((entry) => entry.entryId === params.entryId);
|
|
106
|
-
if (targetIndex < 0) return failure("drift-mismatch", "The local message could not be mapped to the Codex thread. Refresh the session and try again.");
|
|
107
|
-
const localPrefix = visibleUserEntries.slice(0, targetIndex + 1);
|
|
108
|
-
const resolved = resolveCodexUpstreamForkBoundaryFromTurns({
|
|
109
|
-
turns: await listCodexUpstreamTurns(params.control, params.threadId),
|
|
110
|
-
localPrefix
|
|
111
|
-
});
|
|
112
|
-
const target = localPrefix.at(-1)?.message;
|
|
113
|
-
return resolved.ok ? {
|
|
114
|
-
...resolved,
|
|
115
|
-
editorText: textOnlyMessage(target && "content" in target ? target.content : void 0)
|
|
116
|
-
} : resolved;
|
|
117
|
-
} catch {
|
|
118
|
-
return failure("upstream-unavailable", "The Codex thread could not be read. Check that Codex is available, then try again.");
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
function precheckCodexUpstreamForkBoundary(params) {
|
|
122
|
-
const target = params.turns.find((turn) => turn.id === params.boundary.targetTurnId);
|
|
123
|
-
if (!target) return failure("upstream-unavailable", "The Codex thread changed before it could be forked. Refresh the session and try again.");
|
|
124
|
-
if (target.status === "inProgress") return failure("in-progress-turn", "This Codex turn is still in progress. Wait for it to finish, then try forking again.");
|
|
125
|
-
return {
|
|
126
|
-
ok: true,
|
|
127
|
-
boundary: params.boundary
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
//#endregion
|
|
131
|
-
//#region extensions/codex/src/app-server/upstream-session-fork.ts
|
|
132
|
-
function readConnectionFingerprint(ref) {
|
|
133
|
-
if (!isRecord(ref)) return;
|
|
134
|
-
return typeof ref.connectionFingerprint === "string" && ref.connectionFingerprint.trim() ? ref.connectionFingerprint : void 0;
|
|
135
|
-
}
|
|
136
|
-
async function forkCodexUpstreamSession(params, options) {
|
|
137
|
-
try {
|
|
138
|
-
const sourceFingerprint = params.upstream.kind === "codex-app-server" ? readConnectionFingerprint(params.upstream.ref) : void 0;
|
|
139
|
-
const requestControl = sourceFingerprint ? options.controlFactory.forUpstream(params.source.agentId, sourceFingerprint) : void 0;
|
|
140
|
-
if (!sourceFingerprint || !requestControl) return {
|
|
141
|
-
status: "failed",
|
|
142
|
-
code: "upstream-unavailable",
|
|
143
|
-
message: "This Codex thread is not available on the current connection. Reconnect to its host and try again."
|
|
144
|
-
};
|
|
145
|
-
return await requestControl.withPinnedConnection(async (control) => {
|
|
146
|
-
const sourceBinding = await options.bindingStore.read(sessionBindingIdentity({
|
|
147
|
-
...params.source,
|
|
148
|
-
config: options.resolveConfig?.()
|
|
149
|
-
}));
|
|
150
|
-
const supervised = sourceBinding?.connectionScope === "supervision";
|
|
151
|
-
const sourceThreadId = params.upstream.threadId;
|
|
152
|
-
let linked = false;
|
|
153
|
-
let createdBinding;
|
|
154
|
-
const compensateFork = async (forkedThreadId) => {
|
|
155
|
-
if (createdBinding) {
|
|
156
|
-
if (!await options.bindingStore.mutate(createdBinding.identity, {
|
|
157
|
-
kind: "clear",
|
|
158
|
-
threadId: forkedThreadId,
|
|
159
|
-
expectedPendingSupervisionBranch: createdBinding.pending
|
|
160
|
-
}).catch(() => false)) return;
|
|
161
|
-
}
|
|
162
|
-
if (linked) deleteSessionUpstreamLink(params.targetKey, params.source.agentId);
|
|
163
|
-
await control.archiveThread(forkedThreadId).catch(() => void 0);
|
|
164
|
-
};
|
|
165
|
-
if (sourceFingerprint !== control.connectionFingerprint || supervised && (sourceBinding.supervisionSourceThreadId !== params.upstream.threadId || (sourceBinding.pendingSupervisionBranch?.connectionFingerprint ?? sourceBinding.appServerRuntimeFingerprint) !== sourceFingerprint)) return {
|
|
166
|
-
status: "failed",
|
|
167
|
-
code: "upstream-unavailable",
|
|
168
|
-
message: "This Codex thread is not available on the current connection. Reconnect to its host and try again."
|
|
169
|
-
};
|
|
170
|
-
const resolved = await resolveCodexUpstreamForkBoundary({
|
|
171
|
-
...params.source,
|
|
172
|
-
threadId: sourceThreadId,
|
|
173
|
-
control
|
|
174
|
-
});
|
|
175
|
-
if (!resolved.ok) return {
|
|
176
|
-
status: "failed",
|
|
177
|
-
code: resolved.code,
|
|
178
|
-
message: resolved.message
|
|
179
|
-
};
|
|
180
|
-
const liveTurns = await listCodexUpstreamTurns(control, sourceThreadId);
|
|
181
|
-
const precheck = precheckCodexUpstreamForkBoundary({
|
|
182
|
-
boundary: resolved.boundary,
|
|
183
|
-
turns: liveTurns
|
|
184
|
-
});
|
|
185
|
-
if (!precheck.ok) return {
|
|
186
|
-
status: "failed",
|
|
187
|
-
code: precheck.code,
|
|
188
|
-
message: precheck.message
|
|
189
|
-
};
|
|
190
|
-
const rawResponse = await control.forkThread({
|
|
191
|
-
threadId: sourceThreadId,
|
|
192
|
-
beforeTurnId: resolved.boundary.beforeTurnId,
|
|
193
|
-
...params.sandbox === "required" ? { sandbox: "workspace-write" } : {},
|
|
194
|
-
excludeTurns: true
|
|
195
|
-
});
|
|
196
|
-
let response;
|
|
197
|
-
try {
|
|
198
|
-
response = assertCodexThreadForkResponse(rawResponse);
|
|
199
|
-
} catch (error) {
|
|
200
|
-
const orphanThreadId = isRecord(rawResponse.thread) && typeof rawResponse.thread.id === "string" ? rawResponse.thread.id.trim() : "";
|
|
201
|
-
if (orphanThreadId && orphanThreadId !== sourceThreadId && orphanThreadId !== sourceBinding?.threadId) await control.archiveThread(orphanThreadId).catch(() => void 0);
|
|
202
|
-
throw error;
|
|
203
|
-
}
|
|
204
|
-
const threadId = response.thread.id.trim();
|
|
205
|
-
if (!threadId) throw new Error("Codex thread/fork response did not include a thread id");
|
|
206
|
-
if (threadId === sourceThreadId || threadId === sourceBinding?.threadId) throw new Error("Codex thread/fork response reused the source thread id");
|
|
207
|
-
const forkedThreadId = threadId;
|
|
208
|
-
try {
|
|
209
|
-
const connectionFingerprint = normalizeOptionalString(control.connectionFingerprint);
|
|
210
|
-
if (!connectionFingerprint) throw new Error("Codex fork connection did not include a fingerprint");
|
|
211
|
-
const forkedTurns = await listCodexUpstreamTurns(control, threadId);
|
|
212
|
-
const expectedLastTurnId = resolved.boundary.retainedMarker.turnId;
|
|
213
|
-
if ((forkedTurns.at(-1)?.id ?? null) !== expectedLastTurnId) {
|
|
214
|
-
await compensateFork(forkedThreadId);
|
|
215
|
-
return {
|
|
216
|
-
status: "failed",
|
|
217
|
-
code: "upstream-unavailable",
|
|
218
|
-
message: "This Codex version does not support message-level forks. Update Codex, reconnect, and try again."
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
const forkedThread = {
|
|
222
|
-
...response.thread,
|
|
223
|
-
turns: forkedTurns
|
|
224
|
-
};
|
|
225
|
-
const throughTurnId = codexLastTerminalTurnId(forkedThread, normalizeOptionalString) ?? null;
|
|
226
|
-
const marker = codexUpstreamBaseline(forkedThread, normalizeOptionalString);
|
|
227
|
-
const config = options.resolveConfig?.() ?? {};
|
|
228
|
-
return {
|
|
229
|
-
status: "created",
|
|
230
|
-
key: (await createImportedCodexSession({
|
|
231
|
-
runtime: options.runtime,
|
|
232
|
-
config,
|
|
233
|
-
key: params.targetKey,
|
|
234
|
-
agentId: params.source.agentId,
|
|
235
|
-
thread: forkedThread,
|
|
236
|
-
throughTurnId,
|
|
237
|
-
initialEntry: {
|
|
238
|
-
agentHarnessId: options.harnessRuntimeId,
|
|
239
|
-
modelSelectionLocked: true
|
|
240
|
-
},
|
|
241
|
-
afterImport: async (entry) => {
|
|
242
|
-
const bindingIdentity = sessionBindingIdentity({
|
|
243
|
-
agentId: entry.agentId,
|
|
244
|
-
sessionId: entry.sessionId,
|
|
245
|
-
sessionKey: entry.key,
|
|
246
|
-
config
|
|
247
|
-
});
|
|
248
|
-
linked = upsertSessionUpstreamLink({
|
|
249
|
-
sessionKey: entry.key,
|
|
250
|
-
agentId: entry.agentId,
|
|
251
|
-
catalogId: params.upstream.catalogId,
|
|
252
|
-
hostId: params.upstream.hostId,
|
|
253
|
-
threadId,
|
|
254
|
-
upstreamKind: params.upstream.kind,
|
|
255
|
-
upstreamRef: {
|
|
256
|
-
connectionFingerprint,
|
|
257
|
-
threadId
|
|
258
|
-
},
|
|
259
|
-
marker
|
|
260
|
-
});
|
|
261
|
-
if (!linked) throw new Error("Codex fork link could not be persisted");
|
|
262
|
-
createdBinding = {
|
|
263
|
-
identity: bindingIdentity,
|
|
264
|
-
pending: {
|
|
265
|
-
sourceThreadId: threadId,
|
|
266
|
-
connectionFingerprint,
|
|
267
|
-
...throughTurnId ? { lastTurnId: throughTurnId } : {}
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
if (!await options.bindingStore.mutate(bindingIdentity, {
|
|
271
|
-
kind: "set",
|
|
272
|
-
if: { kind: "absent" },
|
|
273
|
-
binding: {
|
|
274
|
-
threadId,
|
|
275
|
-
connectionScope: "supervision",
|
|
276
|
-
supervisionSourceThreadId: threadId,
|
|
277
|
-
preserveNativeModel: true,
|
|
278
|
-
conversationSourceTransferComplete: true,
|
|
279
|
-
pendingSupervisionBranch: createdBinding.pending,
|
|
280
|
-
cwd: forkedThread.cwd ?? "",
|
|
281
|
-
model: response.model,
|
|
282
|
-
modelProvider: response.modelProvider ?? void 0,
|
|
283
|
-
historyCoveredThrough: (/* @__PURE__ */ new Date()).toISOString()
|
|
284
|
-
}
|
|
285
|
-
})) {
|
|
286
|
-
createdBinding = void 0;
|
|
287
|
-
throw new Error("Codex session binding changed before the fork could be attached");
|
|
288
|
-
}
|
|
289
|
-
return { pluginExtensions: entry.entry.pluginExtensions };
|
|
290
|
-
}
|
|
291
|
-
})).key,
|
|
292
|
-
...resolved.editorText !== void 0 ? { editorText: resolved.editorText } : {}
|
|
293
|
-
};
|
|
294
|
-
} catch {
|
|
295
|
-
await compensateFork(forkedThreadId);
|
|
296
|
-
return {
|
|
297
|
-
status: "failed",
|
|
298
|
-
code: "upstream-unavailable",
|
|
299
|
-
message: "The Codex fork could not be verified or imported into a new session. Refresh sessions and try again."
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
} catch {
|
|
304
|
-
return {
|
|
305
|
-
status: "failed",
|
|
306
|
-
code: "upstream-unavailable",
|
|
307
|
-
message: "The Codex thread could not be forked. Check that Codex is available, then try again."
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
//#endregion
|
|
312
|
-
export { forkCodexUpstreamSession };
|