@openclaw/codex 2026.7.1-beta.6 → 2026.7.2-beta.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/README.md +27 -3
- package/dist/app-server-policy-DVcxdse_.js +49 -0
- package/dist/{attempt-notifications-BGsEIIDI.js → attempt-notifications-DEv9h7iC.js} +1 -1
- package/dist/auth-binding-C1czuHPA.js +62 -0
- package/dist/capabilities-42Dfn2TV.js +33 -0
- package/dist/cli-metadata.js +20 -0
- package/dist/{app-server-policy-C968Kgin.js → command-formatters-D6ZlGNmb.js} +8 -51
- package/dist/{command-handlers-Dv-pzAmg.js → command-handlers-Cpl9fUWv.js} +163 -60
- package/dist/{compact-4mVBriKT.js → compact-FnJmiPNA.js} +40 -10
- package/dist/computer-use-DVzr8OP1.js +685 -0
- package/dist/{config-CYEDnLJ2.js → config-c48K5HP9.js} +200 -29
- package/dist/conversation-control-DtQ-E7mr.js +288 -0
- package/dist/doctor-contract-api.js +120 -81
- package/dist/{provider-capabilities-CDnHbmUZ.js → dynamic-tools-BMLoaTeG.js} +5246 -5140
- package/dist/harness.js +55 -15
- package/dist/index.js +1435 -265
- package/dist/{media-understanding-provider-BHOu9-Gg.js → media-understanding-provider-D580L0P8.js} +28 -10
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-DDmO1zwd.js → models-DuKzZA6G.js} +1 -1
- package/dist/notification-correlation-DfaCm0mx.js +67 -0
- package/dist/plugin-app-cache-key-BKNjHMEs.js +315 -0
- package/dist/{protocol-2POPqAY4.js → protocol-BMifTfdW.js} +3 -1
- package/dist/provider-catalog.js +66 -27
- package/dist/provider.js +306 -1
- package/dist/{provider-zjPfx5Fs.js → rate-limits-Dhp04Rqo.js} +4 -261
- package/dist/request-DborTWgw.js +111 -0
- package/dist/{run-attempt-FUyOjGCV.js → run-attempt-VPVJoYDP.js} +7897 -7136
- package/dist/runtime-artifact-C2ITXdhM.js +488 -0
- package/dist/{sandbox-guard-DA2TQfZW.js → sandbox-guard-D1Wky__S.js} +10 -1
- package/dist/{session-binding-C1ZXdP-x.js → session-binding-BMfX1OX-.js} +247 -88
- package/dist/session-catalog-7H112Tr_.js +2396 -0
- package/dist/session-cli-B28RhCyp.js +175 -0
- package/dist/{shared-client-4ICy3U6d.js → shared-client-D4mFI9al.js} +1997 -1297
- package/dist/{side-question-N4OaDer2.js → side-question-CgJBz52s.js} +135 -67
- package/dist/{thread-lifecycle-qWE88Dn2.js → thread-lifecycle-BgLXzjvV.js} +747 -317
- package/dist/transcript-mirror-DhLwFIL4.js +485 -0
- package/dist/transport-stdio-DXgGZ7Ib.js +88 -0
- package/dist/{web-search-provider.runtime-BSlriav6.js → web-search-provider.runtime-Bs-eTn5U.js} +3 -3
- package/npm-shrinkwrap.json +56 -30
- package/openclaw.plugin.json +168 -10
- package/package.json +8 -6
- package/dist/command-rpc-eLM_mtC7.js +0 -784
- package/dist/computer-use-Bmaz333N.js +0 -377
- package/dist/notification-correlation-Bo7KB3ks.js +0 -35
- package/dist/plugin-app-cache-key-BrhVdeEf.js +0 -98
- package/dist/request-BcJyl8KL.js +0 -36
package/dist/{media-understanding-provider-BHOu9-Gg.js → media-understanding-provider-D580L0P8.js}
RENAMED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { CODEX_PROVIDER_ID, FALLBACK_CODEX_MODELS } from "./provider-catalog.js";
|
|
2
|
-
import {
|
|
3
|
-
import { _ as readCodexNotificationItem } from "./attempt-notifications-
|
|
4
|
-
import {
|
|
2
|
+
import { i as isJsonObject } from "./protocol-BMifTfdW.js";
|
|
3
|
+
import { _ as readCodexNotificationItem } from "./attempt-notifications-DEv9h7iC.js";
|
|
4
|
+
import { f as resolveCodexAppServerRuntimeOptions } from "./config-c48K5HP9.js";
|
|
5
5
|
import { i as assertCodexTurnStartResponse, l as readCodexTurnCompletedNotification, o as readCodexErrorNotification, r as assertCodexThreadStartResponse } from "./protocol-validators-dZQ-UTOa.js";
|
|
6
|
-
import { i as readModelListResult } from "./models-
|
|
7
|
-
import {
|
|
6
|
+
import { i as readModelListResult } from "./models-DuKzZA6G.js";
|
|
7
|
+
import { o as isCodexAppServerStartSelectionChangedError } from "./shared-client-D4mFI9al.js";
|
|
8
|
+
import { W as mergeCodexThreadConfigs, r as buildCodexRuntimeThreadConfig } from "./thread-lifecycle-BgLXzjvV.js";
|
|
8
9
|
import { validateJsonSchemaValue } from "openclaw/plugin-sdk/json-schema-runtime";
|
|
9
10
|
import fs from "node:fs/promises";
|
|
10
11
|
import path from "node:path";
|
|
@@ -30,7 +31,10 @@ const CODEX_PRIVATE_BOUNDED_THREAD_CONFIG = {
|
|
|
30
31
|
notify: []
|
|
31
32
|
};
|
|
32
33
|
async function runBoundedCodexAppServerTurn(params) {
|
|
33
|
-
const appServer = resolveCodexAppServerRuntimeOptions({
|
|
34
|
+
const appServer = resolveCodexAppServerRuntimeOptions({
|
|
35
|
+
pluginConfig: params.options.pluginConfig,
|
|
36
|
+
managedCommandOrder: params.isolation === "private-stdio" ? "package-first" : void 0
|
|
37
|
+
});
|
|
34
38
|
if (params.isolation === "configured-transport") return await runBoundedCodexAppServerTurnInWorkspace(params, appServer, { cwd: params.agentDir?.trim() || process.cwd() });
|
|
35
39
|
if (appServer.start.transport !== "stdio") throw new Error("Bounded Codex turns require stdio transport so native tools can be isolated.");
|
|
36
40
|
return await withTempWorkspace({
|
|
@@ -46,8 +50,11 @@ async function runBoundedCodexAppServerTurn(params) {
|
|
|
46
50
|
});
|
|
47
51
|
});
|
|
48
52
|
}
|
|
49
|
-
async function runBoundedCodexAppServerTurnInWorkspace(params, appServer, workspace) {
|
|
50
|
-
const
|
|
53
|
+
async function runBoundedCodexAppServerTurnInWorkspace(params, appServer, workspace, selectionAttempt = 0, timing) {
|
|
54
|
+
const totalTimeoutMs = timing?.timeoutMs ?? resolveTimerTimeoutMs(params.timeoutMs, 100, 100);
|
|
55
|
+
const deadline = timing?.deadline ?? Date.now() + totalTimeoutMs;
|
|
56
|
+
const timeoutMs = deadline - Date.now();
|
|
57
|
+
if (timeoutMs <= 0) throw new Error(`${params.taskLabel} timed out`);
|
|
51
58
|
const agentDir = params.agentDir?.trim() || void 0;
|
|
52
59
|
const startOptions = workspace.codexHome ? buildPrivateCodexAppServerStartOptions(appServer.start, workspace.codexHome) : appServer.start;
|
|
53
60
|
const ownsClient = !params.options.clientFactory;
|
|
@@ -57,7 +64,7 @@ async function runBoundedCodexAppServerTurnInWorkspace(params, appServer, worksp
|
|
|
57
64
|
agentDir,
|
|
58
65
|
config: params.config,
|
|
59
66
|
timeoutMs
|
|
60
|
-
}) : await import("./shared-client-
|
|
67
|
+
}) : await import("./shared-client-D4mFI9al.js").then((n) => n.f).then(({ createIsolatedCodexAppServerClient }) => createIsolatedCodexAppServerClient({
|
|
61
68
|
startOptions,
|
|
62
69
|
timeoutMs,
|
|
63
70
|
authProfileId: params.profile,
|
|
@@ -69,8 +76,11 @@ async function runBoundedCodexAppServerTurnInWorkspace(params, appServer, worksp
|
|
|
69
76
|
const abortFromCaller = () => abortController.abort(params.signal?.reason ?? "aborted");
|
|
70
77
|
if (params.signal?.aborted) abortFromCaller();
|
|
71
78
|
else params.signal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
72
|
-
const
|
|
79
|
+
const remainingRunMs = deadline - Date.now();
|
|
80
|
+
if (remainingRunMs <= 0) abortController.abort("timeout");
|
|
81
|
+
const timeout = setTimeout(() => abortController.abort("timeout"), Math.max(1, remainingRunMs));
|
|
73
82
|
timeout.unref?.();
|
|
83
|
+
let retrySelection = false;
|
|
74
84
|
try {
|
|
75
85
|
const model = await resolveCodexBoundedTurnModel({
|
|
76
86
|
client,
|
|
@@ -122,11 +132,19 @@ async function runBoundedCodexAppServerTurnInWorkspace(params, appServer, worksp
|
|
|
122
132
|
requestCleanup();
|
|
123
133
|
cleanup();
|
|
124
134
|
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (ownsClient && isCodexAppServerStartSelectionChangedError(error) && selectionAttempt === 0) retrySelection = true;
|
|
137
|
+
else throw error;
|
|
125
138
|
} finally {
|
|
126
139
|
clearTimeout(timeout);
|
|
127
140
|
params.signal?.removeEventListener("abort", abortFromCaller);
|
|
128
141
|
if (ownsClient) client.close();
|
|
129
142
|
}
|
|
143
|
+
if (retrySelection) return await runBoundedCodexAppServerTurnInWorkspace(params, appServer, workspace, selectionAttempt + 1, {
|
|
144
|
+
deadline,
|
|
145
|
+
timeoutMs: totalTimeoutMs
|
|
146
|
+
});
|
|
147
|
+
throw new Error("Codex bounded turn selection retry exited unexpectedly");
|
|
130
148
|
}
|
|
131
149
|
function resolveBoundedThreadConfig(params, workspace) {
|
|
132
150
|
const boundedConfig = mergeCodexThreadConfigs(CODEX_BOUNDED_THREAD_CONFIG, params.threadConfig) ?? CODEX_BOUNDED_THREAD_CONFIG;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as buildCodexMediaUnderstandingProvider } from "./media-understanding-provider-
|
|
1
|
+
import { t as buildCodexMediaUnderstandingProvider } from "./media-understanding-provider-D580L0P8.js";
|
|
2
2
|
export { buildCodexMediaUnderstandingProvider };
|
|
@@ -46,7 +46,7 @@ async function listAllCodexAppServerModels(options = {}) {
|
|
|
46
46
|
async function withCodexAppServerModelClient(options, run) {
|
|
47
47
|
const timeoutMs = options.timeoutMs ?? 2500;
|
|
48
48
|
const useSharedClient = options.sharedClient !== false;
|
|
49
|
-
const { createIsolatedCodexAppServerClient, getLeasedSharedCodexAppServerClient, releaseLeasedSharedCodexAppServerClient } = await import("./shared-client-
|
|
49
|
+
const { createIsolatedCodexAppServerClient, getLeasedSharedCodexAppServerClient, releaseLeasedSharedCodexAppServerClient } = await import("./shared-client-D4mFI9al.js").then((n) => n.f);
|
|
50
50
|
const client = useSharedClient ? await getLeasedSharedCodexAppServerClient({
|
|
51
51
|
startOptions: options.startOptions,
|
|
52
52
|
timeoutMs,
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { i as isJsonObject } from "./protocol-BMifTfdW.js";
|
|
2
|
+
import { d as readCodexPluginConfig, f as resolveCodexAppServerRuntimeOptions, v as resolveCodexSupervisionAppServerRuntimeOptions } from "./config-c48K5HP9.js";
|
|
3
|
+
import { t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-BKNjHMEs.js";
|
|
4
|
+
//#region extensions/codex/src/app-server/binding-connection.ts
|
|
5
|
+
/** Requires the native model pair after a supervised pending branch has materialized. */
|
|
6
|
+
function requireCodexSupervisionModelSelection(binding) {
|
|
7
|
+
const model = binding.model?.trim();
|
|
8
|
+
const modelProvider = binding.modelProvider?.trim();
|
|
9
|
+
if (binding.connectionScope !== "supervision" || !model || !modelProvider) throw new Error("Codex supervised binding is missing its native model and provider; refusing request selection");
|
|
10
|
+
return {
|
|
11
|
+
model,
|
|
12
|
+
modelProvider
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** Resolves connection and auth ownership exclusively from the private thread binding. */
|
|
16
|
+
function resolveCodexBindingAppServerConnection(params) {
|
|
17
|
+
const { binding, authProfileId, ...runtimeParams } = params;
|
|
18
|
+
const usesSupervisionConnection = binding?.connectionScope === "supervision";
|
|
19
|
+
if (usesSupervisionConnection && readCodexPluginConfig(runtimeParams.pluginConfig).supervision?.enabled !== true) throw new Error("Codex supervision is disabled; refusing to open a native user-home supervised session");
|
|
20
|
+
const appServer = (usesSupervisionConnection ? resolveCodexSupervisionAppServerRuntimeOptions : resolveCodexAppServerRuntimeOptions)(runtimeParams);
|
|
21
|
+
if (usesSupervisionConnection) {
|
|
22
|
+
const persistedFingerprint = binding.pendingSupervisionBranch?.connectionFingerprint ?? binding.appServerRuntimeFingerprint;
|
|
23
|
+
const currentFingerprint = buildCodexAppServerConnectionFingerprint(appServer, runtimeParams.agentDir);
|
|
24
|
+
if (!persistedFingerprint || persistedFingerprint !== currentFingerprint) throw new Error("Codex supervision connection changed; refusing to operate on its bound native thread");
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
appServer,
|
|
28
|
+
usesSupervisionConnection,
|
|
29
|
+
requestAuthProfileId: usesSupervisionConnection ? void 0 : authProfileId,
|
|
30
|
+
clientAuthProfileId: usesSupervisionConnection ? null : authProfileId
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region extensions/codex/src/app-server/notification-correlation.ts
|
|
35
|
+
/**
|
|
36
|
+
* Correlates Codex app-server notifications with the active thread/turn so
|
|
37
|
+
* projectors can ignore global or stale events without losing diagnostics.
|
|
38
|
+
*/
|
|
39
|
+
/** Returns true when a notification payload belongs to the exact active thread and turn. */
|
|
40
|
+
function isCodexNotificationForTurn(value, threadId, turnId) {
|
|
41
|
+
if (!isJsonObject(value)) return false;
|
|
42
|
+
return readCodexNotificationThreadId(value) === threadId && readCodexNotificationTurnId(value) === turnId;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Reads a thread id from canonical top-level or nested thread payloads.
|
|
46
|
+
* The generated v2 schemas require top-level `threadId` on turn/item-scoped
|
|
47
|
+
* notifications and define `Turn` without one, so `turn.threadId` is not a
|
|
48
|
+
* wire shape and is deliberately not read here.
|
|
49
|
+
*/
|
|
50
|
+
function readCodexNotificationThreadId(record) {
|
|
51
|
+
const thread = isJsonObject(record.thread) ? record.thread : void 0;
|
|
52
|
+
return readString(record, "threadId") ?? (thread ? readString(thread, "id") : void 0);
|
|
53
|
+
}
|
|
54
|
+
/** Reads a turn id from either top-level notification params or nested turn payloads. */
|
|
55
|
+
function readCodexNotificationTurnId(record) {
|
|
56
|
+
return readNestedTurnId(record) ?? readString(record, "turnId");
|
|
57
|
+
}
|
|
58
|
+
function readNestedTurnId(record) {
|
|
59
|
+
const turn = record.turn;
|
|
60
|
+
return isJsonObject(turn) ? readString(turn, "id") : void 0;
|
|
61
|
+
}
|
|
62
|
+
function readString(record, key) {
|
|
63
|
+
const value = record[key];
|
|
64
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
export { resolveCodexBindingAppServerConnection as a, requireCodexSupervisionModelSelection as i, readCodexNotificationThreadId as n, readCodexNotificationTurnId as r, isCodexNotificationForTurn as t };
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { m as resolveCodexAppServerUserHomeDir } from "./config-c48K5HP9.js";
|
|
2
|
+
import { D as resolveCodexAppServerHomeDir } from "./shared-client-D4mFI9al.js";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { isFutureDateTimestampMs, resolveDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
5
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
8
|
+
import { OPENCLAW_VERSION, embeddedAgentLog } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
9
|
+
import { readPluginPackageVersion } from "openclaw/plugin-sdk/extension-shared";
|
|
10
|
+
//#region extensions/codex/src/app-server/app-inventory-cache.ts
|
|
11
|
+
/**
|
|
12
|
+
* Process-local cache for Codex app-server app inventories, keyed by runtime
|
|
13
|
+
* identity and safe to refresh in the background.
|
|
14
|
+
*/
|
|
15
|
+
/** Default app inventory cache freshness window. */
|
|
16
|
+
const CODEX_APP_INVENTORY_CACHE_TTL_MS = 3600 * 1e3;
|
|
17
|
+
const CODEX_TARGETED_APP_INVENTORY_LIMIT = 1e3;
|
|
18
|
+
const MAX_SERIALIZED_ERROR_MESSAGE_LENGTH = 500;
|
|
19
|
+
/** In-memory app inventory cache with coalesced refreshes per key. */
|
|
20
|
+
var CodexAppInventoryCache = class {
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
23
|
+
this.inFlight = /* @__PURE__ */ new Map();
|
|
24
|
+
this.refreshTokens = /* @__PURE__ */ new Map();
|
|
25
|
+
this.diagnostics = /* @__PURE__ */ new Map();
|
|
26
|
+
this.revision = 0;
|
|
27
|
+
this.ttlMs = options.ttlMs ?? CODEX_APP_INVENTORY_CACHE_TTL_MS;
|
|
28
|
+
}
|
|
29
|
+
/** Reads a snapshot and schedules refresh when missing, stale, or forced. */
|
|
30
|
+
read(params) {
|
|
31
|
+
const nowMs = resolveDateTimestampMs(params.nowMs);
|
|
32
|
+
const entry = this.entries.get(params.key);
|
|
33
|
+
if (!entry) {
|
|
34
|
+
const refreshScheduled = params.suppressRefresh ? false : this.scheduleRefresh(params);
|
|
35
|
+
return {
|
|
36
|
+
state: "missing",
|
|
37
|
+
key: params.key,
|
|
38
|
+
revision: this.revision,
|
|
39
|
+
refreshScheduled,
|
|
40
|
+
...this.diagnostics.get(params.key) ? { diagnostic: this.diagnostics.get(params.key) } : {}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const state = entry.invalidated || !isFutureDateTimestampMs(entry.expiresAtMs, { nowMs }) ? "stale" : "fresh";
|
|
44
|
+
const refreshScheduled = state === "fresh" && !params.forceRefetch ? false : this.scheduleRefresh(params);
|
|
45
|
+
return {
|
|
46
|
+
state,
|
|
47
|
+
key: params.key,
|
|
48
|
+
revision: entry.revision,
|
|
49
|
+
snapshot: stripEntryState(entry),
|
|
50
|
+
refreshScheduled,
|
|
51
|
+
...entry.lastError ? { diagnostic: entry.lastError } : {}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Forces or joins an immediate refresh for a cache key. */
|
|
55
|
+
refreshNow(params) {
|
|
56
|
+
return this.refresh(params);
|
|
57
|
+
}
|
|
58
|
+
/** Marks a key stale and records the reason as a diagnostic. */
|
|
59
|
+
invalidate(key, reason, nowMs = Date.now()) {
|
|
60
|
+
this.revision += 1;
|
|
61
|
+
const diagnostic = {
|
|
62
|
+
message: reason,
|
|
63
|
+
atMs: nowMs
|
|
64
|
+
};
|
|
65
|
+
const entry = this.entries.get(key);
|
|
66
|
+
if (entry) {
|
|
67
|
+
entry.invalidated = true;
|
|
68
|
+
entry.lastError = diagnostic;
|
|
69
|
+
entry.revision = this.revision;
|
|
70
|
+
} else this.diagnostics.set(key, diagnostic);
|
|
71
|
+
return this.revision;
|
|
72
|
+
}
|
|
73
|
+
/** Clears all cached snapshots, diagnostics, in-flight requests, and revision state. */
|
|
74
|
+
clear() {
|
|
75
|
+
this.entries.clear();
|
|
76
|
+
this.inFlight.clear();
|
|
77
|
+
this.refreshTokens.clear();
|
|
78
|
+
this.diagnostics.clear();
|
|
79
|
+
this.revision = 0;
|
|
80
|
+
}
|
|
81
|
+
/** Returns the monotonically increasing cache revision. */
|
|
82
|
+
getRevision() {
|
|
83
|
+
return this.revision;
|
|
84
|
+
}
|
|
85
|
+
scheduleRefresh(params) {
|
|
86
|
+
if (this.inFlight.has(params.key) && !params.forceRefetch) return true;
|
|
87
|
+
const promise = this.refresh(params);
|
|
88
|
+
this.inFlight.set(params.key, promise);
|
|
89
|
+
promise.catch(() => void 0);
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
async refresh(params) {
|
|
93
|
+
const existing = this.inFlight.get(params.key);
|
|
94
|
+
if (existing && !params.forceRefetch) return existing;
|
|
95
|
+
const refreshToken = (this.refreshTokens.get(params.key) ?? 0) + 1;
|
|
96
|
+
this.refreshTokens.set(params.key, refreshToken);
|
|
97
|
+
const promise = this.refreshUncoalesced(params, refreshToken);
|
|
98
|
+
this.inFlight.set(params.key, promise);
|
|
99
|
+
try {
|
|
100
|
+
return await promise;
|
|
101
|
+
} finally {
|
|
102
|
+
if (this.inFlight.get(params.key) === promise) this.inFlight.delete(params.key);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async refreshUncoalesced(params, refreshToken) {
|
|
106
|
+
const nowMs = resolveDateTimestampMs(params.nowMs);
|
|
107
|
+
try {
|
|
108
|
+
const apps = await listAllApps(params.request, params.forceRefetch ?? false, params.targetAppIds);
|
|
109
|
+
this.revision += 1;
|
|
110
|
+
const expiresAtMs = resolveExpiresAtMsFromDurationMs(this.ttlMs, { nowMs }) ?? 0;
|
|
111
|
+
const snapshot = {
|
|
112
|
+
key: params.key,
|
|
113
|
+
apps,
|
|
114
|
+
fetchedAtMs: nowMs,
|
|
115
|
+
expiresAtMs,
|
|
116
|
+
revision: this.revision
|
|
117
|
+
};
|
|
118
|
+
if (this.refreshTokens.get(params.key) === refreshToken) {
|
|
119
|
+
this.entries.set(params.key, {
|
|
120
|
+
...snapshot,
|
|
121
|
+
invalidated: false
|
|
122
|
+
});
|
|
123
|
+
this.diagnostics.delete(params.key);
|
|
124
|
+
}
|
|
125
|
+
return snapshot;
|
|
126
|
+
} catch (error) {
|
|
127
|
+
const diagnostic = {
|
|
128
|
+
message: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
|
|
129
|
+
atMs: nowMs
|
|
130
|
+
};
|
|
131
|
+
this.diagnostics.set(params.key, diagnostic);
|
|
132
|
+
const entry = this.entries.get(params.key);
|
|
133
|
+
if (entry) entry.lastError = diagnostic;
|
|
134
|
+
embeddedAgentLog.warn("codex app inventory refresh failed", {
|
|
135
|
+
forceRefetch: params.forceRefetch === true,
|
|
136
|
+
keyFingerprint: fingerprintInventoryCacheKey(params.key),
|
|
137
|
+
error: serializeCodexAppInventoryError(error)
|
|
138
|
+
});
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
/** Serializes a refresh failure without leaking large or sensitive error data. */
|
|
144
|
+
function serializeCodexAppInventoryError(error) {
|
|
145
|
+
const record = isRecord(error) ? error : void 0;
|
|
146
|
+
const data = record && "data" in record ? redactErrorData(record.data) : void 0;
|
|
147
|
+
return {
|
|
148
|
+
name: error instanceof Error ? error.name : typeof record?.name === "string" ? record.name : void 0,
|
|
149
|
+
message: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
|
|
150
|
+
...typeof record?.code === "number" ? { code: record.code } : {},
|
|
151
|
+
...data !== void 0 ? { data } : {}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/** Shared app inventory cache used by Codex app-server runtime paths. */
|
|
155
|
+
const defaultCodexAppInventoryCache = new CodexAppInventoryCache();
|
|
156
|
+
/** Builds a stable cache key from build versions and runtime identity fields. */
|
|
157
|
+
function buildCodexAppInventoryCacheKey(input, openClawVersion, codexPluginVersion) {
|
|
158
|
+
return JSON.stringify({
|
|
159
|
+
openClawVersion,
|
|
160
|
+
codexPluginVersion,
|
|
161
|
+
codexHome: input.codexHome ?? null,
|
|
162
|
+
endpoint: input.endpoint ?? null,
|
|
163
|
+
runtimeIdentity: normalizeRuntimeIdentityForCacheKey(input.runtimeIdentity),
|
|
164
|
+
authProfileId: input.authProfileId ?? null,
|
|
165
|
+
accountId: input.accountId ?? null,
|
|
166
|
+
envApiKeyFingerprint: input.envApiKeyFingerprint ?? null,
|
|
167
|
+
appServerVersion: input.appServerVersion ?? null
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function normalizeRuntimeIdentityForCacheKey(value) {
|
|
171
|
+
if (!value) return null;
|
|
172
|
+
const entries = Object.entries(value).flatMap(([key, rawValue]) => {
|
|
173
|
+
const normalized = rawValue?.trim();
|
|
174
|
+
return normalized ? [[key, normalized]] : [];
|
|
175
|
+
}).toSorted(([left], [right]) => left.localeCompare(right));
|
|
176
|
+
return entries.length > 0 ? Object.fromEntries(entries) : null;
|
|
177
|
+
}
|
|
178
|
+
async function listAllApps(request, forceRefetch, targetAppIds = []) {
|
|
179
|
+
const apps = [];
|
|
180
|
+
const targetIds = new Set(targetAppIds.filter(Boolean));
|
|
181
|
+
const remainingTargetIds = new Set(targetIds);
|
|
182
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
183
|
+
let cursor;
|
|
184
|
+
do {
|
|
185
|
+
const response = await request("app/list", {
|
|
186
|
+
cursor,
|
|
187
|
+
limit: targetIds.size > 0 ? CODEX_TARGETED_APP_INVENTORY_LIMIT : 100,
|
|
188
|
+
forceRefetch
|
|
189
|
+
});
|
|
190
|
+
apps.push(...response.data);
|
|
191
|
+
for (const app of response.data) remainingTargetIds.delete(app.id);
|
|
192
|
+
if (targetIds.size > 0 && remainingTargetIds.size === 0) break;
|
|
193
|
+
cursor = response.nextCursor;
|
|
194
|
+
if (cursor && seenCursors.has(cursor)) throw new Error(`app/list returned repeated cursor ${cursor}`);
|
|
195
|
+
if (cursor) seenCursors.add(cursor);
|
|
196
|
+
} while (cursor);
|
|
197
|
+
return apps;
|
|
198
|
+
}
|
|
199
|
+
function stripEntryState(entry) {
|
|
200
|
+
const { invalidated: _invalidated, ...snapshot } = entry;
|
|
201
|
+
return snapshot;
|
|
202
|
+
}
|
|
203
|
+
function fingerprintInventoryCacheKey(key) {
|
|
204
|
+
let hash = 0;
|
|
205
|
+
for (let index = 0; index < key.length; index += 1) hash = hash * 31 + key.charCodeAt(index) >>> 0;
|
|
206
|
+
return hash.toString(16).padStart(8, "0");
|
|
207
|
+
}
|
|
208
|
+
function truncateSerializedErrorText(value) {
|
|
209
|
+
return value.length > MAX_SERIALIZED_ERROR_MESSAGE_LENGTH ? `${truncateUtf16Safe(value, MAX_SERIALIZED_ERROR_MESSAGE_LENGTH)}...` : value;
|
|
210
|
+
}
|
|
211
|
+
function redactErrorData(value, depth = 0) {
|
|
212
|
+
if (value === void 0) return;
|
|
213
|
+
if (value === null || typeof value === "boolean" || typeof value === "number") return value;
|
|
214
|
+
if (depth > 6) return "[truncated]";
|
|
215
|
+
if (Array.isArray(value)) return value.map((entry) => redactErrorData(entry, depth + 1) ?? null);
|
|
216
|
+
if (isRecord(value)) {
|
|
217
|
+
const redacted = {};
|
|
218
|
+
for (const [key, entry] of Object.entries(value)) redacted[key] = isSensitiveErrorDataKey(key) ? "<redacted>" : redactErrorData(entry, depth + 1) ?? null;
|
|
219
|
+
return redacted;
|
|
220
|
+
}
|
|
221
|
+
if (typeof value === "string") return truncateSerializedErrorText(value);
|
|
222
|
+
if (typeof value === "bigint") return value.toString();
|
|
223
|
+
if (typeof value === "symbol") return value.description ? `Symbol(${value.description})` : "Symbol()";
|
|
224
|
+
if (typeof value === "function") return value.name ? `[function ${value.name}]` : "[function]";
|
|
225
|
+
return "[unserializable]";
|
|
226
|
+
}
|
|
227
|
+
function sanitizeErrorMessage(message) {
|
|
228
|
+
const htmlStart = message.search(/<html[\s>]/i);
|
|
229
|
+
return truncateSerializedErrorText((htmlStart >= 0 ? `${message.slice(0, htmlStart).trimEnd()} [HTML response body omitted]` : message).replace(/([?&][^=\s"'<>]*(?:api[_-]?key|authorization|cookie|credential|password|secret|token|tk)[^=\s"'<>]*=)[^&\s"'<>]+/gi, "$1<redacted>"));
|
|
230
|
+
}
|
|
231
|
+
function isSensitiveErrorDataKey(key) {
|
|
232
|
+
return /api[_-]?key|authorization|cookie|credential|password|secret|token/i.test(key);
|
|
233
|
+
}
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region extensions/codex/src/app-server/plugin-app-cache-key.ts
|
|
236
|
+
/**
|
|
237
|
+
* Builds stable Codex plugin/app inventory cache keys from app-server startup,
|
|
238
|
+
* auth, account, and version inputs without storing secret material.
|
|
239
|
+
*/
|
|
240
|
+
const CODEX_PLUGIN_VERSION = readPluginPackageVersion({ require: createRequire(import.meta.url) });
|
|
241
|
+
/** Builds the full app inventory cache key for Codex plugin/app discovery. */
|
|
242
|
+
function buildCodexPluginAppCacheKey(params) {
|
|
243
|
+
return buildCodexAppInventoryCacheKey({
|
|
244
|
+
codexHome: params.runtimeIdentity?.codexHome ?? resolveCodexPluginAppCacheCodexHome(params.appServer, params.agentDir),
|
|
245
|
+
endpoint: resolveCodexPluginAppCacheEndpoint(params.appServer),
|
|
246
|
+
authProfileId: params.authProfileId,
|
|
247
|
+
accountId: params.accountId,
|
|
248
|
+
envApiKeyFingerprint: params.envApiKeyFingerprint,
|
|
249
|
+
appServerVersion: params.appServerVersion ?? params.runtimeIdentity?.serverVersion,
|
|
250
|
+
runtimeIdentity: params.runtimeIdentity
|
|
251
|
+
}, OPENCLAW_VERSION, CODEX_PLUGIN_VERSION);
|
|
252
|
+
}
|
|
253
|
+
/** Builds a durable thread-binding fingerprint for one initialized app-server runtime. */
|
|
254
|
+
function buildCodexAppServerRuntimeFingerprint(params) {
|
|
255
|
+
return JSON.stringify({
|
|
256
|
+
endpoint: resolveCodexPluginAppCacheEndpoint(params.appServer),
|
|
257
|
+
connectionClass: params.appServer.connectionClass,
|
|
258
|
+
remoteWorkspaceRoot: params.appServer.remoteWorkspaceRoot ?? null,
|
|
259
|
+
appServerVersion: params.appServerVersion ?? params.runtimeIdentity?.serverVersion ?? null,
|
|
260
|
+
runtimeIdentity: params.runtimeIdentity ?? null
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/** Fingerprints the configured connection that owns a supervised source thread. */
|
|
264
|
+
function buildCodexAppServerConnectionFingerprint(appServer, agentDir) {
|
|
265
|
+
return JSON.stringify({
|
|
266
|
+
endpoint: resolveCodexPluginAppCacheEndpoint(appServer),
|
|
267
|
+
connectionClass: appServer.connectionClass,
|
|
268
|
+
remoteWorkspaceRoot: appServer.remoteWorkspaceRoot ?? null,
|
|
269
|
+
homeScope: appServer.start.homeScope ?? null,
|
|
270
|
+
codexHome: resolveCodexAppServerConnectionHome(appServer.start, agentDir),
|
|
271
|
+
cwd: appServer.start.cwd ?? null
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
function resolveCodexAppServerConnectionHome(start, agentDir) {
|
|
275
|
+
const configured = start.env?.CODEX_HOME?.trim();
|
|
276
|
+
if (configured) return configured;
|
|
277
|
+
if (start.transport === "unix" && (!start.url || start.url === "unix://")) return resolveCodexAppServerUserHomeDir(start.env ?? process.env);
|
|
278
|
+
if (start.transport !== "stdio") return null;
|
|
279
|
+
if (start.homeScope === "user") return resolveCodexAppServerUserHomeDir(process.env);
|
|
280
|
+
return agentDir ? resolveCodexAppServerHomeDir(agentDir) : null;
|
|
281
|
+
}
|
|
282
|
+
/** Serializes app-server endpoint identity, including credential fingerprints. */
|
|
283
|
+
function resolveCodexPluginAppCacheEndpoint(appServer) {
|
|
284
|
+
return JSON.stringify({
|
|
285
|
+
transport: appServer.start.transport,
|
|
286
|
+
command: appServer.start.command,
|
|
287
|
+
args: appServer.start.args,
|
|
288
|
+
url: appServer.start.url ?? null,
|
|
289
|
+
credentialFingerprint: fingerprintCodexPluginAppCacheCredentials(appServer.start)
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/** Resolves the CODEX_HOME value that scopes local app-server inventory. */
|
|
293
|
+
function resolveCodexPluginAppCacheCodexHome(appServer, agentDir) {
|
|
294
|
+
const configuredCodexHome = appServer.start.env?.CODEX_HOME?.trim();
|
|
295
|
+
if (configuredCodexHome) return configuredCodexHome;
|
|
296
|
+
return appServer.start.transport === "stdio" && agentDir ? resolveCodexAppServerHomeDir(agentDir) : void 0;
|
|
297
|
+
}
|
|
298
|
+
function fingerprintCodexPluginAppCacheCredentials(startOptions) {
|
|
299
|
+
const authToken = startOptions.authToken ?? "";
|
|
300
|
+
const headers = Object.entries(startOptions.headers).map(([key, value]) => [key.toLowerCase(), value]).toSorted(([left], [right]) => left.localeCompare(right));
|
|
301
|
+
if (!authToken && headers.length === 0) return null;
|
|
302
|
+
const hash = createHash("sha256");
|
|
303
|
+
hash.update("openclaw:codex:plugin-app-cache-credentials:v1");
|
|
304
|
+
hash.update("\0");
|
|
305
|
+
hash.update(authToken);
|
|
306
|
+
for (const [key, value] of headers) {
|
|
307
|
+
hash.update("\0");
|
|
308
|
+
hash.update(key);
|
|
309
|
+
hash.update("\0");
|
|
310
|
+
hash.update(value);
|
|
311
|
+
}
|
|
312
|
+
return `sha256:${hash.digest("hex")}`;
|
|
313
|
+
}
|
|
314
|
+
//#endregion
|
|
315
|
+
export { serializeCodexAppInventoryError as a, defaultCodexAppInventoryCache as i, buildCodexAppServerRuntimeFingerprint as n, buildCodexPluginAppCacheKey as r, buildCodexAppServerConnectionFingerprint as t };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
//#region extensions/codex/src/app-server/protocol.ts
|
|
2
|
+
/** Namespace Codex keeps directly model-visible without exposing it to Code Mode guests. */
|
|
3
|
+
const CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE = "openclaw_direct";
|
|
2
4
|
function flattenCodexDynamicToolFunctions(tools) {
|
|
3
5
|
return (tools ?? []).flatMap((tool) => tool.type === "namespace" ? tool.tools : [tool]);
|
|
4
6
|
}
|
|
@@ -10,4 +12,4 @@ function isRpcResponse(message) {
|
|
|
10
12
|
return "id" in message && !("method" in message);
|
|
11
13
|
}
|
|
12
14
|
//#endregion
|
|
13
|
-
export { isRpcResponse as i,
|
|
15
|
+
export { isRpcResponse as a, isJsonObject as i, CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE as n, flattenCodexDynamicToolFunctions as r, CODEX_INTERACTIVE_THREAD_SOURCE_KINDS as t };
|
package/dist/provider-catalog.js
CHANGED
|
@@ -7,33 +7,72 @@ const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
|
|
|
7
7
|
const CODEX_APP_SERVER_AUTH_MARKER = "codex-app-server";
|
|
8
8
|
const DEFAULT_CONTEXT_WINDOW = 272e3;
|
|
9
9
|
const DEFAULT_MAX_TOKENS = 128e3;
|
|
10
|
+
const KNOWN_CONTEXT_WINDOW_BY_MODEL_ID = Object.freeze({
|
|
11
|
+
"gpt-5.6-sol": 372e3,
|
|
12
|
+
"gpt-5.6-terra": 372e3,
|
|
13
|
+
"gpt-5.6-luna": 372e3
|
|
14
|
+
});
|
|
10
15
|
/** Offline fallback catalog used when live app-server discovery is unavailable. */
|
|
11
|
-
const FALLBACK_CODEX_MODELS = [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
16
|
+
const FALLBACK_CODEX_MODELS = [
|
|
17
|
+
{
|
|
18
|
+
id: "gpt-5.6-sol",
|
|
19
|
+
model: "gpt-5.6-sol",
|
|
20
|
+
displayName: "GPT-5.6 Sol",
|
|
21
|
+
description: "Latest frontier agentic coding model.",
|
|
22
|
+
isDefault: true,
|
|
23
|
+
contextWindow: 372e3,
|
|
24
|
+
inputModalities: ["text", "image"],
|
|
25
|
+
supportedReasoningEfforts: [
|
|
26
|
+
"low",
|
|
27
|
+
"medium",
|
|
28
|
+
"high",
|
|
29
|
+
"xhigh",
|
|
30
|
+
"max",
|
|
31
|
+
"ultra"
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "gpt-5.6-luna",
|
|
36
|
+
model: "gpt-5.6-luna",
|
|
37
|
+
displayName: "GPT-5.6 Luna",
|
|
38
|
+
description: "High-throughput frontier agentic coding model.",
|
|
39
|
+
contextWindow: 372e3,
|
|
40
|
+
inputModalities: ["text", "image"],
|
|
41
|
+
supportedReasoningEfforts: [
|
|
42
|
+
"low",
|
|
43
|
+
"medium",
|
|
44
|
+
"high",
|
|
45
|
+
"xhigh",
|
|
46
|
+
"max"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "gpt-5.5",
|
|
51
|
+
model: "gpt-5.5",
|
|
52
|
+
displayName: "GPT-5.5",
|
|
53
|
+
description: "Previous frontier agentic coding model.",
|
|
54
|
+
inputModalities: ["text", "image"],
|
|
55
|
+
supportedReasoningEfforts: [
|
|
56
|
+
"low",
|
|
57
|
+
"medium",
|
|
58
|
+
"high",
|
|
59
|
+
"xhigh"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: "gpt-5.4-mini",
|
|
64
|
+
model: "gpt-5.4-mini",
|
|
65
|
+
displayName: "GPT-5.4-Mini",
|
|
66
|
+
description: "Smaller frontier agentic coding model.",
|
|
67
|
+
inputModalities: ["text", "image"],
|
|
68
|
+
supportedReasoningEfforts: [
|
|
69
|
+
"low",
|
|
70
|
+
"medium",
|
|
71
|
+
"high",
|
|
72
|
+
"xhigh"
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
];
|
|
37
76
|
/**
|
|
38
77
|
* Converts a Codex app-server model record into OpenClaw provider model config.
|
|
39
78
|
*/
|
|
@@ -52,7 +91,7 @@ function buildCodexModelDefinition(model) {
|
|
|
52
91
|
cacheRead: 0,
|
|
53
92
|
cacheWrite: 0
|
|
54
93
|
},
|
|
55
|
-
contextWindow: DEFAULT_CONTEXT_WINDOW,
|
|
94
|
+
contextWindow: model.contextWindow ?? KNOWN_CONTEXT_WINDOW_BY_MODEL_ID[id] ?? DEFAULT_CONTEXT_WINDOW,
|
|
56
95
|
maxTokens: DEFAULT_MAX_TOKENS,
|
|
57
96
|
compat: {
|
|
58
97
|
...supportedReasoningEfforts !== void 0 ? { supportsReasoningEffort: supportedReasoningEfforts.length > 0 } : {},
|