@openclaw/codex 2026.9.2 → 2026.9.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/README.md +2 -0
- package/dist/api.js +14 -19
- package/dist/apply-B6el6hdO.js +357 -0
- package/dist/{attempt-client-cleanup-Bp1ZOnk2.js → attempt-client-cleanup-D6cR54TV.js} +39 -19
- package/dist/{attempt-context-CSazkWns.js → attempt-context-AO4DSjE0.js} +8 -6
- package/dist/{event-projector-usage-ZqEVbAEY.js → attempt-notifications--H8zXR5-.js} +3 -106
- package/dist/auth-cache-key-Kq6oerr6.js +498 -0
- package/dist/auth-profile-CwzO4_RG.js +43 -0
- package/dist/auth-profile-selection-DFu9e167.js +45 -0
- package/dist/binding-connection-QqgcPCgk.js +60 -0
- package/dist/{bounded-turn-DxGJRYbY.js → bounded-turn-lRpmZHFv.js} +25 -122
- package/dist/build-state-6RL6qohz.js +19 -0
- package/dist/{capabilities-B1mgvpP8.js → capabilities-BeqLPbtM.js} +1 -1
- package/dist/cli-metadata.js +1 -1
- package/dist/client-BEKu4yDx.js +3983 -0
- package/dist/command-dispatch-CisT9Jqq.js +21 -0
- package/dist/{command-formatters-DSbGMXA0.js → command-formatters-DeTfrapr.js} +2 -2
- package/dist/{command-handlers-DOJvGv6-.js → command-handlers-BfIsJEuA.js} +31 -21
- package/dist/command-rpc-BMqRQOFX.js +226 -0
- package/dist/{compact-Y_xBqrHN.js → compact-CGJZ2rlb.js} +19 -14
- package/dist/config-JDfTI4VI.js +106 -0
- package/dist/{config-CyapYSji.js → config-options-jnIFvwxD.js} +204 -592
- package/dist/config-runtime-BWf6JLWG.js +11 -0
- package/dist/conversation-binding-CxOlHiix.js +889 -0
- package/dist/{plugin-marketplace-discovery-B7gHXJ3d.js → conversation-control-CUCrSIQI.js} +10 -142
- package/dist/doctor-contract-api.js +5 -5
- package/dist/{dynamic-tools-BwpvTxaX.js → dynamic-tools-D-JBoaLt.js} +66 -70
- package/dist/{effective-mcp-catalog-CY9Jn4bA.js → effective-mcp-catalog-J_5Ep5Um.js} +3 -3
- package/dist/{elicitation-input-DMkeki8d.js → elicitation-input-DPoK8F7w.js} +8 -22
- package/dist/ephemeral-turn-DSwzP8YY.js +101 -0
- package/dist/{event-projector-assistant-message-B9kp6iGC.js → event-projector-assistant-message-BmBM4qBV.js} +12 -25
- package/dist/event-projector-usage-CIyrYYid.js +102 -0
- package/dist/{harness-Czh2Sdoc.js → harness-FR6eQgyO.js} +31 -24
- package/dist/harness.js +1 -1
- package/dist/history-rejection-B4nOXNXF.js +13 -0
- package/dist/incognito-session-uhrBF6wJ.js +15 -0
- package/dist/index.js +411 -2875
- package/dist/{isolated-completion-CRh-51AK.js → isolated-completion-DHBefrzg.js} +7 -4
- package/dist/launch-args-RXQwn8zV.js +90 -0
- package/dist/{managed-binary-DcpnJF9x.js → managed-binary-DrjjRRUD.js} +4 -103
- package/dist/managed-thread-store-D1t5jh3G.js +66 -0
- package/dist/media-understanding-provider.js +4 -1
- package/dist/{model-catalog-DT5vo6qo.js → model-catalog-B_6M04Wo.js} +6 -5
- package/dist/{models-CcrgSMQR.js → models-P9k2qAof.js} +3 -3
- package/dist/native-execution-policy-umd0tgh4.js +128 -0
- package/dist/{native-subagent-monitor-B3bvQGcg.js → native-subagent-monitor-BbCUKRwC.js} +69 -42
- package/dist/node-cli-sessions-rj0ysJwM.js +527 -0
- package/dist/{node-exec-server.runtime-nfbUOtUB.js → node-exec-server.runtime-BY_ctYnT.js} +53 -22
- package/dist/plan-BMJdMOEF.js +711 -0
- package/dist/plugin-activation-BQK-f5sV.js +133 -0
- package/dist/{thread-resume-BLgqkUc1.js → plugin-app-cache-key-CdSQzWyX.js} +4 -52
- package/dist/{plugin-approval-roundtrip-aQVdyL5g.js → plugin-approval-roundtrip-DeW95wB5.js} +1 -1
- package/dist/plugin-inventory-CoRCzWW4.js +325 -0
- package/dist/plugin-marketplace-discovery-3akBizzF.js +134 -0
- package/dist/{plugin-metadata-cache-BwCLTY_U.js → plugin-metadata-cache-DgNfRAGA.js} +13 -9
- package/dist/protocol-CAAcVIKF.js +275 -0
- package/dist/{provider-capabilities-BpG9Ltc5.js → provider-capabilities-W4tfK5Y5.js} +2 -2
- package/dist/{rate-limits-Fx_l4eNa.js → rate-limits-Bqnx1N4z.js} +1 -1
- package/dist/request-CGvGOpeR.js +198 -0
- package/dist/{run-attempt-BzthFQSM.js → run-attempt-DwKds3DN.js} +589 -373
- package/dist/{runtime-artifact-Bh_gf6-t.js → runtime-artifact-T-NwqeSH.js} +27 -27
- package/dist/sandbox-guard-BcT66fWy.js +165 -0
- package/dist/{session-binding-4Ki04sW-.js → session-binding-CKSepwIC.js} +29 -47
- package/dist/{session-binding-record-CyaXZvTe.js → session-binding-record-CaZjYwNS.js} +11 -25
- package/dist/session-catalog-HaDcp7Sv.js +1533 -0
- package/dist/session-catalog-adoption-BKLtSrwi.js +251 -0
- package/dist/session-catalog-archive-D0i2pGj2.js +56 -0
- package/dist/session-catalog-home-id-D7-aLiNt.js +190 -0
- package/dist/session-catalog-node-adoption-BUuDYY8R.js +182 -0
- package/dist/session-catalog-node-continue-CBtc4wkI.js +238 -0
- package/dist/session-catalog-parsing-CdD3JhdM.js +280 -0
- package/dist/session-catalog-provenance-DkJHo735.js +124 -0
- package/dist/{session-cli-BjLhpmS1.js → session-cli-CbgaHh0f.js} +3 -2
- package/dist/{session-history-DWmMqiqW.js → session-history-Cz-tOOYj.js} +6 -3
- package/dist/{session-upstream-marker-DReTpGz6.js → session-history-import-DFN7sWRU.js} +5 -50
- package/dist/{session-history-read-LHldwvyh.js → session-history-read-cxPUxyVV.js} +21 -7
- package/dist/session-history-worker-runtime.js +18 -6
- package/dist/{session-history.worker-zZVcx0ME.js → session-history.worker-CNEvGaU5.js} +58 -51
- package/dist/session-history.worker.js +1 -1
- package/dist/{session-initialization-BCFVnLL4.js → session-initialization-Jo86C60I.js} +2 -2
- package/dist/{session-permission-policy-BaueHmSj.js → session-permission-policy-B22InTp_.js} +4 -7
- package/dist/{session-retirement-DNdmYdFc.js → session-retirement-BSNG_mhf.js} +6 -5
- package/dist/session-upstream-marker-D15C9NHp.js +46 -0
- package/dist/{settled-turn-context-BOM3Y1pl.js → settled-turn-context-Du6gdUDd.js} +9 -9
- package/dist/{settled-turn-finalizer-BI-b1sry.js → settled-turn-finalizer-BuLUrZl2.js} +7 -6
- package/dist/shared-client-CVhez2Uo.js +2911 -0
- package/dist/{side-question-B6DWAw1A.js → side-question-DLbr2vUU.js} +64 -169
- package/dist/source-Da-h6f7e.js +518 -0
- package/dist/thread-archive-guard-D1bP04Tf.js +53 -0
- package/dist/{thread-lifecycle-DPK30jad.js → thread-lifecycle-D1pzRwHB.js} +112 -616
- package/dist/{thread-ownership-CbucAmmp.js → thread-ownership-D6oASVRu.js} +13 -15
- package/dist/thread-policy-D8kI7yAO.js +54 -0
- package/dist/thread-resume-T5cveyxA.js +52 -0
- package/dist/timeout-BVw8aaM1.js +33 -0
- package/dist/{transcript-mirror-attestation-Byl34PC-.js → transcript-mirror-attestation-DibE0krH.js} +17 -4
- package/dist/{transcript-mirror-_njEB84z.js → transcript-mirror-t5TOccct.js} +50 -260
- package/dist/{transport-stdio-CzTngV0j.js → transport-stdio-BGjHoYLr.js} +73 -43
- package/dist/{upstream-session-fork-1fwbNKzQ.js → upstream-session-fork-De-sLJ2r.js} +26 -18
- package/dist/{usage-i9fv5-4m.js → usage-CPPTmBvZ.js} +4 -3
- package/dist/version-omm6IPI3.js +12 -0
- package/dist/{web-search-provider.runtime-DKQn67A7.js → web-search-provider.runtime-CSVgQL0w.js} +3 -3
- package/openclaw.plugin.json +22 -1
- package/package.json +4 -4
- package/dist/request-C8H22rPi.js +0 -475
- package/dist/session-catalog-B47uwekb.js +0 -3272
- package/dist/shared-client-CKhX2kFt.js +0 -7505
- package/dist/thread-policy-CTalhWFy.js +0 -105
- package/dist/{auth-binding-Ds_QVm-X.js → auth-binding-Co2Vfcf_.js} +1 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { l as sessionBindingIdentity } from "./session-binding-record-CaZjYwNS.js";
|
|
2
|
+
import { S as requireBoundThread, o as CatalogParamsError, u as boundedCatalogString } from "./session-catalog-parsing-CdD3JhdM.js";
|
|
3
|
+
import { n as codexUpstreamBaseline, t as codexLastTerminalTurnId } from "./session-upstream-marker-D15C9NHp.js";
|
|
4
|
+
import { i as reclaimCurrentCodexSessionGeneration } from "./session-binding-CKSepwIC.js";
|
|
5
|
+
import { adoptionSessionKeyRest, continueOperations, runSessionActionExclusive } from "./session-catalog-node-adoption-BUuDYY8R.js";
|
|
6
|
+
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
7
|
+
import { listSessionCatalogEntries, sessionCatalogAdoptedSessionKey, sessionCatalogAdoptedSourceKey } from "openclaw/plugin-sdk/session-catalog";
|
|
8
|
+
//#region extensions/codex/src/session-catalog-adoption.ts
|
|
9
|
+
const CODEX_SUPERVISION_SESSION_KEY_PREFIX = "harness:codex:supervision:";
|
|
10
|
+
const boundCatalogSessionId = (value) => boundedCatalogString(value, 256);
|
|
11
|
+
function requireIdleThread(thread, action) {
|
|
12
|
+
if (thread.status?.type === "idle" || action === "archive" && thread.status?.type === "notLoaded") return;
|
|
13
|
+
if (thread.status?.type === "active") throw new CatalogParamsError(`Codex session is active in this App Server; wait for it to finish before ${action === "continue" ? "starting a branch" : "archiving"}`);
|
|
14
|
+
throw new CatalogParamsError(action === "archive" ? "Codex session cannot be archived in its current state" : "Codex session cannot start a branch in its current state");
|
|
15
|
+
}
|
|
16
|
+
function adoptionSessionKey(threadId, sourceHomeId) {
|
|
17
|
+
const source = sourceHomeId ? JSON.stringify([sourceHomeId, threadId]) : threadId;
|
|
18
|
+
return sessionCatalogAdoptedSessionKey(CODEX_SUPERVISION_SESSION_KEY_PREFIX, source);
|
|
19
|
+
}
|
|
20
|
+
function isAdoptionSessionKeyForThread(sessionKey, threadId, sourceHomeId) {
|
|
21
|
+
return adoptionSessionKeyRest(sessionKey) === adoptionSessionKey(threadId, sourceHomeId);
|
|
22
|
+
}
|
|
23
|
+
function readCodexSupervisionMarker(entry) {
|
|
24
|
+
const codex = isRecord(entry.pluginExtensions?.codex) ? entry.pluginExtensions.codex : void 0;
|
|
25
|
+
const marker = codex && isRecord(codex.supervision) ? codex.supervision : void 0;
|
|
26
|
+
const sourceThreadId = marker?.sourceThreadId;
|
|
27
|
+
const sourceHomeId = marker?.sourceHomeId;
|
|
28
|
+
if (typeof sourceThreadId !== "string" || !sourceThreadId.trim() || sourceHomeId !== void 0 && (typeof sourceHomeId !== "string" || !sourceHomeId.trim())) return;
|
|
29
|
+
return {
|
|
30
|
+
sourceThreadId: sourceThreadId.trim(),
|
|
31
|
+
...typeof sourceHomeId === "string" ? { sourceHomeId: sourceHomeId.trim() } : {}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
async function listAdoptedSessionEntries(params) {
|
|
35
|
+
const adopted = /* @__PURE__ */ new Map();
|
|
36
|
+
for (const { agentId, entry, sessionKey } of listSessionCatalogEntries({
|
|
37
|
+
...params.agentId ? { agentId: params.agentId } : {},
|
|
38
|
+
config: params.config ?? {},
|
|
39
|
+
runtime: params.runtime,
|
|
40
|
+
sessionEntries: params.sessionEntries
|
|
41
|
+
})) {
|
|
42
|
+
const sessionKeyRest = adoptionSessionKeyRest(sessionKey);
|
|
43
|
+
const marker = readCodexSupervisionMarker(entry);
|
|
44
|
+
if (!sessionKeyRest.startsWith(CODEX_SUPERVISION_SESSION_KEY_PREFIX) || !marker || entry.initializationPending === true || entry.agentHarnessId !== "codex" || entry.modelSelectionLocked !== true) continue;
|
|
45
|
+
const sessionId = entry.sessionId?.trim();
|
|
46
|
+
if (!sessionId) continue;
|
|
47
|
+
const binding = params.bindingStore.read(sessionBindingIdentity({
|
|
48
|
+
sessionId,
|
|
49
|
+
sessionKey,
|
|
50
|
+
config: params.config
|
|
51
|
+
}));
|
|
52
|
+
const sourceThreadId = binding?.supervisionSourceThreadId?.trim();
|
|
53
|
+
const boundThreadId = binding?.threadId.trim();
|
|
54
|
+
if (binding?.connectionScope !== "supervision" || !sourceThreadId || !boundThreadId || sessionKeyRest !== adoptionSessionKey(sourceThreadId, marker.sourceHomeId)) continue;
|
|
55
|
+
const sourceKey = sessionCatalogAdoptedSourceKey(marker.sourceHomeId ?? "gateway:local", sourceThreadId);
|
|
56
|
+
if (adopted.has(sourceKey)) throw new Error(`multiple OpenClaw sessions adopt Codex thread ${sourceThreadId} from the same home`);
|
|
57
|
+
adopted.set(sourceKey, {
|
|
58
|
+
key: sessionKey,
|
|
59
|
+
sessionId,
|
|
60
|
+
agentId,
|
|
61
|
+
boundThreadId
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return adopted;
|
|
65
|
+
}
|
|
66
|
+
async function findAdoptedSessionEntry(params) {
|
|
67
|
+
const adopted = await listAdoptedSessionEntries(params);
|
|
68
|
+
return adopted.get(sessionCatalogAdoptedSourceKey(params.sourceHomeId ?? "gateway:local", params.threadId)) ?? (params.sourceHomeId && params.allowLegacy === true ? adopted.get(sessionCatalogAdoptedSourceKey("gateway:local", params.threadId)) : void 0);
|
|
69
|
+
}
|
|
70
|
+
function matchesPendingAdoptionBinding(binding, expected) {
|
|
71
|
+
const historyCoveredThrough = binding?.historyCoveredThrough;
|
|
72
|
+
return binding?.threadId === expected.sourceThreadId && binding.connectionScope === "supervision" && binding.supervisionSourceThreadId === expected.sourceThreadId && binding.cwd === expected.cwd && binding.conversationSourceTransferComplete === true && binding.preserveNativeModel === true && binding.pendingSupervisionBranch?.sourceThreadId === expected.sourceThreadId && binding.pendingSupervisionBranch.connectionFingerprint === expected.connectionFingerprint && binding.pendingSupervisionBranch.lastTurnId === expected.lastTurnId && (binding.pendingSupervisionBranch.cleanupThreadIds?.length ?? 0) === 0 && typeof historyCoveredThrough === "string" && Number.isFinite(Date.parse(historyCoveredThrough));
|
|
73
|
+
}
|
|
74
|
+
async function ensurePendingAdoptionBinding(params) {
|
|
75
|
+
const pending = {
|
|
76
|
+
sourceThreadId: params.sourceThreadId,
|
|
77
|
+
connectionFingerprint: params.connectionFingerprint,
|
|
78
|
+
...params.lastTurnId ? { lastTurnId: params.lastTurnId } : {}
|
|
79
|
+
};
|
|
80
|
+
const ownsGeneration = await reclaimCurrentCodexSessionGeneration({
|
|
81
|
+
assertCurrent: params.initialization.assertCurrent,
|
|
82
|
+
bindingStore: params.bindingStore,
|
|
83
|
+
identity: params.identity,
|
|
84
|
+
config: params.config
|
|
85
|
+
});
|
|
86
|
+
params.initialization.assertCurrent();
|
|
87
|
+
if (!ownsGeneration) throw new Error(`failed to claim the OpenClaw session generation for ${params.sourceThreadId}`);
|
|
88
|
+
const existing = params.bindingStore.read(params.identity);
|
|
89
|
+
params.initialization.assertCurrent();
|
|
90
|
+
if (existing) {
|
|
91
|
+
if (matchesPendingAdoptionBinding(existing, params)) return;
|
|
92
|
+
throw new Error(`OpenClaw session is already bound to Codex thread ${existing.threadId}`);
|
|
93
|
+
}
|
|
94
|
+
const binding = {
|
|
95
|
+
threadId: params.sourceThreadId,
|
|
96
|
+
connectionScope: "supervision",
|
|
97
|
+
supervisionSourceThreadId: params.sourceThreadId,
|
|
98
|
+
cwd: params.cwd,
|
|
99
|
+
historyCoveredThrough: (/* @__PURE__ */ new Date()).toISOString(),
|
|
100
|
+
conversationSourceTransferComplete: true,
|
|
101
|
+
preserveNativeModel: true,
|
|
102
|
+
pendingSupervisionBranch: pending
|
|
103
|
+
};
|
|
104
|
+
await params.initialization.bind(binding);
|
|
105
|
+
}
|
|
106
|
+
async function createOrReuseAdoptedSession(params) {
|
|
107
|
+
const runtime = params.api.runtime;
|
|
108
|
+
const lookup = {
|
|
109
|
+
...params,
|
|
110
|
+
runtime,
|
|
111
|
+
threadId: params.sourceThread.id
|
|
112
|
+
};
|
|
113
|
+
const existing = await findAdoptedSessionEntry(lookup);
|
|
114
|
+
if (existing) return existing;
|
|
115
|
+
try {
|
|
116
|
+
const spawnedCwd = params.sourceThread.cwd?.trim() || void 0;
|
|
117
|
+
const pendingLastTurnId = codexLastTerminalTurnId(params.sourceThread, boundCatalogSessionId);
|
|
118
|
+
const marker = {
|
|
119
|
+
sourceThreadId: params.sourceThread.id,
|
|
120
|
+
...params.sourceHomeId ? { sourceHomeId: params.sourceHomeId } : {}
|
|
121
|
+
};
|
|
122
|
+
const { createImportedCodexSession } = await import("./session-history-import-DFN7sWRU.js");
|
|
123
|
+
const created = await createImportedCodexSession({
|
|
124
|
+
runtime: params.api.runtime,
|
|
125
|
+
bindingStore: params.bindingStore,
|
|
126
|
+
config: params.config,
|
|
127
|
+
key: adoptionSessionKey(params.sourceThread.id, params.sourceHomeId),
|
|
128
|
+
agentId: params.agentId,
|
|
129
|
+
displayName: params.sourceThread.name ?? void 0,
|
|
130
|
+
thread: params.sourceThread,
|
|
131
|
+
throughTurnId: pendingLastTurnId ?? null,
|
|
132
|
+
recoverMatchingInitialEntry: true,
|
|
133
|
+
initialEntry: {
|
|
134
|
+
agentHarnessId: "codex",
|
|
135
|
+
modelSelectionLocked: true,
|
|
136
|
+
pluginExtensions: { codex: { supervision: {
|
|
137
|
+
...marker,
|
|
138
|
+
initializing: true,
|
|
139
|
+
modelLocked: true
|
|
140
|
+
} } }
|
|
141
|
+
},
|
|
142
|
+
afterImport: async (entry, initialization) => {
|
|
143
|
+
const identity = sessionBindingIdentity({
|
|
144
|
+
sessionId: entry.sessionId,
|
|
145
|
+
sessionKey: entry.key,
|
|
146
|
+
config: params.config
|
|
147
|
+
});
|
|
148
|
+
await ensurePendingAdoptionBinding({
|
|
149
|
+
bindingStore: params.bindingStore,
|
|
150
|
+
config: params.config,
|
|
151
|
+
identity,
|
|
152
|
+
initialization,
|
|
153
|
+
sourceThreadId: params.sourceThread.id,
|
|
154
|
+
connectionFingerprint: params.connectionFingerprint,
|
|
155
|
+
cwd: spawnedCwd ?? "",
|
|
156
|
+
...pendingLastTurnId ? { lastTurnId: pendingLastTurnId } : {}
|
|
157
|
+
});
|
|
158
|
+
return { pluginExtensions: { codex: { supervision: {
|
|
159
|
+
...marker,
|
|
160
|
+
modelLocked: true
|
|
161
|
+
} } } };
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
key: created.key,
|
|
166
|
+
sessionId: created.sessionId,
|
|
167
|
+
agentId: created.agentId,
|
|
168
|
+
boundThreadId: params.sourceThread.id
|
|
169
|
+
};
|
|
170
|
+
} catch (error) {
|
|
171
|
+
const raced = await findAdoptedSessionEntry(lookup);
|
|
172
|
+
if (raced) return raced;
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function continueLocalCodexSessionInner(params) {
|
|
177
|
+
await params.control.requireEligibleThread(params.threadId);
|
|
178
|
+
const existing = await findAdoptedSessionEntry({
|
|
179
|
+
...params,
|
|
180
|
+
runtime: params.api.runtime
|
|
181
|
+
});
|
|
182
|
+
if (existing) {
|
|
183
|
+
const boundThreadId = requireBoundThread(existing);
|
|
184
|
+
const boundThread = await params.control.readThread(boundThreadId, true);
|
|
185
|
+
if (boundThread.id !== boundThreadId) throw new Error("Codex app-server returned a different thread than requested");
|
|
186
|
+
const changedError = () => new CatalogParamsError("Codex OpenClaw session changed before it could be opened. Retry.");
|
|
187
|
+
if (!await params.api.runtime.agent.session.patchSessionEntry({
|
|
188
|
+
sessionKey: existing.key,
|
|
189
|
+
readConsistency: "latest",
|
|
190
|
+
preserveActivity: true,
|
|
191
|
+
update: (entry) => {
|
|
192
|
+
if (entry.sessionId?.trim() !== existing.sessionId || entry.initializationPending === true || entry.agentHarnessId !== "codex" || entry.modelSelectionLocked !== true) throw changedError();
|
|
193
|
+
return {
|
|
194
|
+
archivedAt: void 0,
|
|
195
|
+
archivedBy: void 0,
|
|
196
|
+
archiveReason: void 0
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
})) throw changedError();
|
|
200
|
+
const connectionFingerprint = params.control.connectionFingerprint;
|
|
201
|
+
if (connectionFingerprint) params.onContinued?.({
|
|
202
|
+
connectionFingerprint,
|
|
203
|
+
...codexUpstreamBaseline(boundThread, boundCatalogSessionId)
|
|
204
|
+
});
|
|
205
|
+
return {
|
|
206
|
+
sessionKey: existing.key,
|
|
207
|
+
disposition: "existing"
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const sourceThread = await params.control.readThread(params.threadId, true);
|
|
211
|
+
if (sourceThread.id !== params.threadId) throw new Error("Codex app-server returned a different thread than requested");
|
|
212
|
+
if (sourceThread.status?.type !== "notLoaded") requireIdleThread(sourceThread, "continue");
|
|
213
|
+
const connectionFingerprint = params.control.connectionFingerprint;
|
|
214
|
+
if (!connectionFingerprint) throw new Error("Codex Continue requires a pinned app-server connection");
|
|
215
|
+
const adopted = await createOrReuseAdoptedSession({
|
|
216
|
+
...params,
|
|
217
|
+
sourceThread,
|
|
218
|
+
connectionFingerprint
|
|
219
|
+
});
|
|
220
|
+
const boundThreadId = requireBoundThread(adopted);
|
|
221
|
+
const baselineThread = boundThreadId === sourceThread.id ? sourceThread : await params.control.readThread(boundThreadId, true);
|
|
222
|
+
if (baselineThread.id !== boundThreadId) throw new Error("Codex app-server returned a different thread than requested");
|
|
223
|
+
params.onContinued?.({
|
|
224
|
+
connectionFingerprint,
|
|
225
|
+
...codexUpstreamBaseline(baselineThread, boundCatalogSessionId)
|
|
226
|
+
});
|
|
227
|
+
return {
|
|
228
|
+
sessionKey: adopted.key,
|
|
229
|
+
disposition: "forked"
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/** Creates one locked OpenClaw branch whose first harness run forks the Codex source. */
|
|
233
|
+
async function continueLocalCodexSession(params) {
|
|
234
|
+
const sourceKey = sessionCatalogAdoptedSourceKey(params.hostId ?? "gateway:local", params.threadId);
|
|
235
|
+
const operationKey = sessionCatalogAdoptedSourceKey(params.agentId, sourceKey);
|
|
236
|
+
const current = continueOperations.get(operationKey);
|
|
237
|
+
if (current) return await current;
|
|
238
|
+
const run = async (control) => await continueLocalCodexSessionInner({
|
|
239
|
+
...params,
|
|
240
|
+
control
|
|
241
|
+
});
|
|
242
|
+
const operation = runSessionActionExclusive(sourceKey, async () => params.control.withPinnedConnection(run));
|
|
243
|
+
continueOperations.set(operationKey, operation);
|
|
244
|
+
try {
|
|
245
|
+
return await operation;
|
|
246
|
+
} finally {
|
|
247
|
+
if (continueOperations.get(operationKey) === operation) continueOperations.delete(operationKey);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
//#endregion
|
|
251
|
+
export { continueLocalCodexSession, isAdoptionSessionKeyForThread, listAdoptedSessionEntries, requireIdleThread };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { l as sessionBindingIdentity } from "./session-binding-record-CaZjYwNS.js";
|
|
2
|
+
import { t as assertCodexArchiveDescendantsUnowned } from "./thread-archive-guard-D1bP04Tf.js";
|
|
3
|
+
import { o as CatalogParamsError } from "./session-catalog-parsing-CdD3JhdM.js";
|
|
4
|
+
import "./session-binding-CKSepwIC.js";
|
|
5
|
+
import { runSessionActionExclusive } from "./session-catalog-node-adoption-BUuDYY8R.js";
|
|
6
|
+
import { isAdoptionSessionKeyForThread, requireIdleThread } from "./session-catalog-adoption-BKLtSrwi.js";
|
|
7
|
+
import { listAgentIds } from "openclaw/plugin-sdk/agent-scope-runtime";
|
|
8
|
+
import { sessionCatalogAdoptedSourceKey } from "openclaw/plugin-sdk/session-catalog";
|
|
9
|
+
//#region extensions/codex/src/session-catalog-archive.ts
|
|
10
|
+
function assertNoPendingSupervisionBranch(params) {
|
|
11
|
+
const adoptedEntries = [params.agentId, ...listAgentIds(params.config).filter((agentId) => agentId !== params.agentId)].flatMap((agentId) => params.runtime.agent.session.listSessionEntries({
|
|
12
|
+
agentId,
|
|
13
|
+
readOnly: true
|
|
14
|
+
})).filter((candidate) => isAdoptionSessionKeyForThread(candidate.sessionKey, params.threadId, params.sourceHomeId) || params.sourceHomeId !== void 0 && params.allowLegacy === true && isAdoptionSessionKeyForThread(candidate.sessionKey, params.threadId));
|
|
15
|
+
for (const adopted of adoptedEntries) {
|
|
16
|
+
if (adopted.entry.initializationPending === true) throw new CatalogParamsError("Codex session cannot be archived while its OpenClaw branch is initializing");
|
|
17
|
+
const sessionId = adopted.entry.sessionId?.trim();
|
|
18
|
+
if (!sessionId) continue;
|
|
19
|
+
const binding = params.bindingStore.read(sessionBindingIdentity({
|
|
20
|
+
sessionId,
|
|
21
|
+
sessionKey: adopted.sessionKey,
|
|
22
|
+
config: params.config
|
|
23
|
+
}));
|
|
24
|
+
if (binding?.connectionScope === "supervision" && binding.supervisionSourceThreadId === params.threadId && binding.pendingSupervisionBranch?.sourceThreadId === params.threadId) throw new CatalogParamsError("Codex session cannot be archived until its OpenClaw branch starts");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Archives one inactive Gateway-local Codex thread after a fresh status read. */
|
|
28
|
+
async function archiveLocalCodexSession(params) {
|
|
29
|
+
return await runSessionActionExclusive(sessionCatalogAdoptedSourceKey(params.hostId ?? "gateway:local", params.threadId), async () => {
|
|
30
|
+
return await params.bindingStore.withThreadArchiveFence(async () => {
|
|
31
|
+
const run = async (control) => {
|
|
32
|
+
assertNoPendingSupervisionBranch(params);
|
|
33
|
+
await control.requireEligibleThread(params.threadId);
|
|
34
|
+
const thread = await control.readThread(params.threadId, false);
|
|
35
|
+
if (thread.id !== params.threadId) throw new Error("Codex app-server returned a different thread than requested");
|
|
36
|
+
requireIdleThread(thread, "archive");
|
|
37
|
+
if (await params.bindingStore.hasOtherThreadOwner(params.threadId)) throw new CatalogParamsError("Codex session cannot be archived while it is attached to an OpenClaw session");
|
|
38
|
+
await assertCodexArchiveDescendantsUnowned({
|
|
39
|
+
bindingStore: params.bindingStore,
|
|
40
|
+
threadId: params.threadId,
|
|
41
|
+
listPage: (request) => control.listDescendantPage(request),
|
|
42
|
+
assertDescendantIdle: async (descendantThreadId) => {
|
|
43
|
+
const descendant = await control.readThread(descendantThreadId, false);
|
|
44
|
+
if (descendant.id !== descendantThreadId) throw new Error("Codex app-server returned a different descendant than requested");
|
|
45
|
+
requireIdleThread(descendant, "archive");
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
await control.archiveThread(params.threadId);
|
|
49
|
+
return { archived: true };
|
|
50
|
+
};
|
|
51
|
+
return await params.control.withPinnedConnection(run);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { archiveLocalCodexSession };
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-LphB8slG.js";
|
|
2
|
+
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { truncateUtf8Prefix } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
7
|
+
import { Buffer } from "node:buffer";
|
|
8
|
+
//#region extensions/codex/src/app-server/transcript-history-projection.ts
|
|
9
|
+
const CODEX_HISTORY_IMPORT_MAX_MESSAGES = 200;
|
|
10
|
+
const CODEX_HISTORY_IMPORT_MAX_BYTES = 524288;
|
|
11
|
+
const CODEX_HISTORY_IMPORT_MAX_MESSAGE_BYTES = 65536;
|
|
12
|
+
const CODEX_HISTORY_TRUNCATION_SUFFIX = "\n\n[Message truncated during Codex history import.]";
|
|
13
|
+
const CODEX_HISTORY_ASSISTANT_API = "openai-chatgpt-responses";
|
|
14
|
+
const CODEX_HISTORY_ASSISTANT_PROVIDER = "openai";
|
|
15
|
+
const CODEX_HISTORY_ASSISTANT_MODEL = "native-history";
|
|
16
|
+
const CODEX_HISTORY_ZERO_USAGE = {
|
|
17
|
+
input: 0,
|
|
18
|
+
output: 0,
|
|
19
|
+
cacheRead: 0,
|
|
20
|
+
cacheWrite: 0,
|
|
21
|
+
totalTokens: 0,
|
|
22
|
+
cost: {
|
|
23
|
+
input: 0,
|
|
24
|
+
output: 0,
|
|
25
|
+
cacheRead: 0,
|
|
26
|
+
cacheWrite: 0,
|
|
27
|
+
total: 0
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
function normalizeImportedHistoryText(value) {
|
|
31
|
+
if (typeof value !== "string") return;
|
|
32
|
+
const text = value.trim();
|
|
33
|
+
if (!text) return;
|
|
34
|
+
if (Buffer.byteLength(text, "utf8") <= CODEX_HISTORY_IMPORT_MAX_MESSAGE_BYTES) return text;
|
|
35
|
+
const suffixBytes = Buffer.byteLength(CODEX_HISTORY_TRUNCATION_SUFFIX, "utf8");
|
|
36
|
+
const contentLimitBytes = Math.max(0, CODEX_HISTORY_IMPORT_MAX_MESSAGE_BYTES - suffixBytes);
|
|
37
|
+
return `${truncateUtf8Prefix(text, contentLimitBytes)}${CODEX_HISTORY_TRUNCATION_SUFFIX}`;
|
|
38
|
+
}
|
|
39
|
+
function projectCodexUserItemText(item) {
|
|
40
|
+
if (!Array.isArray(item.content)) return;
|
|
41
|
+
const parts = [];
|
|
42
|
+
for (const value of item.content) {
|
|
43
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
44
|
+
const input = value;
|
|
45
|
+
if (input.type === "text") {
|
|
46
|
+
const text = normalizeImportedHistoryText(input.text);
|
|
47
|
+
if (text) parts.push(text);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (input.type === "image" || input.type === "localImage") {
|
|
51
|
+
parts.push("[Image attachment]");
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (input.type === "audio" || input.type === "localAudio" || input.type === "local_audio") parts.push("[Audio attachment]");
|
|
55
|
+
if (input.type === "skill" || input.type === "mention") {
|
|
56
|
+
const name = normalizeOptionalString(input.name);
|
|
57
|
+
if (name) parts.push(`${input.type === "skill" ? "$" : "@"}${name}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return normalizeImportedHistoryText(parts.join("\n"));
|
|
61
|
+
}
|
|
62
|
+
function selectTurnsThroughBoundary(thread, throughTurnId) {
|
|
63
|
+
if (throughTurnId === null) return [];
|
|
64
|
+
const turns = thread.turns ?? [];
|
|
65
|
+
const boundaryIndex = turns.findIndex((turn) => turn.id === throughTurnId);
|
|
66
|
+
if (boundaryIndex < 0) throw new Error(`Codex history boundary turn not found: ${throughTurnId}`);
|
|
67
|
+
const boundary = turns[boundaryIndex];
|
|
68
|
+
if (boundary?.status !== "completed" && boundary?.status !== "interrupted" && boundary?.status !== "failed") throw new Error(`Codex history boundary turn is not terminal: ${throughTurnId}`);
|
|
69
|
+
return turns.slice(0, boundaryIndex + 1);
|
|
70
|
+
}
|
|
71
|
+
function projectCodexThreadHistory(params) {
|
|
72
|
+
const projected = [];
|
|
73
|
+
const threadTimestamp = typeof params.thread.createdAt === "number" && Number.isFinite(params.thread.createdAt) ? params.thread.createdAt * 1e3 : params.importedAt;
|
|
74
|
+
let itemOffset = 0;
|
|
75
|
+
for (const turn of selectTurnsThroughBoundary(params.thread, params.throughTurnId)) for (const value of turn.items) {
|
|
76
|
+
const item = value;
|
|
77
|
+
const itemId = normalizeOptionalString(item.id);
|
|
78
|
+
const identity = `${turn.id}:${itemId ?? itemOffset}`;
|
|
79
|
+
const timestampSeconds = item.type === "agentMessage" ? turn.completedAt ?? turn.startedAt : turn.startedAt ?? turn.completedAt;
|
|
80
|
+
const timestamp = typeof timestampSeconds === "number" && Number.isFinite(timestampSeconds) ? timestampSeconds * 1e3 + itemOffset : threadTimestamp + itemOffset;
|
|
81
|
+
const text = item.type === "userMessage" ? projectCodexUserItemText(item) : item.type === "agentMessage" ? normalizeImportedHistoryText(item.text) : void 0;
|
|
82
|
+
const role = item.type === "userMessage" ? "user" : item.type === "agentMessage" ? "assistant" : void 0;
|
|
83
|
+
itemOffset += 1;
|
|
84
|
+
if (!text || !role) continue;
|
|
85
|
+
const phase = item.phase === "commentary" || item.phase === "final_answer" ? item.phase : void 0;
|
|
86
|
+
const asyncDelivery = item.delivery === "async";
|
|
87
|
+
const message = role === "assistant" ? attachCodexMirrorIdentity({
|
|
88
|
+
role,
|
|
89
|
+
content: [{
|
|
90
|
+
type: "text",
|
|
91
|
+
text
|
|
92
|
+
}],
|
|
93
|
+
api: CODEX_HISTORY_ASSISTANT_API,
|
|
94
|
+
provider: normalizeOptionalString(params.modelProvider) ?? normalizeOptionalString(params.thread.modelProvider) ?? CODEX_HISTORY_ASSISTANT_PROVIDER,
|
|
95
|
+
model: CODEX_HISTORY_ASSISTANT_MODEL,
|
|
96
|
+
usage: CODEX_HISTORY_ZERO_USAGE,
|
|
97
|
+
stopReason: turn.status === "interrupted" ? "aborted" : turn.status === "failed" ? "error" : "stop",
|
|
98
|
+
...turn.status === "failed" && turn.error?.message ? { errorMessage: turn.error.message } : {},
|
|
99
|
+
...phase ? { phase } : {},
|
|
100
|
+
...asyncDelivery && itemId ? { openclawAsyncDelivery: { itemId } } : {},
|
|
101
|
+
timestamp
|
|
102
|
+
}, identity) : attachCodexMirrorIdentity({
|
|
103
|
+
role,
|
|
104
|
+
content: text,
|
|
105
|
+
timestamp
|
|
106
|
+
}, identity);
|
|
107
|
+
projected.push({
|
|
108
|
+
message,
|
|
109
|
+
responseItem: {
|
|
110
|
+
type: "message",
|
|
111
|
+
role,
|
|
112
|
+
content: [{
|
|
113
|
+
type: role === "assistant" ? "output_text" : "input_text",
|
|
114
|
+
text
|
|
115
|
+
}],
|
|
116
|
+
...role === "assistant" && phase ? { phase } : {}
|
|
117
|
+
},
|
|
118
|
+
textBytes: Buffer.byteLength(text, "utf8")
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return projected;
|
|
122
|
+
}
|
|
123
|
+
function selectBoundedCodexHistoryTail(projected) {
|
|
124
|
+
const selected = [];
|
|
125
|
+
let selectedBytes = 0;
|
|
126
|
+
for (let index = projected.length - 1; index >= 0; index -= 1) {
|
|
127
|
+
const candidate = projected[index];
|
|
128
|
+
if (!candidate) continue;
|
|
129
|
+
if (selected.length >= CODEX_HISTORY_IMPORT_MAX_MESSAGES || selectedBytes + candidate.textBytes > CODEX_HISTORY_IMPORT_MAX_BYTES) break;
|
|
130
|
+
selected.push(candidate);
|
|
131
|
+
selectedBytes += candidate.textBytes;
|
|
132
|
+
}
|
|
133
|
+
return selected.toReversed();
|
|
134
|
+
}
|
|
135
|
+
/** Projects one terminal Codex history prefix into transcript and Responses API items. */
|
|
136
|
+
function projectBoundedCodexThreadHistory(params) {
|
|
137
|
+
const projected = projectCodexThreadHistory({
|
|
138
|
+
thread: params.thread,
|
|
139
|
+
throughTurnId: params.throughTurnId,
|
|
140
|
+
importedAt: params.importedAt,
|
|
141
|
+
...params.modelProvider ? { modelProvider: params.modelProvider } : {}
|
|
142
|
+
});
|
|
143
|
+
const selected = selectBoundedCodexHistoryTail(projected);
|
|
144
|
+
return {
|
|
145
|
+
importedMessages: selected.length,
|
|
146
|
+
omittedMessages: projected.length - selected.length,
|
|
147
|
+
responseItems: selected.filter(({ message }) => message.role !== "assistant" || message.stopReason !== "aborted" && message.stopReason !== "error" && !("openclawAsyncDelivery" in message)).map(({ responseItem }) => responseItem),
|
|
148
|
+
transcriptMessages: selected.map(({ message }) => message)
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/** Projects only visible local user/assistant messages through the same bounded history policy. */
|
|
152
|
+
function projectBoundedCodexVisibleSessionHistory(entries) {
|
|
153
|
+
const projected = [];
|
|
154
|
+
for (const entry of entries) {
|
|
155
|
+
if (entry.role !== "user" && entry.role !== "assistant" || !("content" in entry.message)) continue;
|
|
156
|
+
if (entry.role === "assistant" && ("stopReason" in entry.message && (entry.message.stopReason === "aborted" || entry.message.stopReason === "error") || "openclawAsyncDelivery" in entry.message)) continue;
|
|
157
|
+
const content = entry.message.content;
|
|
158
|
+
const text = normalizeImportedHistoryText(typeof content === "string" ? content : Array.isArray(content) ? content.flatMap((part) => part && typeof part === "object" && "text" in part && typeof part.text === "string" ? [part.text] : []).join("\n") : void 0);
|
|
159
|
+
if (!text) continue;
|
|
160
|
+
projected.push({
|
|
161
|
+
message: entry.message,
|
|
162
|
+
responseItem: {
|
|
163
|
+
type: "message",
|
|
164
|
+
role: entry.role,
|
|
165
|
+
content: [{
|
|
166
|
+
type: entry.role === "assistant" ? "output_text" : "input_text",
|
|
167
|
+
text
|
|
168
|
+
}]
|
|
169
|
+
},
|
|
170
|
+
textBytes: Buffer.byteLength(text, "utf8")
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return selectBoundedCodexHistoryTail(projected).map(({ responseItem }) => responseItem);
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region extensions/codex/src/session-catalog-home-id.ts
|
|
177
|
+
function canonicalCodexCatalogHome(value) {
|
|
178
|
+
const resolved = path.resolve(value);
|
|
179
|
+
try {
|
|
180
|
+
return fs.realpathSync.native(resolved);
|
|
181
|
+
} catch {
|
|
182
|
+
return resolved;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/** One canonical identity for catalog discovery and durable ownership rows. */
|
|
186
|
+
function codexCatalogHomeId(codexHome) {
|
|
187
|
+
return createHash("sha256").update("openclaw:codex-session-catalog-home:v1\0").update(canonicalCodexCatalogHome(codexHome)).digest("hex");
|
|
188
|
+
}
|
|
189
|
+
//#endregion
|
|
190
|
+
export { projectCodexUserItemText as a, projectBoundedCodexVisibleSessionHistory as i, codexCatalogHomeId as n, projectBoundedCodexThreadHistory as r, canonicalCodexCatalogHome as t };
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { o as CatalogParamsError } from "./session-catalog-parsing-CdD3JhdM.js";
|
|
2
|
+
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
+
import { parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
|
4
|
+
import { resolveStorePath } from "openclaw/plugin-sdk/session-store-paths";
|
|
5
|
+
import { listSessionCatalogEntries, sessionCatalogAdoptedSessionKey, sessionCatalogAdoptedSourceKey } from "openclaw/plugin-sdk/session-catalog";
|
|
6
|
+
//#region extensions/codex/src/session-catalog-node-adoption.ts
|
|
7
|
+
const CODEX_NODE_SESSION_KEY_PREFIX = "harness:codex:node-session:";
|
|
8
|
+
const continueOperations = /* @__PURE__ */ new Map();
|
|
9
|
+
const sessionActionTails = /* @__PURE__ */ new Map();
|
|
10
|
+
async function runSessionActionExclusive(threadId, run) {
|
|
11
|
+
const operation = (sessionActionTails.get(threadId) ?? Promise.resolve()).then(run);
|
|
12
|
+
const tail = operation.then(() => void 0, () => void 0);
|
|
13
|
+
sessionActionTails.set(threadId, tail);
|
|
14
|
+
try {
|
|
15
|
+
return await operation;
|
|
16
|
+
} finally {
|
|
17
|
+
if (sessionActionTails.get(threadId) === tail) sessionActionTails.delete(threadId);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function adoptionSessionKeyRest(sessionKey) {
|
|
21
|
+
const trimmed = sessionKey.trim();
|
|
22
|
+
return parseAgentSessionKey(trimmed)?.rest ?? trimmed;
|
|
23
|
+
}
|
|
24
|
+
function nodeAdoptionSessionKey(hostId, threadId) {
|
|
25
|
+
const source = JSON.stringify([hostId, threadId]);
|
|
26
|
+
return sessionCatalogAdoptedSessionKey(CODEX_NODE_SESSION_KEY_PREFIX, source);
|
|
27
|
+
}
|
|
28
|
+
function readNodeSessionMarker(entry) {
|
|
29
|
+
const codex = isRecord(entry.pluginExtensions?.codex) ? entry.pluginExtensions.codex : void 0;
|
|
30
|
+
const marker = codex && isRecord(codex.sessionCatalog) ? codex.sessionCatalog : void 0;
|
|
31
|
+
if (!marker || typeof marker.sourceHostId !== "string" || !marker.sourceHostId.startsWith("node:") || typeof marker.sourceThreadId !== "string" || !marker.sourceThreadId.trim() || typeof marker.nodeId !== "string" || !marker.nodeId.trim()) return;
|
|
32
|
+
return {
|
|
33
|
+
sourceHostId: marker.sourceHostId,
|
|
34
|
+
sourceThreadId: marker.sourceThreadId,
|
|
35
|
+
nodeId: marker.nodeId,
|
|
36
|
+
...marker.initializing === true ? { initializing: true } : {}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function listNodeAdoptedSessionEntries(params) {
|
|
40
|
+
const adopted = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const { agentId, entry, sessionKey } of listSessionCatalogEntries({
|
|
42
|
+
...params.agentId ? { agentId: params.agentId } : {},
|
|
43
|
+
config: params.config ?? {},
|
|
44
|
+
runtime: params.runtime,
|
|
45
|
+
sessionEntries: params.sessionEntries
|
|
46
|
+
})) {
|
|
47
|
+
const marker = readNodeSessionMarker(entry);
|
|
48
|
+
const sessionId = entry.sessionId?.trim();
|
|
49
|
+
if (!marker || marker.initializing === true && params.includeInitializing !== true || entry.initializationPending === true || entry.agentHarnessId !== "codex" || entry.modelSelectionLocked !== true || !sessionId || adoptionSessionKeyRest(sessionKey) !== nodeAdoptionSessionKey(marker.sourceHostId, marker.sourceThreadId) || marker.sourceHostId !== `node:${marker.nodeId}`) continue;
|
|
50
|
+
const sourceKey = sessionCatalogAdoptedSourceKey(marker.sourceHostId, marker.sourceThreadId);
|
|
51
|
+
if (adopted.has(sourceKey)) throw new Error(`multiple OpenClaw sessions adopt Codex thread ${marker.sourceThreadId} on ${marker.sourceHostId}`);
|
|
52
|
+
adopted.set(sourceKey, {
|
|
53
|
+
key: sessionKey,
|
|
54
|
+
sessionId,
|
|
55
|
+
agentId,
|
|
56
|
+
...marker.initializing === true ? { initializing: true } : {}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return adopted;
|
|
60
|
+
}
|
|
61
|
+
function findNodeAdoptedSessionEntry(params) {
|
|
62
|
+
return listNodeAdoptedSessionEntries(params).get(sessionCatalogAdoptedSourceKey(params.hostId, params.threadId));
|
|
63
|
+
}
|
|
64
|
+
function nodeSessionMarker(params) {
|
|
65
|
+
return {
|
|
66
|
+
sourceHostId: params.hostId,
|
|
67
|
+
sourceThreadId: params.threadId,
|
|
68
|
+
nodeId: params.nodeId,
|
|
69
|
+
...params.initializing === true ? { initializing: true } : {}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
async function finalizeNodeAdoptedSession(params) {
|
|
73
|
+
const changedError = () => new CatalogParamsError("Codex OpenClaw session changed before it could be bound. Retry.");
|
|
74
|
+
let finalized;
|
|
75
|
+
try {
|
|
76
|
+
finalized = await params.api.runtime.agent.session.patchSessionEntry({
|
|
77
|
+
sessionKey: params.adopted.key,
|
|
78
|
+
readConsistency: "latest",
|
|
79
|
+
preserveActivity: true,
|
|
80
|
+
update: (entry) => {
|
|
81
|
+
const current = readNodeSessionMarker(entry);
|
|
82
|
+
if (entry.sessionId?.trim() !== params.adopted.sessionId || entry.initializationPending === true || entry.agentHarnessId !== "codex" || entry.modelSelectionLocked !== true || !current || current.sourceHostId !== params.marker.sourceHostId || current.sourceThreadId !== params.marker.sourceThreadId || current.nodeId !== params.marker.nodeId) throw changedError();
|
|
83
|
+
if (current.initializing !== true) return {
|
|
84
|
+
archivedAt: void 0,
|
|
85
|
+
archivedBy: void 0,
|
|
86
|
+
archiveReason: void 0
|
|
87
|
+
};
|
|
88
|
+
const codex = isRecord(entry.pluginExtensions?.codex) ? entry.pluginExtensions.codex : {};
|
|
89
|
+
return {
|
|
90
|
+
archivedAt: void 0,
|
|
91
|
+
archivedBy: void 0,
|
|
92
|
+
archiveReason: void 0,
|
|
93
|
+
pluginExtensions: {
|
|
94
|
+
...entry.pluginExtensions,
|
|
95
|
+
codex: {
|
|
96
|
+
...codex,
|
|
97
|
+
sessionCatalog: params.marker
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
} catch (error) {
|
|
104
|
+
const currentEntry = params.api.runtime.agent.session.getSessionEntry({
|
|
105
|
+
sessionKey: params.adopted.key,
|
|
106
|
+
readConsistency: "latest"
|
|
107
|
+
});
|
|
108
|
+
const current = currentEntry ? readNodeSessionMarker(currentEntry) : void 0;
|
|
109
|
+
if (currentEntry?.sessionId?.trim() === params.adopted.sessionId && current?.initializing !== true && current?.sourceHostId === params.marker.sourceHostId && current.sourceThreadId === params.marker.sourceThreadId && current.nodeId === params.marker.nodeId) return;
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
if (!finalized) throw changedError();
|
|
113
|
+
}
|
|
114
|
+
async function createOrReuseNodeAdoptedSession(params) {
|
|
115
|
+
const existing = findNodeAdoptedSessionEntry({
|
|
116
|
+
agentId: params.agentId,
|
|
117
|
+
config: params.config,
|
|
118
|
+
runtime: params.api.runtime,
|
|
119
|
+
hostId: params.hostId,
|
|
120
|
+
threadId: params.record.threadId,
|
|
121
|
+
includeInitializing: true
|
|
122
|
+
});
|
|
123
|
+
if (existing) return existing;
|
|
124
|
+
const initializingMarker = {
|
|
125
|
+
...nodeSessionMarker({
|
|
126
|
+
hostId: params.hostId,
|
|
127
|
+
threadId: params.record.threadId,
|
|
128
|
+
nodeId: params.nodeId
|
|
129
|
+
}),
|
|
130
|
+
initializing: true
|
|
131
|
+
};
|
|
132
|
+
try {
|
|
133
|
+
const created = await params.api.runtime.agent.session.createSessionEntry({
|
|
134
|
+
cfg: params.config,
|
|
135
|
+
key: nodeAdoptionSessionKey(params.hostId, params.record.threadId),
|
|
136
|
+
agentId: params.agentId,
|
|
137
|
+
recoverMatchingInitialEntry: true,
|
|
138
|
+
displayName: params.record.name ?? void 0,
|
|
139
|
+
...params.record.cwd?.trim() ? { spawnedCwd: params.record.cwd.trim() } : {},
|
|
140
|
+
initialEntry: {
|
|
141
|
+
agentHarnessId: "codex",
|
|
142
|
+
modelSelectionLocked: true,
|
|
143
|
+
pluginExtensions: { codex: { sessionCatalog: initializingMarker } }
|
|
144
|
+
},
|
|
145
|
+
afterCreate: async (entry) => {
|
|
146
|
+
const storePath = resolveStorePath(params.config.session?.store, { agentId: entry.agentId });
|
|
147
|
+
const { importCodexThreadHistoryToTranscript } = await import("./transcript-mirror-t5TOccct.js").then((n) => n.a);
|
|
148
|
+
await importCodexThreadHistoryToTranscript({
|
|
149
|
+
thread: params.history.thread,
|
|
150
|
+
throughTurnId: params.history.throughTurnId,
|
|
151
|
+
storePath,
|
|
152
|
+
sessionId: entry.sessionId,
|
|
153
|
+
sessionKey: entry.key,
|
|
154
|
+
agentId: entry.agentId,
|
|
155
|
+
...params.record.cwd?.trim() ? { cwd: params.record.cwd.trim() } : {},
|
|
156
|
+
modelProvider: params.record.modelProvider,
|
|
157
|
+
config: params.config
|
|
158
|
+
});
|
|
159
|
+
return { pluginExtensions: { codex: { sessionCatalog: initializingMarker } } };
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
return {
|
|
163
|
+
key: created.key,
|
|
164
|
+
sessionId: created.sessionId,
|
|
165
|
+
agentId: created.agentId,
|
|
166
|
+
initializing: true
|
|
167
|
+
};
|
|
168
|
+
} catch (error) {
|
|
169
|
+
const raced = findNodeAdoptedSessionEntry({
|
|
170
|
+
agentId: params.agentId,
|
|
171
|
+
config: params.config,
|
|
172
|
+
runtime: params.api.runtime,
|
|
173
|
+
hostId: params.hostId,
|
|
174
|
+
threadId: params.record.threadId,
|
|
175
|
+
includeInitializing: true
|
|
176
|
+
});
|
|
177
|
+
if (raced) return raced;
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
export { adoptionSessionKeyRest, continueOperations, createOrReuseNodeAdoptedSession, finalizeNodeAdoptedSession, findNodeAdoptedSessionEntry, listNodeAdoptedSessionEntries, nodeSessionMarker, runSessionActionExclusive };
|