@openclaw/codex 2026.5.20-beta.2 → 2026.5.22
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/{client-Ab4Fw77C.js → client-D3SUwPFl.js} +14 -8
- package/dist/{client-factory-BbesECdh.js → client-factory-DdrIY1lw.js} +1 -1
- package/dist/{command-handlers-Gceg_rBZ.js → command-handlers-DsB0GYd2.js} +64 -7
- package/dist/{compact-BaSDurdH.js → compact-BwQn7jKq.js} +186 -40
- package/dist/{computer-use-B4iTog6z.js → computer-use-BMMwWwno.js} +2 -2
- package/dist/{config-DqIp6oTk.js → config-DDMrwfJl.js} +21 -2
- package/dist/dynamic-tools-DA42no4X.js +497 -0
- package/dist/harness.js +15 -5
- package/dist/index.js +10 -9
- package/dist/media-understanding-provider.js +6 -3
- package/dist/{models-C8MdOXGD.js → models-B_uo1pf5.js} +1 -1
- package/dist/{node-cli-sessions-T1olB1SH.js → node-cli-sessions-yVCifOyG.js} +23 -9
- package/dist/{command-formatters-BVBnEgyA.js → notification-correlation-qKY_sgga.js} +66 -15
- package/dist/provider.js +3 -3
- package/dist/{request-BePR77QD.js → request-NklFaHM4.js} +12 -3
- package/dist/{run-attempt-CRHVB240.js → run-attempt-DzlLXP5Y.js} +3750 -1409
- package/dist/sandbox-guard-CTnEWuor.js +233 -0
- package/dist/{session-binding-DEiYHgJ_.js → session-binding-Bw_mfIW2.js} +4 -2
- package/dist/{shared-client-DCxJx5QU.js → shared-client-C_RbGxW8.js} +2 -2
- package/dist/{side-question-DlJAUYst.js → side-question-DzP1wClA.js} +20 -15
- package/dist/test-api.js +3 -2
- package/dist/{thread-lifecycle-Cgi62SSl.js → thread-lifecycle-4Ul7RoW4.js} +606 -589
- package/dist/{vision-tools-B2wt6ecs.js → vision-tools-DOnxzH2y.js} +7 -3
- package/npm-shrinkwrap.json +1934 -0
- package/openclaw.plugin.json +29 -0
- package/package.json +8 -6
- package/dist/plugin-activation-BkQkPLOY.js +0 -452
package/openclaw.plugin.json
CHANGED
|
@@ -175,6 +175,10 @@
|
|
|
175
175
|
"minimum": 1,
|
|
176
176
|
"default": 60000
|
|
177
177
|
},
|
|
178
|
+
"postToolRawAssistantCompletionIdleTimeoutMs": {
|
|
179
|
+
"type": "number",
|
|
180
|
+
"minimum": 1
|
|
181
|
+
},
|
|
178
182
|
"approvalPolicy": {
|
|
179
183
|
"type": "string",
|
|
180
184
|
"enum": ["never", "on-request", "on-failure", "untrusted"]
|
|
@@ -190,6 +194,16 @@
|
|
|
190
194
|
"serviceTier": { "type": ["string", "null"] },
|
|
191
195
|
"defaultWorkspaceDir": {
|
|
192
196
|
"type": "string"
|
|
197
|
+
},
|
|
198
|
+
"experimental": {
|
|
199
|
+
"type": "object",
|
|
200
|
+
"additionalProperties": false,
|
|
201
|
+
"properties": {
|
|
202
|
+
"sandboxExecServer": {
|
|
203
|
+
"type": "boolean",
|
|
204
|
+
"default": false
|
|
205
|
+
}
|
|
206
|
+
}
|
|
193
207
|
}
|
|
194
208
|
}
|
|
195
209
|
}
|
|
@@ -345,6 +359,11 @@
|
|
|
345
359
|
"help": "Maximum quiet time after Codex accepts a turn or after a turn-scoped app-server request before OpenClaw interrupts the turn while waiting for turn/completed.",
|
|
346
360
|
"advanced": true
|
|
347
361
|
},
|
|
362
|
+
"appServer.postToolRawAssistantCompletionIdleTimeoutMs": {
|
|
363
|
+
"label": "Post-Tool Raw Assistant Completion Idle Timeout",
|
|
364
|
+
"help": "Completion-idle guard after a tool handoff when Codex emits raw assistant completion or progress without turn/completed. Defaults to the assistant completion idle timeout when unset.",
|
|
365
|
+
"advanced": true
|
|
366
|
+
},
|
|
348
367
|
"appServer.approvalPolicy": {
|
|
349
368
|
"label": "Approval Policy",
|
|
350
369
|
"help": "Codex native approval policy sent to thread start, resume, and turns.",
|
|
@@ -369,6 +388,16 @@
|
|
|
369
388
|
"label": "Default Workspace",
|
|
370
389
|
"help": "Workspace used by /codex bind when --cwd is omitted.",
|
|
371
390
|
"advanced": true
|
|
391
|
+
},
|
|
392
|
+
"appServer.experimental": {
|
|
393
|
+
"label": "Experimental",
|
|
394
|
+
"help": "Experimental Codex app-server integrations.",
|
|
395
|
+
"advanced": true
|
|
396
|
+
},
|
|
397
|
+
"appServer.experimental.sandboxExecServer": {
|
|
398
|
+
"label": "Sandbox Exec Server",
|
|
399
|
+
"help": "Route native Codex execution through an OpenClaw sandbox-backed exec-server when OpenClaw sandboxing is active.",
|
|
400
|
+
"advanced": true
|
|
372
401
|
}
|
|
373
402
|
}
|
|
374
403
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/codex",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.22",
|
|
4
4
|
"description": "OpenClaw Codex harness and model provider plugin",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,14 +27,15 @@
|
|
|
27
27
|
"minHostVersion": ">=2026.5.1-beta.1"
|
|
28
28
|
},
|
|
29
29
|
"compat": {
|
|
30
|
-
"pluginApi": ">=2026.5.
|
|
30
|
+
"pluginApi": ">=2026.5.22"
|
|
31
31
|
},
|
|
32
32
|
"build": {
|
|
33
|
-
"openclawVersion": "2026.5.
|
|
33
|
+
"openclawVersion": "2026.5.22"
|
|
34
34
|
},
|
|
35
35
|
"release": {
|
|
36
36
|
"publishToClawHub": true,
|
|
37
|
-
"publishToNpm": true
|
|
37
|
+
"publishToNpm": true,
|
|
38
|
+
"bundleRuntimeDependencies": false
|
|
38
39
|
},
|
|
39
40
|
"runtimeExtensions": [
|
|
40
41
|
"./dist/index.js"
|
|
@@ -42,10 +43,11 @@
|
|
|
42
43
|
},
|
|
43
44
|
"files": [
|
|
44
45
|
"dist/**",
|
|
45
|
-
"openclaw.plugin.json"
|
|
46
|
+
"openclaw.plugin.json",
|
|
47
|
+
"npm-shrinkwrap.json"
|
|
46
48
|
],
|
|
47
49
|
"peerDependencies": {
|
|
48
|
-
"openclaw": ">=2026.5.
|
|
50
|
+
"openclaw": ">=2026.5.22"
|
|
49
51
|
},
|
|
50
52
|
"peerDependenciesMeta": {
|
|
51
53
|
"openclaw": {
|
|
@@ -1,452 +0,0 @@
|
|
|
1
|
-
import { t as CODEX_PLUGINS_MARKETPLACE_NAME, u as resolveCodexPluginsPolicy } from "./config-DqIp6oTk.js";
|
|
2
|
-
import { embeddedAgentLog } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
3
|
-
import "node:fs/promises";
|
|
4
|
-
import "node:path";
|
|
5
|
-
const MAX_SERIALIZED_ERROR_MESSAGE_LENGTH = 500;
|
|
6
|
-
var CodexAppInventoryCache = class {
|
|
7
|
-
constructor(options = {}) {
|
|
8
|
-
this.entries = /* @__PURE__ */ new Map();
|
|
9
|
-
this.inFlight = /* @__PURE__ */ new Map();
|
|
10
|
-
this.refreshTokens = /* @__PURE__ */ new Map();
|
|
11
|
-
this.diagnostics = /* @__PURE__ */ new Map();
|
|
12
|
-
this.revision = 0;
|
|
13
|
-
this.ttlMs = options.ttlMs ?? 36e5;
|
|
14
|
-
}
|
|
15
|
-
read(params) {
|
|
16
|
-
const nowMs = params.nowMs ?? Date.now();
|
|
17
|
-
const entry = this.entries.get(params.key);
|
|
18
|
-
if (!entry) {
|
|
19
|
-
const refreshScheduled = params.suppressRefresh ? false : this.scheduleRefresh(params);
|
|
20
|
-
return {
|
|
21
|
-
state: "missing",
|
|
22
|
-
key: params.key,
|
|
23
|
-
revision: this.revision,
|
|
24
|
-
refreshScheduled,
|
|
25
|
-
...this.diagnostics.get(params.key) ? { diagnostic: this.diagnostics.get(params.key) } : {}
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
const state = entry.invalidated || entry.expiresAtMs <= nowMs ? "stale" : "fresh";
|
|
29
|
-
const refreshScheduled = state === "fresh" && !params.forceRefetch ? false : this.scheduleRefresh(params);
|
|
30
|
-
return {
|
|
31
|
-
state,
|
|
32
|
-
key: params.key,
|
|
33
|
-
revision: entry.revision,
|
|
34
|
-
snapshot: stripEntryState(entry),
|
|
35
|
-
refreshScheduled,
|
|
36
|
-
...entry.lastError ? { diagnostic: entry.lastError } : {}
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
refreshNow(params) {
|
|
40
|
-
return this.refresh(params);
|
|
41
|
-
}
|
|
42
|
-
invalidate(key, reason, nowMs = Date.now()) {
|
|
43
|
-
this.revision += 1;
|
|
44
|
-
const diagnostic = {
|
|
45
|
-
message: reason,
|
|
46
|
-
atMs: nowMs
|
|
47
|
-
};
|
|
48
|
-
const entry = this.entries.get(key);
|
|
49
|
-
if (entry) {
|
|
50
|
-
entry.invalidated = true;
|
|
51
|
-
entry.lastError = diagnostic;
|
|
52
|
-
entry.revision = this.revision;
|
|
53
|
-
} else this.diagnostics.set(key, diagnostic);
|
|
54
|
-
return this.revision;
|
|
55
|
-
}
|
|
56
|
-
clear() {
|
|
57
|
-
this.entries.clear();
|
|
58
|
-
this.inFlight.clear();
|
|
59
|
-
this.refreshTokens.clear();
|
|
60
|
-
this.diagnostics.clear();
|
|
61
|
-
this.revision = 0;
|
|
62
|
-
}
|
|
63
|
-
getRevision() {
|
|
64
|
-
return this.revision;
|
|
65
|
-
}
|
|
66
|
-
scheduleRefresh(params) {
|
|
67
|
-
if (this.inFlight.has(params.key) && !params.forceRefetch) return true;
|
|
68
|
-
const promise = this.refresh(params);
|
|
69
|
-
this.inFlight.set(params.key, promise);
|
|
70
|
-
promise.catch(() => void 0);
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
async refresh(params) {
|
|
74
|
-
const existing = this.inFlight.get(params.key);
|
|
75
|
-
if (existing && !params.forceRefetch) return existing;
|
|
76
|
-
const refreshToken = (this.refreshTokens.get(params.key) ?? 0) + 1;
|
|
77
|
-
this.refreshTokens.set(params.key, refreshToken);
|
|
78
|
-
const promise = this.refreshUncoalesced(params, refreshToken);
|
|
79
|
-
this.inFlight.set(params.key, promise);
|
|
80
|
-
try {
|
|
81
|
-
return await promise;
|
|
82
|
-
} finally {
|
|
83
|
-
if (this.inFlight.get(params.key) === promise) this.inFlight.delete(params.key);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
async refreshUncoalesced(params, refreshToken) {
|
|
87
|
-
const nowMs = params.nowMs ?? Date.now();
|
|
88
|
-
try {
|
|
89
|
-
const apps = await listAllApps(params.request, params.forceRefetch ?? false);
|
|
90
|
-
this.revision += 1;
|
|
91
|
-
const snapshot = {
|
|
92
|
-
key: params.key,
|
|
93
|
-
apps,
|
|
94
|
-
fetchedAtMs: nowMs,
|
|
95
|
-
expiresAtMs: nowMs + this.ttlMs,
|
|
96
|
-
revision: this.revision
|
|
97
|
-
};
|
|
98
|
-
if (this.refreshTokens.get(params.key) === refreshToken) {
|
|
99
|
-
this.entries.set(params.key, {
|
|
100
|
-
...snapshot,
|
|
101
|
-
invalidated: false
|
|
102
|
-
});
|
|
103
|
-
this.diagnostics.delete(params.key);
|
|
104
|
-
}
|
|
105
|
-
return snapshot;
|
|
106
|
-
} catch (error) {
|
|
107
|
-
const diagnostic = {
|
|
108
|
-
message: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
|
|
109
|
-
atMs: nowMs
|
|
110
|
-
};
|
|
111
|
-
this.diagnostics.set(params.key, diagnostic);
|
|
112
|
-
const entry = this.entries.get(params.key);
|
|
113
|
-
if (entry) entry.lastError = diagnostic;
|
|
114
|
-
embeddedAgentLog.warn("codex app inventory refresh failed", {
|
|
115
|
-
forceRefetch: params.forceRefetch === true,
|
|
116
|
-
keyFingerprint: fingerprintInventoryCacheKey(params.key),
|
|
117
|
-
error: serializeCodexAppInventoryError(error)
|
|
118
|
-
});
|
|
119
|
-
throw error;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
function serializeCodexAppInventoryError(error) {
|
|
124
|
-
const record = isRecord(error) ? error : void 0;
|
|
125
|
-
const data = record && "data" in record ? redactErrorData(record.data) : void 0;
|
|
126
|
-
return {
|
|
127
|
-
name: error instanceof Error ? error.name : typeof record?.name === "string" ? record.name : void 0,
|
|
128
|
-
message: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
|
|
129
|
-
...typeof record?.code === "number" ? { code: record.code } : {},
|
|
130
|
-
...data !== void 0 ? { data } : {}
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
const defaultCodexAppInventoryCache = new CodexAppInventoryCache();
|
|
134
|
-
function buildCodexAppInventoryCacheKey(input) {
|
|
135
|
-
return JSON.stringify({
|
|
136
|
-
codexHome: input.codexHome ?? null,
|
|
137
|
-
endpoint: input.endpoint ?? null,
|
|
138
|
-
authProfileId: input.authProfileId ?? null,
|
|
139
|
-
accountId: input.accountId ?? null,
|
|
140
|
-
envApiKeyFingerprint: input.envApiKeyFingerprint ?? null,
|
|
141
|
-
appServerVersion: input.appServerVersion ?? null
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
async function listAllApps(request, forceRefetch) {
|
|
145
|
-
const apps = [];
|
|
146
|
-
let cursor;
|
|
147
|
-
do {
|
|
148
|
-
const response = await request("app/list", {
|
|
149
|
-
cursor,
|
|
150
|
-
limit: 100,
|
|
151
|
-
forceRefetch
|
|
152
|
-
});
|
|
153
|
-
apps.push(...response.data);
|
|
154
|
-
cursor = response.nextCursor;
|
|
155
|
-
} while (cursor);
|
|
156
|
-
return apps;
|
|
157
|
-
}
|
|
158
|
-
function stripEntryState(entry) {
|
|
159
|
-
const { invalidated: _invalidated, ...snapshot } = entry;
|
|
160
|
-
return snapshot;
|
|
161
|
-
}
|
|
162
|
-
function fingerprintInventoryCacheKey(key) {
|
|
163
|
-
let hash = 0;
|
|
164
|
-
for (let index = 0; index < key.length; index += 1) hash = hash * 31 + key.charCodeAt(index) >>> 0;
|
|
165
|
-
return hash.toString(16).padStart(8, "0");
|
|
166
|
-
}
|
|
167
|
-
function isRecord(value) {
|
|
168
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
169
|
-
}
|
|
170
|
-
function redactErrorData(value, depth = 0) {
|
|
171
|
-
if (value === void 0) return;
|
|
172
|
-
if (value === null || typeof value === "boolean" || typeof value === "number") return value;
|
|
173
|
-
if (depth > 6) return "[truncated]";
|
|
174
|
-
if (Array.isArray(value)) return value.map((entry) => redactErrorData(entry, depth + 1) ?? null);
|
|
175
|
-
if (isRecord(value)) {
|
|
176
|
-
const redacted = {};
|
|
177
|
-
for (const [key, entry] of Object.entries(value)) redacted[key] = isSensitiveErrorDataKey(key) ? "<redacted>" : redactErrorData(entry, depth + 1) ?? null;
|
|
178
|
-
return redacted;
|
|
179
|
-
}
|
|
180
|
-
if (typeof value === "string" && value.length > 500) return `${value.slice(0, 500)}...`;
|
|
181
|
-
if (typeof value === "string") return value;
|
|
182
|
-
if (typeof value === "bigint") return value.toString();
|
|
183
|
-
if (typeof value === "symbol") return value.description ? `Symbol(${value.description})` : "Symbol()";
|
|
184
|
-
if (typeof value === "function") return value.name ? `[function ${value.name}]` : "[function]";
|
|
185
|
-
return "[unserializable]";
|
|
186
|
-
}
|
|
187
|
-
function sanitizeErrorMessage(message) {
|
|
188
|
-
const htmlStart = message.search(/<html[\s>]/i);
|
|
189
|
-
const redacted = (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>");
|
|
190
|
-
return redacted.length > MAX_SERIALIZED_ERROR_MESSAGE_LENGTH ? `${redacted.slice(0, MAX_SERIALIZED_ERROR_MESSAGE_LENGTH)}...` : redacted;
|
|
191
|
-
}
|
|
192
|
-
function isSensitiveErrorDataKey(key) {
|
|
193
|
-
return /api[_-]?key|authorization|cookie|credential|password|secret|token/i.test(key);
|
|
194
|
-
}
|
|
195
|
-
//#endregion
|
|
196
|
-
//#region extensions/codex/src/app-server/plugin-inventory.ts
|
|
197
|
-
async function readCodexPluginInventory(params) {
|
|
198
|
-
const policy = params.policy ?? resolveCodexPluginsPolicy(params.pluginConfig);
|
|
199
|
-
if (!policy.enabled) return {
|
|
200
|
-
policy,
|
|
201
|
-
records: [],
|
|
202
|
-
diagnostics: [{
|
|
203
|
-
code: "disabled",
|
|
204
|
-
message: "Native Codex plugin support is disabled."
|
|
205
|
-
}]
|
|
206
|
-
};
|
|
207
|
-
const appInventory = readCachedAppInventory(params);
|
|
208
|
-
const marketplaceEntry = (await params.request("plugin/list", { cwds: [] })).marketplaces.find((marketplace) => marketplace.name === CODEX_PLUGINS_MARKETPLACE_NAME);
|
|
209
|
-
if (!marketplaceEntry) return {
|
|
210
|
-
policy,
|
|
211
|
-
records: [],
|
|
212
|
-
diagnostics: policy.pluginPolicies.filter((pluginPolicy) => pluginPolicy.enabled).map((pluginPolicy) => ({
|
|
213
|
-
code: "marketplace_missing",
|
|
214
|
-
plugin: pluginPolicy,
|
|
215
|
-
message: `Codex marketplace ${CODEX_PLUGINS_MARKETPLACE_NAME} was not found.`
|
|
216
|
-
})),
|
|
217
|
-
...appInventory ? { appInventory } : {}
|
|
218
|
-
};
|
|
219
|
-
const marketplace = marketplaceRef(marketplaceEntry);
|
|
220
|
-
const diagnostics = [];
|
|
221
|
-
const records = [];
|
|
222
|
-
if (appInventory?.state === "missing") diagnostics.push({
|
|
223
|
-
code: "app_inventory_missing",
|
|
224
|
-
message: "Cached Codex app inventory is missing; plugin apps are excluded for this setup."
|
|
225
|
-
});
|
|
226
|
-
else if (appInventory?.state === "stale") diagnostics.push({
|
|
227
|
-
code: "app_inventory_stale",
|
|
228
|
-
message: "Cached Codex app inventory is stale; using stale app readiness and refreshing."
|
|
229
|
-
});
|
|
230
|
-
for (const pluginPolicy of policy.pluginPolicies) {
|
|
231
|
-
if (!pluginPolicy.enabled) continue;
|
|
232
|
-
const summary = findPluginSummary(marketplaceEntry, pluginPolicy.pluginName);
|
|
233
|
-
if (!summary) {
|
|
234
|
-
diagnostics.push({
|
|
235
|
-
code: "plugin_missing",
|
|
236
|
-
plugin: pluginPolicy,
|
|
237
|
-
message: `${pluginPolicy.pluginName} was not found in ${CODEX_PLUGINS_MARKETPLACE_NAME}.`
|
|
238
|
-
});
|
|
239
|
-
continue;
|
|
240
|
-
}
|
|
241
|
-
const detail = await readPluginDetail(params, marketplace, pluginPolicy, diagnostics);
|
|
242
|
-
const ownedAppIds = detail?.apps.map((app) => app.id).filter(Boolean).toSorted() ?? [];
|
|
243
|
-
const appOwnership = resolveAppOwnership({
|
|
244
|
-
detail,
|
|
245
|
-
appInventory,
|
|
246
|
-
summary
|
|
247
|
-
});
|
|
248
|
-
if (appOwnership === "ambiguous") diagnostics.push({
|
|
249
|
-
code: "app_ownership_ambiguous",
|
|
250
|
-
plugin: pluginPolicy,
|
|
251
|
-
message: `${pluginPolicy.pluginName} has only display-name app matches; apps are not exposed until ownership is stable.`
|
|
252
|
-
});
|
|
253
|
-
if (summary.installed && !summary.enabled) diagnostics.push({
|
|
254
|
-
code: "plugin_disabled",
|
|
255
|
-
plugin: pluginPolicy,
|
|
256
|
-
message: `${pluginPolicy.pluginName} is installed in Codex but disabled.`
|
|
257
|
-
});
|
|
258
|
-
const apps = resolveOwnedApps({
|
|
259
|
-
pluginPolicy,
|
|
260
|
-
detail,
|
|
261
|
-
appInventory
|
|
262
|
-
});
|
|
263
|
-
records.push({
|
|
264
|
-
policy: pluginPolicy,
|
|
265
|
-
summary,
|
|
266
|
-
...detail ? { detail } : {},
|
|
267
|
-
activationRequired: !summary.installed || !summary.enabled,
|
|
268
|
-
authRequired: apps.some((app) => app.needsAuth || !app.accessible),
|
|
269
|
-
appOwnership,
|
|
270
|
-
ownedAppIds,
|
|
271
|
-
apps
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
return {
|
|
275
|
-
policy,
|
|
276
|
-
marketplace,
|
|
277
|
-
records,
|
|
278
|
-
diagnostics,
|
|
279
|
-
...appInventory ? { appInventory } : {}
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
function findOpenAiCuratedPluginSummary(listed, pluginName) {
|
|
283
|
-
const marketplaceEntry = listed.marketplaces.find((marketplace) => marketplace.name === CODEX_PLUGINS_MARKETPLACE_NAME);
|
|
284
|
-
if (!marketplaceEntry) return;
|
|
285
|
-
const summary = findPluginSummary(marketplaceEntry, pluginName);
|
|
286
|
-
return summary ? {
|
|
287
|
-
marketplace: marketplaceRef(marketplaceEntry),
|
|
288
|
-
summary
|
|
289
|
-
} : void 0;
|
|
290
|
-
}
|
|
291
|
-
function pluginReadParams(marketplace, pluginName) {
|
|
292
|
-
return {
|
|
293
|
-
...marketplace.path ? { marketplacePath: marketplace.path } : {},
|
|
294
|
-
...marketplace.remoteMarketplaceName ? { remoteMarketplaceName: marketplace.remoteMarketplaceName } : {},
|
|
295
|
-
pluginName
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
function readCachedAppInventory(params) {
|
|
299
|
-
if (!params.appCache || !params.appCacheKey) return;
|
|
300
|
-
const request = async (method, requestParams) => await params.request(method, requestParams);
|
|
301
|
-
return params.appCache.read({
|
|
302
|
-
key: params.appCacheKey,
|
|
303
|
-
request,
|
|
304
|
-
nowMs: params.nowMs,
|
|
305
|
-
suppressRefresh: params.suppressAppInventoryRefresh
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
async function readPluginDetail(params, marketplace, pluginPolicy, diagnostics) {
|
|
309
|
-
if (params.readPluginDetails === false) return;
|
|
310
|
-
try {
|
|
311
|
-
return (await params.request("plugin/read", pluginReadParams(marketplace, pluginPolicy.pluginName))).plugin;
|
|
312
|
-
} catch (error) {
|
|
313
|
-
diagnostics.push({
|
|
314
|
-
code: "plugin_detail_unavailable",
|
|
315
|
-
plugin: pluginPolicy,
|
|
316
|
-
message: `${pluginPolicy.pluginName} detail unavailable: ${error instanceof Error ? error.message : String(error)}`
|
|
317
|
-
});
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
function resolveAppOwnership(params) {
|
|
322
|
-
if (params.detail && params.detail.apps.length > 0) return "proven";
|
|
323
|
-
return (params.appInventory?.snapshot?.apps ?? []).filter((app) => app.pluginDisplayNames.some((displayName) => displayName === params.summary.name)).length > 0 ? "ambiguous" : "none";
|
|
324
|
-
}
|
|
325
|
-
function resolveOwnedApps(params) {
|
|
326
|
-
const detailApps = params.detail?.apps ?? [];
|
|
327
|
-
if (detailApps.length === 0) return [];
|
|
328
|
-
if (params.appInventory?.state === "missing") {
|
|
329
|
-
embeddedAgentLog.warn("codex plugin inventory missing app inventory for detail apps", {
|
|
330
|
-
configKey: params.pluginPolicy.configKey,
|
|
331
|
-
pluginName: params.pluginPolicy.pluginName,
|
|
332
|
-
appIds: detailApps.map((app) => app.id).toSorted()
|
|
333
|
-
});
|
|
334
|
-
return [];
|
|
335
|
-
}
|
|
336
|
-
const appInfoById = new Map((params.appInventory?.snapshot?.apps ?? []).map((app) => [app.id, app]));
|
|
337
|
-
return detailApps.map((app) => {
|
|
338
|
-
const info = appInfoById.get(app.id);
|
|
339
|
-
if (!info) return {
|
|
340
|
-
id: app.id,
|
|
341
|
-
name: app.name,
|
|
342
|
-
accessible: false,
|
|
343
|
-
enabled: false,
|
|
344
|
-
needsAuth: true
|
|
345
|
-
};
|
|
346
|
-
return {
|
|
347
|
-
id: app.id,
|
|
348
|
-
name: app.name,
|
|
349
|
-
accessible: info.isAccessible,
|
|
350
|
-
enabled: info.isEnabled,
|
|
351
|
-
needsAuth: app.needsAuth || !info.isAccessible
|
|
352
|
-
};
|
|
353
|
-
}).toSorted((left, right) => left.id.localeCompare(right.id));
|
|
354
|
-
}
|
|
355
|
-
function findPluginSummary(marketplace, pluginName) {
|
|
356
|
-
return marketplace.plugins.find((plugin) => plugin.name === pluginName || plugin.id === pluginName || plugin.id === `${pluginName}@${marketplace.name}` || pluginNameFromPluginId(plugin.id, marketplace.name) === pluginName);
|
|
357
|
-
}
|
|
358
|
-
function pluginNameFromPluginId(pluginId, marketplaceName) {
|
|
359
|
-
const trimmed = pluginId.trim();
|
|
360
|
-
if (!trimmed) return;
|
|
361
|
-
const marketplaceSuffix = `@${marketplaceName}`;
|
|
362
|
-
return (trimmed.endsWith(marketplaceSuffix) ? trimmed.slice(0, -marketplaceSuffix.length) : trimmed).split("/").at(-1)?.trim() || void 0;
|
|
363
|
-
}
|
|
364
|
-
function marketplaceRef(marketplace) {
|
|
365
|
-
return {
|
|
366
|
-
name: CODEX_PLUGINS_MARKETPLACE_NAME,
|
|
367
|
-
...marketplace.path ? { path: marketplace.path } : {},
|
|
368
|
-
...!marketplace.path ? { remoteMarketplaceName: marketplace.name } : {}
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
//#endregion
|
|
372
|
-
//#region extensions/codex/src/app-server/plugin-activation.ts
|
|
373
|
-
async function ensureCodexPluginActivation(params) {
|
|
374
|
-
if (params.identity.marketplaceName !== "openai-curated") return activationFailure(params.identity, "marketplace_missing", { message: "Only " + CODEX_PLUGINS_MARKETPLACE_NAME + " plugins can be activated." });
|
|
375
|
-
const listed = await params.request("plugin/list", { cwds: [] });
|
|
376
|
-
const resolved = findOpenAiCuratedPluginSummary(listed, params.identity.pluginName);
|
|
377
|
-
if (!resolved) {
|
|
378
|
-
if (!listed.marketplaces.some((marketplace) => marketplace.name === "openai-curated")) return activationFailure(params.identity, "marketplace_missing", { message: `Codex marketplace ${CODEX_PLUGINS_MARKETPLACE_NAME} was not found.` });
|
|
379
|
-
return activationFailure(params.identity, "plugin_missing", { message: `${params.identity.pluginName} was not found in ${CODEX_PLUGINS_MARKETPLACE_NAME}.` });
|
|
380
|
-
}
|
|
381
|
-
if (resolved.summary.installed && resolved.summary.enabled && !params.installEvenIfActive) return {
|
|
382
|
-
identity: params.identity,
|
|
383
|
-
ok: true,
|
|
384
|
-
reason: "already_active",
|
|
385
|
-
installAttempted: false,
|
|
386
|
-
marketplace: resolved.marketplace,
|
|
387
|
-
diagnostics: []
|
|
388
|
-
};
|
|
389
|
-
const installResponse = await params.request("plugin/install", pluginReadParams(resolved.marketplace, params.identity.pluginName));
|
|
390
|
-
const refreshDiagnostics = [];
|
|
391
|
-
let refreshFailed = false;
|
|
392
|
-
try {
|
|
393
|
-
const refreshResult = await refreshCodexPluginRuntimeState({
|
|
394
|
-
request: params.request,
|
|
395
|
-
appCache: params.appCache,
|
|
396
|
-
appCacheKey: params.appCacheKey
|
|
397
|
-
});
|
|
398
|
-
refreshDiagnostics.push(...refreshResult.diagnostics);
|
|
399
|
-
} catch (error) {
|
|
400
|
-
refreshFailed = true;
|
|
401
|
-
refreshDiagnostics.push({ message: `Codex plugin runtime refresh failed after install: ${error instanceof Error ? error.message : String(error)}` });
|
|
402
|
-
}
|
|
403
|
-
const authRequired = installResponse.appsNeedingAuth.length > 0;
|
|
404
|
-
return {
|
|
405
|
-
identity: params.identity,
|
|
406
|
-
ok: !authRequired && !refreshFailed,
|
|
407
|
-
reason: refreshFailed ? "refresh_failed" : authRequired ? "auth_required" : resolved.summary.installed && resolved.summary.enabled ? "already_active" : "installed",
|
|
408
|
-
installAttempted: true,
|
|
409
|
-
marketplace: resolved.marketplace,
|
|
410
|
-
installResponse,
|
|
411
|
-
diagnostics: [...refreshDiagnostics, ...installResponse.appsNeedingAuth.map((app) => ({ message: `${app.name} requires app authentication before plugin tools are exposed.` }))]
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
async function refreshCodexPluginRuntimeState(params) {
|
|
415
|
-
const diagnostics = [];
|
|
416
|
-
await params.request("plugin/list", { cwds: [] });
|
|
417
|
-
await params.request("skills/list", {
|
|
418
|
-
cwds: [],
|
|
419
|
-
forceReload: true
|
|
420
|
-
});
|
|
421
|
-
try {
|
|
422
|
-
await params.request("hooks/list", { cwds: [] });
|
|
423
|
-
} catch (error) {
|
|
424
|
-
diagnostics.push({ message: `Codex hooks refresh skipped: ${error instanceof Error ? error.message : String(error)}` });
|
|
425
|
-
}
|
|
426
|
-
await params.request("config/mcpServer/reload", void 0);
|
|
427
|
-
if (params.appCache && params.appCacheKey) {
|
|
428
|
-
params.appCache.invalidate(params.appCacheKey, "Codex plugin activation changed app inventory");
|
|
429
|
-
const request = async (method, requestParams) => await params.request(method, requestParams);
|
|
430
|
-
try {
|
|
431
|
-
await params.appCache.refreshNow({
|
|
432
|
-
key: params.appCacheKey,
|
|
433
|
-
request,
|
|
434
|
-
forceRefetch: true
|
|
435
|
-
});
|
|
436
|
-
} catch (error) {
|
|
437
|
-
diagnostics.push({ message: `Codex app inventory refresh skipped: ${error instanceof Error ? error.message : String(error)}` });
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
return { diagnostics };
|
|
441
|
-
}
|
|
442
|
-
function activationFailure(identity, reason, diagnostic) {
|
|
443
|
-
return {
|
|
444
|
-
identity,
|
|
445
|
-
ok: false,
|
|
446
|
-
reason,
|
|
447
|
-
installAttempted: false,
|
|
448
|
-
diagnostics: [diagnostic]
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
//#endregion
|
|
452
|
-
export { defaultCodexAppInventoryCache as a, buildCodexAppInventoryCacheKey as i, pluginReadParams as n, serializeCodexAppInventoryError as o, readCodexPluginInventory as r, ensureCodexPluginActivation as t };
|