@openclaw/codex 2026.7.2-beta.7 → 2026.7.34
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 +3 -27
- package/dist/{command-formatters-C336eRVS.js → .setup/app-server-policy-C_qTMEMy.mjs} +51 -9
- package/dist/{attempt-notifications-AP-a-Q5k.js → .setup/attempt-notifications-F8krwWL4.mjs} +6 -3
- package/dist/{command-handlers-DAHUSry9.js → .setup/command-handlers-DSRFfor4.mjs} +1156 -1319
- package/dist/.setup/command-rpc-BvYsfQ7G.mjs +786 -0
- package/dist/{compact-D_uvHurS.js → .setup/compact-BNgh7I4t.mjs} +10 -40
- package/dist/.setup/computer-use-C3QkmSO3.mjs +377 -0
- package/dist/.setup/config-CYEDnLJ2.mjs +1138 -0
- package/dist/{conversation-binding-data-BHRpYbX3.js → .setup/conversation-binding-data-CrRxtDBu.mjs} +1 -1
- package/dist/{bounded-turn-D2ZAgZVz.js → .setup/media-understanding-provider-EbJCyDqe.mjs} +203 -109
- package/dist/{models-BBMKU4jA.js → .setup/models-C_aZk03A.mjs} +11 -6
- package/dist/.setup/notification-correlation-iYs4WieS.mjs +35 -0
- package/dist/.setup/plugin-app-cache-key-DR8jV9oS.mjs +99 -0
- package/dist/.setup/protocol-2POPqAY4.mjs +13 -0
- package/dist/{transcript-mirror-DzsdNCf5.js → .setup/protocol-validators-dZQ-UTOa.mjs} +112 -922
- package/dist/{rate-limits-CU-gVCCS.js → .setup/provider-DGxDs6g8.mjs} +269 -13
- package/dist/{dynamic-tools-D2gYkSBO.js → .setup/provider-capabilities-DS2N_zc1.mjs} +5632 -3953
- package/dist/.setup/request-DFR0JyoV.mjs +36 -0
- package/dist/.setup/run-attempt-C7xEKFVB.mjs +8180 -0
- package/dist/{sandbox-guard-NuhpXmPB.js → .setup/sandbox-guard-DA2TQfZW.mjs} +1 -14
- package/dist/.setup/session-binding-DuihlMJc.mjs +628 -0
- package/dist/.setup/shared-client-B2KmLPRw.mjs +1854 -0
- package/dist/{side-question-_ulBk1AL.js → .setup/side-question-Cv6JXWCS.mjs} +84 -159
- package/dist/.setup/thread-lifecycle-u6CZdOOH.mjs +2593 -0
- package/dist/{web-search-provider.runtime-CCFhg2z5.js → .setup/web-search-provider.runtime-Ce2zOHxW.mjs} +3 -3
- package/dist/doctor-contract-api.js +122 -88
- package/dist/harness.js +20 -89
- package/dist/index.js +436 -1615
- package/dist/media-understanding-provider.js +1 -173
- package/dist/prompt-overlay.js +15 -0
- package/dist/provider-catalog.js +79 -0
- package/dist/provider-discovery.js +34 -0
- package/dist/provider.js +2 -0
- package/dist/web-search-contract-api.js +1 -1
- package/openclaw.plugin.json +178 -221
- package/package.json +8 -9
- package/dist/app-server-policy-BSaO2JIT.js +0 -49
- package/dist/auth-binding-B4FfbcCg.js +0 -62
- package/dist/capabilities-BFDTVYr4.js +0 -36
- package/dist/cli-metadata.js +0 -20
- package/dist/computer-use-CYEc8U3w.js +0 -693
- package/dist/conversation-control-xsIeXbo4.js +0 -287
- package/dist/effective-mcp-catalog-Bbd98Inw.js +0 -114
- package/dist/event-projector-assistant-message-DTgnPPVF.js +0 -101
- package/dist/event-projector-usage-Xp4AlR2k.js +0 -86
- package/dist/incognito-session-ET_ufDH-.js +0 -2
- package/dist/notification-correlation-CU6_AnOb.js +0 -67
- package/dist/plugin-app-cache-key-DztPafOA.js +0 -408
- package/dist/request-qKh331yu.js +0 -157
- package/dist/run-attempt-CY9WlDLc.js +0 -12413
- package/dist/runtime-artifact-CmtG_t1L.js +0 -554
- package/dist/session-binding-BXpo9ymA.js +0 -2214
- package/dist/session-catalog-DyOgR4WH.js +0 -2668
- package/dist/session-cli-2PZaFLRh.js +0 -175
- package/dist/session-upstream-marker-B6U7tLw5.js +0 -82
- package/dist/settled-turn-finalizer-CXY2Z9dT.js +0 -316
- package/dist/shared-client-DTbvH-XV.js +0 -3069
- package/dist/thread-lifecycle-DF53ogiA.js +0 -3987
- package/dist/transport-stdio-DXgGZ7Ib.js +0 -88
- package/dist/upstream-session-fork-Dy4Xvuoj.js +0 -320
- package/dist/usage-CQ6GhD5-.js +0 -25
- /package/dist/{rolldown-runtime-8H4AJuhK.js → .setup/rolldown-runtime-8H4AJuhK.mjs} +0 -0
- /package/dist/{session-binding-meta-B7aEMU7g.js → .setup/session-binding-meta-B7aEMU7g.mjs} +0 -0
- /package/dist/{web-search-provider.shared-BrZmlqyR.js → .setup/web-search-provider.shared-BrZmlqyR.mjs} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,84 +1,59 @@
|
|
|
1
|
-
import { registerCodexCliMetadata } from "./cli-metadata.js";
|
|
2
1
|
import { createCodexAppServerAgentHarness } from "./harness.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { t as
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { r as
|
|
13
|
-
import { t as resolveCodexAppServerForModelProvider } from "
|
|
14
|
-
import { n as resolveCodexNativeExecutionBlock, r as resolveCodexNativeSandboxBlock } from "
|
|
15
|
-
import {
|
|
16
|
-
import { a as readCodexConversationBindingDataRecord, i as readCodexConversationBindingData
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { t as createCodexWebSearchProviderBase } from "./web-search-provider.shared-BrZmlqyR.js";
|
|
2
|
+
import { r as isJsonObject, t as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS } from "./.setup/protocol-2POPqAY4.mjs";
|
|
3
|
+
import { d as resolveCodexAppServerRuntimeOptions, g as resolveOpenClawExecPolicyForCodexAppServer, i as codexSandboxPolicyForTurn, n as canUseCodexModelBackedApprovalsReviewerForModel, t as CODEX_PLUGINS_MARKETPLACE_NAME, u as readCodexPluginConfig } from "./.setup/config-CYEDnLJ2.mjs";
|
|
4
|
+
import { r as assertCodexThreadStartResponse } from "./.setup/protocol-validators-dZQ-UTOa.mjs";
|
|
5
|
+
import { G as pluginReadParams, W as ensureCodexPluginActivation, f as resolveCodexAppServerRequestModelSelection, q as defaultCodexAppInventoryCache, t as CODEX_NATIVE_PERSONALITY_NONE } from "./.setup/thread-lifecycle-u6CZdOOH.mjs";
|
|
6
|
+
import { t as buildCodexProvider } from "./.setup/provider-DGxDs6g8.mjs";
|
|
7
|
+
import { _ as resolveCodexAppServerAuthAccountCacheKey, a as getLeasedSharedCodexAppServerClient, b as resolveCodexAppServerFallbackApiKeyCacheKey, o as releaseLeasedSharedCodexAppServerClient, r as clearSharedCodexAppServerClientIfCurrentAndWait, y as resolveCodexAppServerAuthProfileIdForAgent } from "./.setup/shared-client-B2KmLPRw.mjs";
|
|
8
|
+
import { n as CODEX_APP_SERVER_BINDING_NAMESPACE, t as CODEX_APP_SERVER_BINDING_MAX_ENTRIES } from "./.setup/session-binding-meta-B7aEMU7g.mjs";
|
|
9
|
+
import { a as normalizeCodexAppServerBindingModelProvider, i as isCodexAppServerNativeAuthProfile, s as sessionBindingIdentity } from "./.setup/session-binding-DuihlMJc.mjs";
|
|
10
|
+
import { t as buildCodexMediaUnderstandingProvider } from "./.setup/media-understanding-provider-EbJCyDqe.mjs";
|
|
11
|
+
import { i as describeControlFailure, n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS } from "./.setup/plugin-app-cache-key-DR8jV9oS.mjs";
|
|
12
|
+
import { a as formatCodexDisplayText, t as resolveCodexAppServerForModelProvider } from "./.setup/app-server-policy-C_qTMEMy.mjs";
|
|
13
|
+
import { n as resolveCodexNativeExecutionBlock, r as resolveCodexNativeSandboxBlock } from "./.setup/sandbox-guard-DA2TQfZW.mjs";
|
|
14
|
+
import { S as canMutateCodexHost, a as createCodexCliSessionNodeHostCommands, b as trackCodexConversationActiveTurn, c as listCodexCliSessionsOnNode, l as resolveCodexCliSessionForBindingOnNode, o as createCodexCliSessionNodeInvokePolicies, t as codexControlRequest, u as resumeCodexCliSessionOnNode, x as CODEX_NATIVE_EXECUTION_AUTH_ERROR } from "./.setup/command-rpc-BvYsfQ7G.mjs";
|
|
15
|
+
import { a as readCodexConversationBindingDataRecord, i as readCodexConversationBindingData } from "./.setup/conversation-binding-data-CrRxtDBu.mjs";
|
|
16
|
+
import { n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./.setup/notification-correlation-iYs4WieS.mjs";
|
|
17
|
+
import { t as requestCodexAppServerJson } from "./.setup/request-DFR0JyoV.mjs";
|
|
18
|
+
import { t as createCodexWebSearchProviderBase } from "./.setup/web-search-provider.shared-BrZmlqyR.mjs";
|
|
21
19
|
import { mutateConfigFile } from "openclaw/plugin-sdk/config-mutation";
|
|
22
|
-
import {
|
|
20
|
+
import { resolveLivePluginConfigObject, resolvePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
23
21
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
24
22
|
import fs from "node:fs/promises";
|
|
25
23
|
import path from "node:path";
|
|
26
24
|
import { parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
|
27
|
-
import { formatErrorMessage, resolveSandboxContext } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
28
|
-
import { loadAuthProfileStoreWithoutExternalProfiles, resolveAgentConfig, resolveAgentWorkspaceDir, resolveDefaultAgentDir, resolveDefaultAgentId, resolveSessionAgentIds as resolveSessionAgentIds$1 } from "openclaw/plugin-sdk/agent-runtime";
|
|
29
25
|
import os from "node:os";
|
|
30
|
-
import {
|
|
26
|
+
import { loadAuthProfileStoreWithoutExternalProfiles, resolveAgentConfig, resolveAgentWorkspaceDir, resolveDefaultAgentId, resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
|
|
31
27
|
import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
|
|
32
28
|
import { asBoolean, asOptionalRecord, isRecord, normalizeOptionalString, normalizeSingleOrTrimmedStringList, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
33
|
-
import {
|
|
29
|
+
import { formatErrorMessage, resolveSandboxContext } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
30
|
+
import { jsonResult, readStringParam } from "openclaw/plugin-sdk/core";
|
|
31
|
+
import { applyAuthProfileConfig, buildApiKeyCredential, buildOauthProviderAuthResult, buildOpenAICodexCredentialExtra, readCodexCliCredentialsCached, resolveOpenAICodexAuthIdentity, resolveOpenAICodexImportProfileName, updateAuthProfileStoreWithLock } from "openclaw/plugin-sdk/provider-auth";
|
|
34
32
|
import { fileURLToPath } from "node:url";
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
33
|
+
import { pathExists } from "openclaw/plugin-sdk/security-runtime";
|
|
34
|
+
import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
37
35
|
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import { archiveMigrationItem, copyMemoryMigrationFileItem, copyMigrationFileItem, withCachedMigrationConfigRuntime, writeMigrationReport } from "openclaw/plugin-sdk/migration-runtime";
|
|
36
|
+
import { MIGRATION_REASON_TARGET_EXISTS, applyMigrationManualItem, createMigrationItem, createMigrationManualItem, hasMigrationConfigPatchConflict, markMigrationItemConflict, markMigrationItemError, markMigrationItemSkipped, readMigrationConfigPath, summarizeMigrationItems, writeMigrationConfigPath } from "openclaw/plugin-sdk/migration";
|
|
37
|
+
import { archiveMigrationItem, copyMigrationFileItem, withCachedMigrationConfigRuntime, writeMigrationReport } from "openclaw/plugin-sdk/migration-runtime";
|
|
41
38
|
import { sleep } from "openclaw/plugin-sdk/runtime-env";
|
|
42
39
|
import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
|
|
43
|
-
import { jsonResult, readStringParam } from "openclaw/plugin-sdk/core";
|
|
44
40
|
import { Type } from "typebox";
|
|
45
41
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
46
42
|
//#region extensions/codex/src/app-server/session-binding-store.ts
|
|
47
43
|
/** Defers schema compilation and auth loading until the first binding operation. */
|
|
48
44
|
function createLazyCodexAppServerBindingStore(state) {
|
|
49
45
|
let resolved;
|
|
50
|
-
const store = () => resolved ??= import("
|
|
46
|
+
const store = () => resolved ??= import("./.setup/session-binding-DuihlMJc.mjs").then((n) => n.c).then(({ createCodexAppServerBindingStore }) => createCodexAppServerBindingStore(state));
|
|
51
47
|
return {
|
|
52
48
|
read: async (identity) => (await store()).read(identity),
|
|
53
|
-
hasOtherThreadOwner: async (threadId, currentIdentity) => (await store()).hasOtherThreadOwner(threadId, currentIdentity),
|
|
54
49
|
mutate: async (identity, mutation) => (await store()).mutate(identity, mutation),
|
|
55
50
|
prepareSessionGenerationReclaim: async (identity) => (await store()).prepareSessionGenerationReclaim(identity),
|
|
56
51
|
adoptSessionGeneration: async (identity, previousSessionId) => (await store()).adoptSessionGeneration(identity, previousSessionId),
|
|
57
|
-
resetSessionGeneration: async (identity) => (await store()).resetSessionGeneration(identity),
|
|
58
52
|
retireSessionGeneration: async (identity) => (await store()).retireSessionGeneration(identity),
|
|
59
|
-
withThreadArchiveFence: async (run) => (await store()).withThreadArchiveFence(run),
|
|
60
53
|
withLease: async (identity, run) => (await store()).withLease(identity, run)
|
|
61
54
|
};
|
|
62
55
|
}
|
|
63
56
|
//#endregion
|
|
64
|
-
//#region extensions/codex/src/command-dispatch.ts
|
|
65
|
-
/** Dispatches a `/codex` command to the lazily loaded handler. */
|
|
66
|
-
async function handleCodexCommand(ctx, options) {
|
|
67
|
-
const { loadSubcommandHandler, resolvePluginConfig, ...subcommandOptions } = options;
|
|
68
|
-
try {
|
|
69
|
-
return await (loadSubcommandHandler ? await loadSubcommandHandler() : await loadDefaultCodexSubcommandHandler())(ctx, {
|
|
70
|
-
...subcommandOptions,
|
|
71
|
-
pluginConfig: resolvePluginConfig?.() ?? subcommandOptions.pluginConfig
|
|
72
|
-
});
|
|
73
|
-
} catch (error) {
|
|
74
|
-
return { text: `Codex command failed: ${formatCodexDisplayText(describeControlFailure(error))}` };
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
async function loadDefaultCodexSubcommandHandler() {
|
|
78
|
-
const { handleCodexSubcommand } = await import("./command-handlers-DAHUSry9.js");
|
|
79
|
-
return handleCodexSubcommand;
|
|
80
|
-
}
|
|
81
|
-
//#endregion
|
|
82
57
|
//#region extensions/codex/src/commands.ts
|
|
83
58
|
/** Creates the reserved `/codex` command definition exposed by the plugin. */
|
|
84
59
|
function createCodexCommand(options) {
|
|
@@ -98,6 +73,22 @@ function createCodexCommand(options) {
|
|
|
98
73
|
handler: (ctx) => handleCodexCommand(ctx, options)
|
|
99
74
|
};
|
|
100
75
|
}
|
|
76
|
+
/** Dispatches a `/codex` command to the subcommand handler and formats failures for chat. */
|
|
77
|
+
async function handleCodexCommand(ctx, options) {
|
|
78
|
+
const { loadSubcommandHandler, resolvePluginConfig, ...subcommandOptions } = options;
|
|
79
|
+
try {
|
|
80
|
+
return await (loadSubcommandHandler ? await loadSubcommandHandler() : await loadDefaultCodexSubcommandHandler())(ctx, {
|
|
81
|
+
...subcommandOptions,
|
|
82
|
+
pluginConfig: resolvePluginConfig?.() ?? subcommandOptions.pluginConfig
|
|
83
|
+
});
|
|
84
|
+
} catch (error) {
|
|
85
|
+
return { text: `Codex command failed: ${formatCodexDisplayText(describeControlFailure(error))}` };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function loadDefaultCodexSubcommandHandler() {
|
|
89
|
+
const { handleCodexSubcommand } = await import("./.setup/command-handlers-DSRFfor4.mjs");
|
|
90
|
+
return handleCodexSubcommand;
|
|
91
|
+
}
|
|
101
92
|
//#endregion
|
|
102
93
|
//#region extensions/codex/src/conversation-turn-collector.ts
|
|
103
94
|
const MAX_PENDING_NOTIFICATIONS_PER_TURN = 100;
|
|
@@ -293,7 +284,7 @@ function readLocalMediaPath(value) {
|
|
|
293
284
|
}
|
|
294
285
|
//#endregion
|
|
295
286
|
//#region extensions/codex/src/conversation-binding.ts
|
|
296
|
-
const DEFAULT_BOUND_TURN_TIMEOUT_MS =
|
|
287
|
+
const DEFAULT_BOUND_TURN_TIMEOUT_MS = 12e5;
|
|
297
288
|
const DEFAULT_AGENT_ID = "main";
|
|
298
289
|
const VALID_AGENT_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
|
|
299
290
|
const INVALID_AGENT_ID_CHARS_PATTERN = /[^a-z0-9_-]+/g;
|
|
@@ -331,72 +322,6 @@ function getGlobalState() {
|
|
|
331
322
|
globalState[CODEX_CONVERSATION_GLOBAL_STATE] ??= { queue: new KeyedAsyncQueue() };
|
|
332
323
|
return globalState[CODEX_CONVERSATION_GLOBAL_STATE];
|
|
333
324
|
}
|
|
334
|
-
async function startCodexConversationThread(params) {
|
|
335
|
-
const workspaceDir = params.workspaceDir?.trim() || resolveCodexDefaultWorkspaceDir(params.pluginConfig);
|
|
336
|
-
const agentDir = params.agentDir?.trim();
|
|
337
|
-
const agentLookup = buildAgentLookup({
|
|
338
|
-
agentDir,
|
|
339
|
-
config: params.config
|
|
340
|
-
});
|
|
341
|
-
const identity = sessionBindingIdentity({
|
|
342
|
-
agentId: params.agentId,
|
|
343
|
-
sessionId: params.sessionFile,
|
|
344
|
-
sessionKey: params.sessionKey,
|
|
345
|
-
config: params.config
|
|
346
|
-
});
|
|
347
|
-
const existingBinding = await params.bindingStore.read(identity);
|
|
348
|
-
assertCodexBindingMayBeReplaced(existingBinding, "starting a conversation-bound Codex thread");
|
|
349
|
-
const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
|
|
350
|
-
authProfileId: params.authProfileId ?? existingBinding?.authProfileId,
|
|
351
|
-
...agentLookup
|
|
352
|
-
});
|
|
353
|
-
if (params.threadId?.trim()) await attachExistingThread({
|
|
354
|
-
pluginConfig: params.pluginConfig,
|
|
355
|
-
bindingStore: params.bindingStore,
|
|
356
|
-
identity,
|
|
357
|
-
threadId: params.threadId.trim(),
|
|
358
|
-
workspaceDir,
|
|
359
|
-
...agentDir ? { agentDir } : {},
|
|
360
|
-
model: params.model,
|
|
361
|
-
modelProvider: params.modelProvider,
|
|
362
|
-
authProfileId,
|
|
363
|
-
approvalPolicy: params.approvalPolicy,
|
|
364
|
-
sandbox: params.sandbox,
|
|
365
|
-
serviceTier: params.serviceTier,
|
|
366
|
-
config: params.config,
|
|
367
|
-
sessionKey: params.sessionKey,
|
|
368
|
-
agentId: params.agentId
|
|
369
|
-
});
|
|
370
|
-
else await createThread({
|
|
371
|
-
pluginConfig: params.pluginConfig,
|
|
372
|
-
bindingStore: params.bindingStore,
|
|
373
|
-
identity,
|
|
374
|
-
workspaceDir,
|
|
375
|
-
...agentDir ? { agentDir } : {},
|
|
376
|
-
model: params.model,
|
|
377
|
-
modelProvider: params.modelProvider,
|
|
378
|
-
authProfileId,
|
|
379
|
-
approvalPolicy: params.approvalPolicy,
|
|
380
|
-
sandbox: params.sandbox,
|
|
381
|
-
serviceTier: params.serviceTier,
|
|
382
|
-
config: params.config,
|
|
383
|
-
sessionKey: params.sessionKey,
|
|
384
|
-
agentId: params.agentId
|
|
385
|
-
});
|
|
386
|
-
const storedBinding = await params.bindingStore.read(identity);
|
|
387
|
-
if (!storedBinding) throw new Error("Codex session binding disappeared while starting its conversation thread.");
|
|
388
|
-
return createCodexConversationBindingData({
|
|
389
|
-
source: {
|
|
390
|
-
agentId: identity.agentId,
|
|
391
|
-
sessionId: identity.sessionId,
|
|
392
|
-
threadId: storedBinding.threadId,
|
|
393
|
-
...identity.sessionKey ? { sessionKey: identity.sessionKey } : {}
|
|
394
|
-
},
|
|
395
|
-
workspaceDir,
|
|
396
|
-
...agentDir ? { agentDir } : {},
|
|
397
|
-
agentId: params.agentId
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
325
|
async function handleCodexConversationInboundClaim(event, ctx, options) {
|
|
401
326
|
const data = readCodexConversationBindingData(ctx.pluginBinding);
|
|
402
327
|
if (!data) return;
|
|
@@ -474,7 +399,6 @@ async function handleCodexConversationBindingResolved(event, options) {
|
|
|
474
399
|
if (!data || data.kind !== "codex-app-server-session") return;
|
|
475
400
|
const identity = conversationBindingIdentity(data);
|
|
476
401
|
const binding = await options.bindingStore.read(identity);
|
|
477
|
-
assertCodexBindingMayBeReplaced(binding, "clearing a denied conversation binding");
|
|
478
402
|
if (!data.start?.id || binding?.conversationStartId === data.start.id) await options.bindingStore.mutate(identity, { kind: "clear" });
|
|
479
403
|
}
|
|
480
404
|
async function resolveThreadBindingRuntime(params) {
|
|
@@ -528,22 +452,19 @@ async function resolveThreadBindingRuntime(params) {
|
|
|
528
452
|
agentDir: params.agentDir
|
|
529
453
|
})
|
|
530
454
|
});
|
|
531
|
-
const
|
|
455
|
+
const client = await getLeasedSharedCodexAppServerClient({
|
|
532
456
|
startOptions: runtime.start,
|
|
533
457
|
timeoutMs: runtime.requestTimeoutMs,
|
|
534
458
|
authProfileId: params.authProfileId,
|
|
535
459
|
...agentLookup
|
|
536
|
-
};
|
|
537
|
-
const client = await getLeasedSharedCodexAppServerClient(clientOptions);
|
|
460
|
+
});
|
|
538
461
|
return {
|
|
539
462
|
execPolicy,
|
|
540
463
|
runtime: modelScopedRuntime,
|
|
541
464
|
agentLookup,
|
|
542
465
|
model: modelSelection?.model,
|
|
543
466
|
modelProvider: modelSelection?.modelProvider ?? modelProvider,
|
|
544
|
-
client
|
|
545
|
-
clientLease: { client },
|
|
546
|
-
clientOptions
|
|
467
|
+
client
|
|
547
468
|
};
|
|
548
469
|
}
|
|
549
470
|
function buildThreadRequestRuntimeOptions(params, resolved) {
|
|
@@ -562,32 +483,22 @@ function codexConversationSandboxOrPermissions(runtime, sandbox) {
|
|
|
562
483
|
return { sandbox };
|
|
563
484
|
}
|
|
564
485
|
async function requestNewConversationBindingThread(params, resolved) {
|
|
565
|
-
return await
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
|
|
575
|
-
experimentalRawEvents: true,
|
|
576
|
-
...isIncognitoSessionKey(params.sessionKey) ? { ephemeral: true } : {}
|
|
577
|
-
}, requestOptions),
|
|
578
|
-
onClientChange: (client) => {
|
|
579
|
-
resolved.client = client;
|
|
580
|
-
}
|
|
581
|
-
});
|
|
486
|
+
return await resolved.client.request("thread/start", {
|
|
487
|
+
cwd: params.workspaceDir,
|
|
488
|
+
...resolved.model ? { model: resolved.model } : {},
|
|
489
|
+
...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
|
|
490
|
+
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
|
491
|
+
...buildThreadRequestRuntimeOptions(params, resolved),
|
|
492
|
+
developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
|
|
493
|
+
experimentalRawEvents: true
|
|
494
|
+
}, { timeoutMs: resolved.runtime.requestTimeoutMs });
|
|
582
495
|
}
|
|
583
496
|
async function writeThreadBindingFromResponse(params, resolved, response) {
|
|
584
|
-
assertCodexBindingMayBeReplaced(await params.bindingStore.read(params.identity), "storing a conversation-bound Codex thread");
|
|
585
497
|
const runtimeApprovalPolicy = typeof resolved.runtime.approvalPolicy === "string" ? resolved.runtime.approvalPolicy : void 0;
|
|
586
498
|
if (!await params.bindingStore.mutate(params.identity, {
|
|
587
499
|
kind: "set",
|
|
588
500
|
binding: {
|
|
589
501
|
threadId: response.thread.id,
|
|
590
|
-
clientId: resolved.client.getInstanceId(),
|
|
591
502
|
cwd: response.thread.cwd ?? params.workspaceDir,
|
|
592
503
|
authProfileId: params.authProfileId,
|
|
593
504
|
model: response.model ?? resolved.model ?? params.model,
|
|
@@ -605,34 +516,25 @@ async function writeThreadBindingFromResponse(params, resolved, response) {
|
|
|
605
516
|
})) throw new Error("Codex conversation binding changed while storing its thread.");
|
|
606
517
|
}
|
|
607
518
|
async function attachExistingThread(params) {
|
|
608
|
-
assertCodexBindingMayBeReplaced(await params.bindingStore.read(params.identity), "attaching a conversation-bound Codex thread");
|
|
609
519
|
const resolved = await resolveThreadBindingRuntime(params);
|
|
610
520
|
try {
|
|
611
|
-
await writeThreadBindingFromResponse(params, resolved, resolved.runtime.networkProxy ? await requestNewConversationBindingThread(params, resolved) : await
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
|
619
|
-
...buildThreadRequestRuntimeOptions(params, resolved)
|
|
620
|
-
}, requestOptions),
|
|
621
|
-
onClientChange: (client) => {
|
|
622
|
-
resolved.client = client;
|
|
623
|
-
}
|
|
624
|
-
}));
|
|
521
|
+
await writeThreadBindingFromResponse(params, resolved, resolved.runtime.networkProxy ? await requestNewConversationBindingThread(params, resolved) : await resolved.client.request(CODEX_CONTROL_METHODS.resumeThread, {
|
|
522
|
+
threadId: params.threadId,
|
|
523
|
+
...resolved.model ? { model: resolved.model } : {},
|
|
524
|
+
...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
|
|
525
|
+
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
|
526
|
+
...buildThreadRequestRuntimeOptions(params, resolved)
|
|
527
|
+
}, { timeoutMs: resolved.runtime.requestTimeoutMs }));
|
|
625
528
|
} finally {
|
|
626
|
-
|
|
529
|
+
releaseLeasedSharedCodexAppServerClient(resolved.client);
|
|
627
530
|
}
|
|
628
531
|
}
|
|
629
532
|
async function createThread(params) {
|
|
630
|
-
assertCodexBindingMayBeReplaced(await params.bindingStore.read(params.identity), "creating a conversation-bound Codex thread");
|
|
631
533
|
const resolved = await resolveThreadBindingRuntime(params);
|
|
632
534
|
try {
|
|
633
535
|
await writeThreadBindingFromResponse(params, resolved, await requestNewConversationBindingThread(params, resolved));
|
|
634
536
|
} finally {
|
|
635
|
-
|
|
537
|
+
releaseLeasedSharedCodexAppServerClient(resolved.client);
|
|
636
538
|
}
|
|
637
539
|
}
|
|
638
540
|
async function runBoundTurn(params) {
|
|
@@ -643,7 +545,6 @@ async function runBoundTurn(params) {
|
|
|
643
545
|
const identity = conversationBindingIdentity(params.data);
|
|
644
546
|
const binding = await params.bindingStore.read(identity);
|
|
645
547
|
if (!binding?.threadId) throw new Error("bound Codex conversation has no thread binding");
|
|
646
|
-
assertCodexBindingMayBeReplaced(binding, "running a conversation-bound Codex thread");
|
|
647
548
|
let threadId = binding.threadId;
|
|
648
549
|
const workspaceDir = binding.cwd || params.data.workspaceDir;
|
|
649
550
|
const reviewerModelProvider = resolveModelBackedReviewerPolicyProvider({
|
|
@@ -696,44 +597,33 @@ async function runBoundTurn(params) {
|
|
|
696
597
|
authProfileId: binding.authProfileId,
|
|
697
598
|
...agentLookup
|
|
698
599
|
}) : void 0;
|
|
699
|
-
const
|
|
600
|
+
const client = await getLeasedSharedCodexAppServerClient({
|
|
700
601
|
startOptions: runtime.start,
|
|
701
602
|
timeoutMs: runtime.requestTimeoutMs,
|
|
702
603
|
authProfileId: binding.authProfileId,
|
|
703
604
|
...agentLookup
|
|
704
|
-
};
|
|
705
|
-
let client = await getLeasedSharedCodexAppServerClient(clientOptions);
|
|
706
|
-
const clientLease = { client };
|
|
605
|
+
});
|
|
707
606
|
let notificationCleanup = () => void 0;
|
|
708
607
|
let requestCleanup = () => void 0;
|
|
709
608
|
try {
|
|
710
609
|
if (networkProxyBindingChanged) {
|
|
711
|
-
const response = assertCodexThreadStartResponse(await
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
|
|
724
|
-
experimentalRawEvents: true,
|
|
725
|
-
...isIncognitoSessionKey(params.sessionKey) ? { ephemeral: true } : {}
|
|
726
|
-
}, requestOptions),
|
|
727
|
-
onClientChange: (nextClient) => {
|
|
728
|
-
client = nextClient;
|
|
729
|
-
}
|
|
730
|
-
}));
|
|
610
|
+
const response = assertCodexThreadStartResponse(await client.request("thread/start", {
|
|
611
|
+
cwd: workspaceDir,
|
|
612
|
+
...modelSelection?.model ? { model: modelSelection.model } : {},
|
|
613
|
+
...modelSelection?.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
|
|
614
|
+
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
|
615
|
+
approvalPolicy,
|
|
616
|
+
approvalsReviewer: modelScopedRuntime.approvalsReviewer,
|
|
617
|
+
...modelScopedRuntime.networkProxy ? { config: modelScopedRuntime.networkProxy.configPatch } : { sandbox },
|
|
618
|
+
...serviceTier ? { serviceTier } : {},
|
|
619
|
+
developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
|
|
620
|
+
experimentalRawEvents: true
|
|
621
|
+
}, { timeoutMs: runtime.requestTimeoutMs }));
|
|
731
622
|
threadId = response.thread.id;
|
|
732
623
|
if (!await params.bindingStore.mutate(identity, {
|
|
733
624
|
kind: "set",
|
|
734
625
|
binding: {
|
|
735
626
|
threadId,
|
|
736
|
-
clientId: client.getInstanceId(),
|
|
737
627
|
cwd: response.thread.cwd ?? workspaceDir,
|
|
738
628
|
authProfileId: binding.authProfileId,
|
|
739
629
|
model: response.model ?? modelSelection?.model ?? binding.model,
|
|
@@ -753,46 +643,6 @@ async function runBoundTurn(params) {
|
|
|
753
643
|
}
|
|
754
644
|
})) throw new Error("Codex conversation binding changed while rotating its thread.");
|
|
755
645
|
useStickyNetworkProfile = modelScopedRuntime.networkProxy !== void 0;
|
|
756
|
-
} else if (binding.clientId !== client.getInstanceId()) {
|
|
757
|
-
const response = await withLeasedCodexAppServerClientStartSelectionRetry({
|
|
758
|
-
lease: clientLease,
|
|
759
|
-
options: clientOptions,
|
|
760
|
-
run: async (requestClient, requestOptions) => await resumeCodexAppServerThread({
|
|
761
|
-
client: requestClient,
|
|
762
|
-
abandonClient: async () => {
|
|
763
|
-
await requestClient.closeAndWait();
|
|
764
|
-
},
|
|
765
|
-
request: {
|
|
766
|
-
threadId,
|
|
767
|
-
...modelSelection?.model ? { model: modelSelection.model } : {},
|
|
768
|
-
...modelSelection?.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
|
|
769
|
-
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
|
770
|
-
approvalPolicy,
|
|
771
|
-
approvalsReviewer: modelScopedRuntime.approvalsReviewer,
|
|
772
|
-
...codexConversationSandboxOrPermissions(modelScopedRuntime, sandbox),
|
|
773
|
-
...serviceTier ? { serviceTier } : {}
|
|
774
|
-
},
|
|
775
|
-
...requestOptions
|
|
776
|
-
}),
|
|
777
|
-
onClientChange: (nextClient) => {
|
|
778
|
-
client = nextClient;
|
|
779
|
-
}
|
|
780
|
-
});
|
|
781
|
-
threadId = response.thread.id;
|
|
782
|
-
if (!await params.bindingStore.mutate(identity, {
|
|
783
|
-
kind: "patch",
|
|
784
|
-
threadId: binding.threadId,
|
|
785
|
-
patch: {
|
|
786
|
-
clientId: client.getInstanceId(),
|
|
787
|
-
cwd: response.thread.cwd ?? binding.cwd,
|
|
788
|
-
model: response.model ?? modelSelection?.model ?? binding.model,
|
|
789
|
-
modelProvider: normalizeCodexAppServerBindingModelProvider({
|
|
790
|
-
authProfileId: binding.authProfileId,
|
|
791
|
-
modelProvider: response.modelProvider ?? modelSelection?.modelProvider ?? binding.modelProvider,
|
|
792
|
-
...agentLookup
|
|
793
|
-
})
|
|
794
|
-
}
|
|
795
|
-
})) throw new Error("Codex conversation binding changed while resuming on a new client.");
|
|
796
646
|
}
|
|
797
647
|
const collector = createCodexConversationTurnCollector(threadId);
|
|
798
648
|
notificationCleanup = client.addNotificationHandler((notification) => collector.handleNotification(notification));
|
|
@@ -833,29 +683,15 @@ async function runBoundTurn(params) {
|
|
|
833
683
|
}, { timeoutMs: runtime.requestTimeoutMs })).turn.id;
|
|
834
684
|
const activeCleanup = trackCodexConversationActiveTurn({
|
|
835
685
|
identity,
|
|
836
|
-
client,
|
|
837
686
|
threadId,
|
|
838
687
|
turnId
|
|
839
688
|
});
|
|
840
689
|
collector.setTurnId(turnId);
|
|
841
690
|
return { reply: { text: (await collector.wait({ timeoutMs: params.timeoutMs ?? DEFAULT_BOUND_TURN_TIMEOUT_MS }).finally(activeCleanup)).replyText.trim() || "Codex completed without a text reply." } };
|
|
842
|
-
} catch (error) {
|
|
843
|
-
if (isIncognitoSessionKey(params.sessionKey)) {
|
|
844
|
-
const bindingReleased = await params.bindingStore.mutate(identity, {
|
|
845
|
-
kind: "clear",
|
|
846
|
-
threadId
|
|
847
|
-
});
|
|
848
|
-
const timedOut = error instanceof Error && error.message === "codex app-server bound turn timed out";
|
|
849
|
-
if (bindingReleased && !timedOut) await unsubscribeCodexThreadBestEffort(client, {
|
|
850
|
-
threadId,
|
|
851
|
-
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
852
|
-
});
|
|
853
|
-
}
|
|
854
|
-
throw error;
|
|
855
691
|
} finally {
|
|
856
692
|
notificationCleanup();
|
|
857
693
|
requestCleanup();
|
|
858
|
-
|
|
694
|
+
releaseLeasedSharedCodexAppServerClient(client);
|
|
859
695
|
}
|
|
860
696
|
}
|
|
861
697
|
function assertNativeConversationApprovalPolicySupported(params) {
|
|
@@ -884,8 +720,6 @@ async function prepareConversationBinding(params, options = {}) {
|
|
|
884
720
|
config: params.config
|
|
885
721
|
}) : void 0;
|
|
886
722
|
const sourceBinding = sourceIdentity ? await params.bindingStore.read(sourceIdentity) : void 0;
|
|
887
|
-
assertCodexBindingMayBeReplaced(current, "initializing a conversation-bound Codex thread");
|
|
888
|
-
assertCodexBindingMayBeReplaced(sourceBinding, "transferring a session into a conversation-bound Codex thread");
|
|
889
723
|
const inherited = current ?? sourceBinding;
|
|
890
724
|
const execPolicy = resolveConversationExecPolicy({
|
|
891
725
|
config: params.config,
|
|
@@ -938,7 +772,7 @@ async function prepareConversationBinding(params, options = {}) {
|
|
|
938
772
|
});
|
|
939
773
|
}
|
|
940
774
|
function resolveConversationExecPolicy(params) {
|
|
941
|
-
const agentId = params.agentId ?? (params.config ? resolveSessionAgentIds
|
|
775
|
+
const agentId = params.agentId ?? (params.config ? resolveSessionAgentIds({
|
|
942
776
|
sessionKey: params.sessionKey,
|
|
943
777
|
config: params.config
|
|
944
778
|
}).sessionAgentId : void 0);
|
|
@@ -961,8 +795,9 @@ function readSessionExecOverrides(params) {
|
|
|
961
795
|
agentId: params.agentId,
|
|
962
796
|
sessionKey
|
|
963
797
|
})) return;
|
|
798
|
+
const storePath = resolveStorePath(params.config.session?.store, { agentId: params.agentId });
|
|
964
799
|
const entry = getSessionEntry({
|
|
965
|
-
storePath
|
|
800
|
+
storePath,
|
|
966
801
|
sessionKey,
|
|
967
802
|
readConsistency: "latest"
|
|
968
803
|
});
|
|
@@ -1048,44 +883,6 @@ function conversationBindingIdentity(data) {
|
|
|
1048
883
|
bindingId: data.bindingId
|
|
1049
884
|
};
|
|
1050
885
|
}
|
|
1051
|
-
const codexConversationBindingRuntime = {
|
|
1052
|
-
startThread: startCodexConversationThread,
|
|
1053
|
-
handleInboundClaim: handleCodexConversationInboundClaim,
|
|
1054
|
-
handleBindingResolved: handleCodexConversationBindingResolved
|
|
1055
|
-
};
|
|
1056
|
-
//#endregion
|
|
1057
|
-
//#region extensions/codex/src/migration/apply-report.ts
|
|
1058
|
-
function codexPluginActivationReportState(result) {
|
|
1059
|
-
switch (result.reason) {
|
|
1060
|
-
case "already_active":
|
|
1061
|
-
case "installed": return {
|
|
1062
|
-
installed: true,
|
|
1063
|
-
enabled: true
|
|
1064
|
-
};
|
|
1065
|
-
case "auth_required": return {
|
|
1066
|
-
installed: true,
|
|
1067
|
-
enabled: false
|
|
1068
|
-
};
|
|
1069
|
-
case "disabled":
|
|
1070
|
-
case "marketplace_missing":
|
|
1071
|
-
case "plugin_missing": return {
|
|
1072
|
-
installed: false,
|
|
1073
|
-
enabled: false
|
|
1074
|
-
};
|
|
1075
|
-
case "refresh_failed": return {
|
|
1076
|
-
installed: true,
|
|
1077
|
-
enabled: false
|
|
1078
|
-
};
|
|
1079
|
-
}
|
|
1080
|
-
return result.reason;
|
|
1081
|
-
}
|
|
1082
|
-
function sanitizeAppsNeedingAuth(apps) {
|
|
1083
|
-
return apps.map((app) => ({
|
|
1084
|
-
id: app.id,
|
|
1085
|
-
name: app.name,
|
|
1086
|
-
needsAuth: true
|
|
1087
|
-
}));
|
|
1088
|
-
}
|
|
1089
886
|
//#endregion
|
|
1090
887
|
//#region extensions/codex/src/migration/helpers.ts
|
|
1091
888
|
async function exists(filePath) {
|
|
@@ -1139,6 +936,11 @@ async function readModelRefs(source) {
|
|
|
1139
936
|
refs.add(OPENAI_CODEX_DEFAULT_MODEL);
|
|
1140
937
|
return [...refs].toSorted();
|
|
1141
938
|
}
|
|
939
|
+
function readProviderAuthModelConfigs(result) {
|
|
940
|
+
const models = result.configPatch?.agents?.defaults?.models;
|
|
941
|
+
if (isRecord(models)) return { ...models };
|
|
942
|
+
return { [normalizeOptionalString(result.defaultModel) ?? OPENAI_CODEX_DEFAULT_MODEL]: {} };
|
|
943
|
+
}
|
|
1142
944
|
async function buildCodexOAuthCredential(source) {
|
|
1143
945
|
const credential = readCodexCliCredentialsCached({
|
|
1144
946
|
codexHome: source.codexHome,
|
|
@@ -1173,11 +975,13 @@ async function buildCodexOAuthCredential(source) {
|
|
|
1173
975
|
kind: "oauth",
|
|
1174
976
|
provider: OPENAI_PROVIDER_ID,
|
|
1175
977
|
profileId: profile.profileId,
|
|
1176
|
-
result
|
|
978
|
+
result,
|
|
979
|
+
modelConfigs: readProviderAuthModelConfigs(result)
|
|
1177
980
|
} : null;
|
|
1178
981
|
}
|
|
1179
982
|
async function buildCodexApiKeyCredential(source) {
|
|
1180
|
-
const
|
|
983
|
+
const raw = await readJsonObject(source.authPath);
|
|
984
|
+
const key = normalizeOptionalString(raw.OPENAI_API_KEY);
|
|
1181
985
|
if (!key) return null;
|
|
1182
986
|
return {
|
|
1183
987
|
kind: "api_key",
|
|
@@ -1231,7 +1035,7 @@ function hasAuthProfileConfigConflict(config, profile, overwrite) {
|
|
|
1231
1035
|
function hasCurrentAuthProfileConfigConflict(ctx, profile) {
|
|
1232
1036
|
let config = ctx.config;
|
|
1233
1037
|
try {
|
|
1234
|
-
config =
|
|
1038
|
+
config = ctx.runtime?.config?.current?.() ?? config;
|
|
1235
1039
|
} catch {}
|
|
1236
1040
|
return hasAuthProfileConfigConflict(config, profile, Boolean(ctx.overwrite));
|
|
1237
1041
|
}
|
|
@@ -1252,8 +1056,30 @@ function applyDefaultModelIfMissing(cfg) {
|
|
|
1252
1056
|
}
|
|
1253
1057
|
};
|
|
1254
1058
|
}
|
|
1059
|
+
function mergeModelConfigEntry(existing, patch) {
|
|
1060
|
+
if (existing && isRecord(existing) && isRecord(patch)) return {
|
|
1061
|
+
...existing,
|
|
1062
|
+
...patch
|
|
1063
|
+
};
|
|
1064
|
+
return existing ?? patch;
|
|
1065
|
+
}
|
|
1066
|
+
function applyOAuthModelConfigsToConfig(cfg, credential) {
|
|
1067
|
+
const existingModels = cfg.agents?.defaults?.models ?? {};
|
|
1068
|
+
const models = credential.result.replaceDefaultModels ? { ...credential.modelConfigs } : { ...existingModels };
|
|
1069
|
+
if (!credential.result.replaceDefaultModels) for (const [modelRef, modelConfig] of Object.entries(credential.modelConfigs)) models[modelRef] = mergeModelConfigEntry(models[modelRef], modelConfig);
|
|
1070
|
+
return {
|
|
1071
|
+
...cfg,
|
|
1072
|
+
agents: {
|
|
1073
|
+
...cfg.agents,
|
|
1074
|
+
defaults: {
|
|
1075
|
+
...cfg.agents?.defaults,
|
|
1076
|
+
models
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1255
1081
|
function applyOAuthConfigToConfig(cfg, credential, profileId) {
|
|
1256
|
-
let next =
|
|
1082
|
+
let next = applyOAuthModelConfigsToConfig(cfg, credential);
|
|
1257
1083
|
const profile = credential.result.profiles[0];
|
|
1258
1084
|
if (profile) next = applyAuthProfileConfig(next, {
|
|
1259
1085
|
profileId,
|
|
@@ -1277,25 +1103,30 @@ function applyApiKeyConfigToConfig(cfg, credential, profileId) {
|
|
|
1277
1103
|
function shouldReturnAuthConfigPatch(ctx) {
|
|
1278
1104
|
return ctx.providerOptions?.configPatchMode === CODEX_CONFIG_PATCH_MODE_RETURN$1;
|
|
1279
1105
|
}
|
|
1280
|
-
function
|
|
1281
|
-
if (credential.kind === "api_key") return {
|
|
1282
|
-
profileId,
|
|
1283
|
-
provider: credential.provider,
|
|
1284
|
-
mode: "api_key",
|
|
1285
|
-
displayName: CODEX_IMPORT_DISPLAY_NAME
|
|
1286
|
-
};
|
|
1106
|
+
function oauthAuthProfileConfig(credential, profileId) {
|
|
1287
1107
|
const profile = credential.result.profiles[0];
|
|
1288
1108
|
if (!profile || profile.credential.type !== "oauth") return null;
|
|
1289
1109
|
return {
|
|
1290
1110
|
profileId,
|
|
1291
1111
|
provider: profile.credential.provider,
|
|
1292
1112
|
mode: "oauth",
|
|
1293
|
-
...profile.credential.email ? { email: profile.credential.email } : {},
|
|
1294
|
-
...profile.credential.displayName ? { displayName: profile.credential.displayName } : {}
|
|
1113
|
+
..."email" in profile.credential && profile.credential.email ? { email: profile.credential.email } : {},
|
|
1114
|
+
..."displayName" in profile.credential && profile.credential.displayName ? { displayName: profile.credential.displayName } : {}
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
function apiKeyAuthProfileConfig(credential, profileId) {
|
|
1118
|
+
return {
|
|
1119
|
+
profileId,
|
|
1120
|
+
provider: credential.provider,
|
|
1121
|
+
mode: "api_key",
|
|
1122
|
+
displayName: CODEX_IMPORT_DISPLAY_NAME
|
|
1295
1123
|
};
|
|
1296
1124
|
}
|
|
1125
|
+
function authProfileConfigForCredential(credential, profileId) {
|
|
1126
|
+
return credential.kind === "oauth" ? oauthAuthProfileConfig(credential, profileId) : apiKeyAuthProfileConfig(credential, profileId);
|
|
1127
|
+
}
|
|
1297
1128
|
async function applyCodexAuthProfileConfig(ctx, profile, applyConfig) {
|
|
1298
|
-
const configApi =
|
|
1129
|
+
const configApi = ctx.runtime?.config;
|
|
1299
1130
|
if (!configApi?.current || !configApi.mutateConfigFile) return "unavailable";
|
|
1300
1131
|
try {
|
|
1301
1132
|
await configApi.mutateConfigFile({
|
|
@@ -1312,13 +1143,13 @@ async function applyCodexAuthProfileConfig(ctx, profile, applyConfig) {
|
|
|
1312
1143
|
return error instanceof CodexAuthConfigConflict ? "conflict" : "unavailable";
|
|
1313
1144
|
}
|
|
1314
1145
|
}
|
|
1315
|
-
async function
|
|
1316
|
-
const profile =
|
|
1146
|
+
async function applyOAuthConfig(ctx, credential, profileId) {
|
|
1147
|
+
const profile = oauthAuthProfileConfig(credential, profileId);
|
|
1317
1148
|
if (!profile) return "unavailable";
|
|
1318
|
-
return applyCodexAuthProfileConfig(ctx, profile, (config) =>
|
|
1149
|
+
return applyCodexAuthProfileConfig(ctx, profile, (config) => applyOAuthConfigToConfig(config, credential, profileId));
|
|
1319
1150
|
}
|
|
1320
|
-
function
|
|
1321
|
-
return
|
|
1151
|
+
async function applyApiKeyConfig(ctx, credential, profileId) {
|
|
1152
|
+
return applyCodexAuthProfileConfig(ctx, apiKeyAuthProfileConfig(credential, profileId), (config) => applyApiKeyConfigToConfig(config, credential, profileId));
|
|
1322
1153
|
}
|
|
1323
1154
|
async function buildCodexAuthItems(params) {
|
|
1324
1155
|
const credentials = await readCodexAuthCredentials(params.source);
|
|
@@ -1351,27 +1182,26 @@ async function buildCodexAuthItems(params) {
|
|
|
1351
1182
|
});
|
|
1352
1183
|
});
|
|
1353
1184
|
}
|
|
1354
|
-
async function
|
|
1185
|
+
async function applyCodexAuthItem(params) {
|
|
1355
1186
|
const { ctx, item, source, targets } = params;
|
|
1356
|
-
if (item.status !== "planned") return
|
|
1187
|
+
if (item.status !== "planned") return item;
|
|
1357
1188
|
const profileId = typeof item.details?.profileId === "string" ? item.details.profileId : "";
|
|
1358
1189
|
const provider = typeof item.details?.provider === "string" ? item.details.provider : "";
|
|
1359
1190
|
const sourceProfileId = typeof item.details?.sourceProfileId === "string" ? item.details.sourceProfileId : void 0;
|
|
1360
|
-
if (!profileId || !provider) return
|
|
1191
|
+
if (!profileId || !provider) return markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA);
|
|
1361
1192
|
const credential = (await readCodexAuthCredentials(source)).find((candidate) => candidate.provider === provider);
|
|
1362
|
-
if (!credential) return
|
|
1363
|
-
if (credential.kind === "oauth" && sourceProfileId && credential.profileId !== sourceProfileId) return
|
|
1193
|
+
if (!credential) return markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT);
|
|
1194
|
+
if (credential.kind === "oauth" && sourceProfileId && credential.profileId !== sourceProfileId) return markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT);
|
|
1364
1195
|
const oauthProfile = credential.kind === "oauth" ? credential.result.profiles[0] : void 0;
|
|
1365
1196
|
const oauthCredential = oauthProfile?.credential.type === "oauth" ? oauthProfile.credential : void 0;
|
|
1366
|
-
if (credential.kind === "oauth" && !oauthCredential) return
|
|
1197
|
+
if (credential.kind === "oauth" && !oauthCredential) return markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA);
|
|
1367
1198
|
const configProfile = authProfileConfigForCredential(credential, profileId);
|
|
1368
|
-
if (!configProfile) return
|
|
1369
|
-
if (hasCurrentAuthProfileConfigConflict(ctx, configProfile)) return
|
|
1199
|
+
if (!configProfile) return markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA);
|
|
1200
|
+
if (hasCurrentAuthProfileConfigConflict(ctx, configProfile)) return markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS);
|
|
1370
1201
|
let conflicted = false;
|
|
1371
1202
|
let wrote = false;
|
|
1372
1203
|
const store = await updateAuthProfileStoreWithLock({
|
|
1373
1204
|
agentDir: targets.agentDir,
|
|
1374
|
-
stateDir: ctx.stateDir,
|
|
1375
1205
|
updater: (freshStore) => {
|
|
1376
1206
|
const existing = freshStore.profiles[profileId];
|
|
1377
1207
|
if (!ctx.overwrite && existing) {
|
|
@@ -1390,11 +1220,11 @@ async function applyCodexAuthItems(params) {
|
|
|
1390
1220
|
return true;
|
|
1391
1221
|
}
|
|
1392
1222
|
});
|
|
1393
|
-
if (conflicted) return
|
|
1394
|
-
if (!store?.profiles[profileId]) return
|
|
1395
|
-
const configResult = shouldReturnAuthConfigPatch(ctx) ? "unavailable" : await
|
|
1396
|
-
if (configResult === "conflict") return
|
|
1397
|
-
|
|
1223
|
+
if (conflicted) return markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS);
|
|
1224
|
+
if (!store?.profiles[profileId]) return markMigrationItemError(item, CODEX_REASON_AUTH_PROFILE_WRITE_FAILED);
|
|
1225
|
+
const configResult = shouldReturnAuthConfigPatch(ctx) ? "unavailable" : credential.kind === "oauth" ? await applyOAuthConfig(ctx, credential, profileId) : await applyApiKeyConfig(ctx, credential, profileId);
|
|
1226
|
+
if (configResult === "conflict") return markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS);
|
|
1227
|
+
return {
|
|
1398
1228
|
...item,
|
|
1399
1229
|
status: "migrated",
|
|
1400
1230
|
details: {
|
|
@@ -1404,10 +1234,18 @@ async function applyCodexAuthItems(params) {
|
|
|
1404
1234
|
...shouldReturnAuthConfigPatch(ctx) ? { configPatchReturned: true } : {}
|
|
1405
1235
|
}
|
|
1406
1236
|
};
|
|
1407
|
-
return [migratedItem, ...shouldReturnAuthConfigPatch(ctx) ? buildCodexAuthConfigPatchItems(ctx, migratedItem, credential, profileId) : []];
|
|
1408
1237
|
}
|
|
1409
|
-
function buildCodexAuthConfigPatchItems(
|
|
1410
|
-
const
|
|
1238
|
+
async function buildCodexAuthConfigPatchItems(params) {
|
|
1239
|
+
const { ctx, item, source } = params;
|
|
1240
|
+
if (item.status !== "migrated" || !shouldReturnAuthConfigPatch(ctx)) return [];
|
|
1241
|
+
const profileId = typeof item.details?.profileId === "string" ? item.details.profileId : "";
|
|
1242
|
+
const provider = typeof item.details?.provider === "string" ? item.details.provider : "";
|
|
1243
|
+
const sourceProfileId = typeof item.details?.sourceProfileId === "string" ? item.details.sourceProfileId : void 0;
|
|
1244
|
+
if (!profileId || !provider) return [];
|
|
1245
|
+
const credential = (await readCodexAuthCredentials(source)).find((candidate) => candidate.provider === provider);
|
|
1246
|
+
if (!credential) return [];
|
|
1247
|
+
if (credential.kind === "oauth" && sourceProfileId && credential.profileId !== sourceProfileId) return [];
|
|
1248
|
+
const next = credential.kind === "oauth" ? applyOAuthConfigToConfig(ctx.config, credential, profileId) : applyApiKeyConfigToConfig(ctx.config, credential, profileId);
|
|
1411
1249
|
const items = [];
|
|
1412
1250
|
if (next.auth) items.push(createMigrationItem({
|
|
1413
1251
|
id: `${item.id}:config:auth`,
|
|
@@ -1436,10 +1274,16 @@ function buildCodexAuthConfigPatchItems(ctx, item, credential, profileId) {
|
|
|
1436
1274
|
return items;
|
|
1437
1275
|
}
|
|
1438
1276
|
//#endregion
|
|
1439
|
-
//#region extensions/codex/src/migration/source
|
|
1277
|
+
//#region extensions/codex/src/migration/source.ts
|
|
1440
1278
|
const SKILL_FILENAME = "SKILL.md";
|
|
1441
1279
|
const MAX_SCAN_DEPTH = 6;
|
|
1442
1280
|
const MAX_DISCOVERED_DIRS = 2e3;
|
|
1281
|
+
function defaultCodexHome() {
|
|
1282
|
+
return resolveHomePath(process.env.CODEX_HOME?.trim() || "~/.codex");
|
|
1283
|
+
}
|
|
1284
|
+
function personalAgentsSkillsDir() {
|
|
1285
|
+
return path.join(resolveUserHomeDir(), ".agents", "skills");
|
|
1286
|
+
}
|
|
1443
1287
|
async function safeReadDir(dir) {
|
|
1444
1288
|
return await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
1445
1289
|
}
|
|
@@ -1458,7 +1302,10 @@ async function discoverSkillDirs(params) {
|
|
|
1458
1302
|
});
|
|
1459
1303
|
return;
|
|
1460
1304
|
}
|
|
1461
|
-
for (const entry of await safeReadDir(dir))
|
|
1305
|
+
for (const entry of await safeReadDir(dir)) {
|
|
1306
|
+
if (!entry.isDirectory()) continue;
|
|
1307
|
+
await visit(path.join(dir, entry.name), depth + 1);
|
|
1308
|
+
}
|
|
1462
1309
|
}
|
|
1463
1310
|
await visit(params.root, 0);
|
|
1464
1311
|
return discovered;
|
|
@@ -1472,86 +1319,43 @@ async function discoverPluginDirs(codexHome) {
|
|
|
1472
1319
|
const manifestPath = path.join(dir, ".codex-plugin", "plugin.json");
|
|
1473
1320
|
if (await exists(manifestPath)) {
|
|
1474
1321
|
const manifest = await readJsonObject(manifestPath);
|
|
1475
|
-
const
|
|
1322
|
+
const name = (typeof manifest.name === "string" ? manifest.name.trim() : "") || path.basename(dir);
|
|
1476
1323
|
discovered.set(dir, {
|
|
1477
|
-
name
|
|
1324
|
+
name,
|
|
1478
1325
|
source: dir,
|
|
1326
|
+
manifestPath,
|
|
1327
|
+
sourceKind: "cache",
|
|
1479
1328
|
migratable: false,
|
|
1480
1329
|
message: "Cached Codex plugin bundle found. Review manually unless the plugin is also installed in the source Codex app-server inventory"
|
|
1481
1330
|
});
|
|
1482
1331
|
return;
|
|
1483
1332
|
}
|
|
1484
|
-
for (const entry of await safeReadDir(dir))
|
|
1333
|
+
for (const entry of await safeReadDir(dir)) {
|
|
1334
|
+
if (!entry.isDirectory()) continue;
|
|
1335
|
+
await visit(path.join(dir, entry.name), depth + 1);
|
|
1336
|
+
}
|
|
1485
1337
|
}
|
|
1486
1338
|
await visit(root, 0);
|
|
1487
1339
|
return [...discovered.values()].toSorted((a, b) => a.source.localeCompare(b.source));
|
|
1488
1340
|
}
|
|
1489
|
-
async function discoverCodexMemoryFile(candidate) {
|
|
1490
|
-
try {
|
|
1491
|
-
const stat = await fs.lstat(candidate.path);
|
|
1492
|
-
if (stat.isSymbolicLink()) throw new Error(`Codex memory source must not be a symbolic link: ${candidate.path}`);
|
|
1493
|
-
if (!stat.isFile()) throw new Error(`Codex memory source must be a regular file: ${candidate.path}`);
|
|
1494
|
-
return candidate;
|
|
1495
|
-
} catch (error) {
|
|
1496
|
-
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return;
|
|
1497
|
-
throw error;
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
async function discoverCodexMemorySources(codexHome) {
|
|
1501
|
-
const memoriesDir = path.join(codexHome, "memories");
|
|
1502
|
-
return (await Promise.all([{
|
|
1503
|
-
id: "memory:codex:MEMORY.md",
|
|
1504
|
-
label: "Codex consolidated memory",
|
|
1505
|
-
name: "MEMORY.md"
|
|
1506
|
-
}, {
|
|
1507
|
-
id: "memory:codex:memory_summary.md",
|
|
1508
|
-
label: "Codex memory summary",
|
|
1509
|
-
name: "memory_summary.md"
|
|
1510
|
-
}].map(async (candidate) => await discoverCodexMemoryFile({
|
|
1511
|
-
id: candidate.id,
|
|
1512
|
-
label: candidate.label,
|
|
1513
|
-
path: path.join(memoriesDir, candidate.name)
|
|
1514
|
-
})))).filter((entry) => entry !== void 0);
|
|
1515
|
-
}
|
|
1516
|
-
//#endregion
|
|
1517
|
-
//#region extensions/codex/src/migration/source.ts
|
|
1518
|
-
function defaultCodexHome() {
|
|
1519
|
-
const configuredHome = process.env.CODEX_HOME;
|
|
1520
|
-
return resolveHomePath(configuredHome !== void 0 && configuredHome.length > 0 ? configuredHome : "~/.codex");
|
|
1521
|
-
}
|
|
1522
|
-
function personalAgentsSkillsDir() {
|
|
1523
|
-
return path.join(resolveUserHomeDir(), ".agents", "skills");
|
|
1524
|
-
}
|
|
1525
1341
|
async function discoverInstalledCuratedPlugins(codexHome, options = {}) {
|
|
1526
|
-
const
|
|
1342
|
+
const requestOptions = { startOptions: sourceCodexAppServerStartOptions(codexHome) };
|
|
1527
1343
|
try {
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
const curatedMarketplaceErrors = response.marketplaceLoadErrors.filter((error) => isPathInside(curatedSyncRoot, error.marketplacePath));
|
|
1544
|
-
if (curatedMarketplaceErrors.length > 0) return {
|
|
1545
|
-
plugins: [],
|
|
1546
|
-
error: curatedMarketplaceErrors.map((error) => error.message).join("; ")
|
|
1547
|
-
};
|
|
1548
|
-
const installed = discoverInstalledCuratedPluginSources(response);
|
|
1549
|
-
return { plugins: (options.evaluatePluginMigrationEligibility === true ? await withPluginMigrationEligibility({
|
|
1550
|
-
plugins: installed,
|
|
1551
|
-
requestOptions,
|
|
1552
|
-
verifyPluginApps: options.verifyPluginApps === true
|
|
1553
|
-
}) : installed.map(({ plugin }) => plugin)).toSorted((left, right) => (left.pluginName ?? left.name).localeCompare(right.pluginName ?? right.name)) };
|
|
1554
|
-
});
|
|
1344
|
+
const marketplace = (await requestSourceCodexAppServerJson(requestOptions, {
|
|
1345
|
+
method: "plugin/list",
|
|
1346
|
+
requestParams: { cwds: [] }
|
|
1347
|
+
})).marketplaces.find((entry) => entry.name === CODEX_PLUGINS_MARKETPLACE_NAME);
|
|
1348
|
+
if (!marketplace) return {
|
|
1349
|
+
plugins: [],
|
|
1350
|
+
error: `Codex marketplace ${CODEX_PLUGINS_MARKETPLACE_NAME} was not found in source plugin inventory.`
|
|
1351
|
+
};
|
|
1352
|
+
const plugins = marketplace.plugins.filter((plugin) => plugin.installed).map((plugin) => buildInstalledPluginSource(plugin)).filter((plugin) => plugin !== void 0);
|
|
1353
|
+
return { plugins: (options.evaluatePluginMigrationEligibility === true ? await withPluginMigrationEligibility({
|
|
1354
|
+
plugins,
|
|
1355
|
+
marketplace: marketplaceRef(marketplace),
|
|
1356
|
+
requestOptions,
|
|
1357
|
+
verifyPluginApps: options.verifyPluginApps === true
|
|
1358
|
+
}) : plugins).toSorted((a, b) => (a.pluginName ?? a.name).localeCompare(b.pluginName ?? b.name)) };
|
|
1555
1359
|
} catch (error) {
|
|
1556
1360
|
return {
|
|
1557
1361
|
plugins: [],
|
|
@@ -1564,7 +1368,6 @@ function sourceCodexAppServerStartOptions(codexHome) {
|
|
|
1564
1368
|
transport: "stdio",
|
|
1565
1369
|
command: "codex",
|
|
1566
1370
|
commandSource: "managed",
|
|
1567
|
-
managedCommandOrder: "desktop-first",
|
|
1568
1371
|
args: [
|
|
1569
1372
|
"app-server",
|
|
1570
1373
|
"--listen",
|
|
@@ -1577,6 +1380,16 @@ function sourceCodexAppServerStartOptions(codexHome) {
|
|
|
1577
1380
|
}
|
|
1578
1381
|
};
|
|
1579
1382
|
}
|
|
1383
|
+
async function requestSourceCodexAppServerJson(options, params) {
|
|
1384
|
+
return await requestCodexAppServerJson({
|
|
1385
|
+
method: params.method,
|
|
1386
|
+
requestParams: params.requestParams,
|
|
1387
|
+
timeoutMs: 6e4,
|
|
1388
|
+
startOptions: options.startOptions,
|
|
1389
|
+
authProfileId: null,
|
|
1390
|
+
isolated: true
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1580
1393
|
function buildInstalledPluginSource(plugin) {
|
|
1581
1394
|
const pluginName = pluginNameFromSummary(plugin);
|
|
1582
1395
|
if (!pluginName) return;
|
|
@@ -1585,32 +1398,12 @@ function buildInstalledPluginSource(plugin) {
|
|
|
1585
1398
|
pluginName,
|
|
1586
1399
|
marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
|
|
1587
1400
|
source: `${CODEX_PLUGINS_MARKETPLACE_NAME}/${pluginName}`,
|
|
1401
|
+
sourceKind: "app-server",
|
|
1588
1402
|
migratable: true,
|
|
1589
1403
|
installed: plugin.installed,
|
|
1590
1404
|
enabled: plugin.enabled
|
|
1591
1405
|
};
|
|
1592
1406
|
}
|
|
1593
|
-
function discoverInstalledCuratedPluginSources(response) {
|
|
1594
|
-
const installedByName = /* @__PURE__ */ new Map();
|
|
1595
|
-
for (const marketplace of response.marketplaces) {
|
|
1596
|
-
if (!isOpenAiCuratedMarketplace(marketplace)) continue;
|
|
1597
|
-
const remote = !marketplace.path;
|
|
1598
|
-
for (const summary of marketplace.plugins) {
|
|
1599
|
-
if (!summary.installed) continue;
|
|
1600
|
-
const plugin = buildInstalledPluginSource(summary);
|
|
1601
|
-
if (!plugin?.pluginName) continue;
|
|
1602
|
-
const existing = installedByName.get(plugin.pluginName);
|
|
1603
|
-
if (existing && (!remote || existing.remote)) continue;
|
|
1604
|
-
installedByName.set(plugin.pluginName, {
|
|
1605
|
-
plugin,
|
|
1606
|
-
marketplace: marketplaceRef(marketplace),
|
|
1607
|
-
...remote ? { readPluginName: summary.remotePluginId?.trim() || void 0 } : { readPluginName: plugin.pluginName },
|
|
1608
|
-
remote
|
|
1609
|
-
});
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
return Array.from(installedByName.values());
|
|
1613
|
-
}
|
|
1614
1407
|
function marketplaceRef(marketplace) {
|
|
1615
1408
|
return {
|
|
1616
1409
|
name: CODEX_PLUGINS_MARKETPLACE_NAME,
|
|
@@ -1621,7 +1414,7 @@ function marketplaceRef(marketplace) {
|
|
|
1621
1414
|
async function withPluginMigrationEligibility(params) {
|
|
1622
1415
|
const pending = [];
|
|
1623
1416
|
const evaluated = [];
|
|
1624
|
-
for (const
|
|
1417
|
+
for (const plugin of params.plugins) {
|
|
1625
1418
|
if (plugin.enabled !== true) {
|
|
1626
1419
|
evaluated.push({
|
|
1627
1420
|
...plugin,
|
|
@@ -1631,7 +1424,7 @@ async function withPluginMigrationEligibility(params) {
|
|
|
1631
1424
|
});
|
|
1632
1425
|
continue;
|
|
1633
1426
|
}
|
|
1634
|
-
const detail = await readPluginDetail(params.requestOptions, marketplace, plugin
|
|
1427
|
+
const detail = await readPluginDetail(params.requestOptions, params.marketplace, plugin);
|
|
1635
1428
|
if (!detail.ok) {
|
|
1636
1429
|
evaluated.push({
|
|
1637
1430
|
...plugin,
|
|
@@ -1659,27 +1452,25 @@ async function withPluginMigrationEligibility(params) {
|
|
|
1659
1452
|
}
|
|
1660
1453
|
if (pending.length === 0) return evaluated;
|
|
1661
1454
|
let sourceAccount;
|
|
1662
|
-
let sourceAccountError;
|
|
1663
1455
|
try {
|
|
1664
1456
|
sourceAccount = await readSourceCodexAccount(params.requestOptions);
|
|
1665
|
-
if (sourceAccount === "missing") sourceAccountError = "Codex app-server did not report an authenticated source account.";
|
|
1666
1457
|
} catch (error) {
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1458
|
+
if (!params.verifyPluginApps) {
|
|
1459
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1460
|
+
for (const { plugin, apps } of pending) evaluated.push({
|
|
1461
|
+
...plugin,
|
|
1462
|
+
migratable: false,
|
|
1463
|
+
migrationBlock: {
|
|
1464
|
+
code: "codex_account_unavailable",
|
|
1465
|
+
apps,
|
|
1466
|
+
error: message
|
|
1467
|
+
},
|
|
1468
|
+
message: `Codex plugin "${plugin.pluginName ?? plugin.name}" owns apps, but the source Codex app-server account could not be read: ${message}`
|
|
1469
|
+
});
|
|
1470
|
+
return evaluated;
|
|
1471
|
+
}
|
|
1681
1472
|
}
|
|
1682
|
-
if (sourceAccount
|
|
1473
|
+
if (sourceAccount && sourceAccount !== "chatgpt") {
|
|
1683
1474
|
for (const { plugin, apps } of pending) evaluated.push({
|
|
1684
1475
|
...plugin,
|
|
1685
1476
|
migratable: false,
|
|
@@ -1714,9 +1505,8 @@ async function withPluginMigrationEligibility(params) {
|
|
|
1714
1505
|
});
|
|
1715
1506
|
if (!snapshot) return evaluated;
|
|
1716
1507
|
const appInfoById = new Map(snapshot.apps.map((app) => [app.id, app]));
|
|
1717
|
-
const installedAppsById = new Map(snapshot.installedApps.map((app) => [app.id, app]));
|
|
1718
1508
|
for (const { plugin, apps: declaredApps } of pending) {
|
|
1719
|
-
const apps = declaredApps.map((app) => sourcePluginAppFactWithInventory(app, appInfoById.get(app.id)
|
|
1509
|
+
const apps = declaredApps.map((app) => sourcePluginAppFactWithInventory(app, appInfoById.get(app.id))).toSorted((left, right) => left.id.localeCompare(right.id));
|
|
1720
1510
|
const blockCode = migrationBlockCodeForApps(apps);
|
|
1721
1511
|
if (!blockCode) {
|
|
1722
1512
|
evaluated.push({
|
|
@@ -1739,29 +1529,20 @@ async function withPluginMigrationEligibility(params) {
|
|
|
1739
1529
|
return evaluated;
|
|
1740
1530
|
}
|
|
1741
1531
|
async function readSourceCodexAccount(options) {
|
|
1742
|
-
const response = await options
|
|
1532
|
+
const response = await requestSourceCodexAppServerJson(options, {
|
|
1743
1533
|
method: "account/read",
|
|
1744
1534
|
requestParams: { refreshToken: false }
|
|
1745
1535
|
});
|
|
1746
1536
|
if (!response.account || typeof response.account !== "object" || Array.isArray(response.account)) return "missing";
|
|
1747
|
-
|
|
1748
|
-
case "chatgpt": return "chatgpt";
|
|
1749
|
-
case "apiKey":
|
|
1750
|
-
case "amazonBedrock": return "non_chatgpt";
|
|
1751
|
-
default: return "missing";
|
|
1752
|
-
}
|
|
1537
|
+
return response.account.type === "chatgpt" ? "chatgpt" : "non_chatgpt";
|
|
1753
1538
|
}
|
|
1754
|
-
async function readPluginDetail(options, marketplace, plugin
|
|
1755
|
-
if (!readPluginName) return {
|
|
1756
|
-
ok: false,
|
|
1757
|
-
error: `Codex remote plugin "${plugin.pluginName ?? plugin.name}" has no readable remote plugin id.`
|
|
1758
|
-
};
|
|
1539
|
+
async function readPluginDetail(options, marketplace, plugin) {
|
|
1759
1540
|
try {
|
|
1760
1541
|
return {
|
|
1761
1542
|
ok: true,
|
|
1762
|
-
detail: (await options
|
|
1543
|
+
detail: (await requestSourceCodexAppServerJson(options, {
|
|
1763
1544
|
method: "plugin/read",
|
|
1764
|
-
requestParams: pluginReadParams(marketplace,
|
|
1545
|
+
requestParams: pluginReadParams(marketplace, plugin.pluginName ?? plugin.name)
|
|
1765
1546
|
})).plugin
|
|
1766
1547
|
};
|
|
1767
1548
|
} catch (error) {
|
|
@@ -1773,7 +1554,7 @@ async function readPluginDetail(options, marketplace, plugin, readPluginName) {
|
|
|
1773
1554
|
}
|
|
1774
1555
|
async function refreshSourceAppInventory(options) {
|
|
1775
1556
|
const key = buildCodexPluginAppCacheKey({ appServer: { start: options.startOptions } });
|
|
1776
|
-
const request = async (method, requestParams) => await options
|
|
1557
|
+
const request = async (method, requestParams) => await requestSourceCodexAppServerJson(options, {
|
|
1777
1558
|
method,
|
|
1778
1559
|
requestParams
|
|
1779
1560
|
});
|
|
@@ -1786,29 +1567,16 @@ async function refreshSourceAppInventory(options) {
|
|
|
1786
1567
|
function sourcePluginAppFact(app) {
|
|
1787
1568
|
return {
|
|
1788
1569
|
id: app.id,
|
|
1789
|
-
name: app.name
|
|
1570
|
+
name: app.name,
|
|
1571
|
+
needsAuth: app.needsAuth
|
|
1790
1572
|
};
|
|
1791
1573
|
}
|
|
1792
|
-
function sourcePluginAppFactWithInventory(app, info
|
|
1793
|
-
if (!
|
|
1794
|
-
if (!info) return installedApp.enabled ? {
|
|
1795
|
-
...app,
|
|
1796
|
-
isAccessible: false,
|
|
1797
|
-
isEnabled: true
|
|
1798
|
-
} : {
|
|
1799
|
-
...app,
|
|
1800
|
-
isEnabled: false
|
|
1801
|
-
};
|
|
1802
|
-
if (!installedApp.enabled) return {
|
|
1803
|
-
...app,
|
|
1804
|
-
isAccessible: info.isAccessible,
|
|
1805
|
-
isEnabled: false
|
|
1806
|
-
};
|
|
1574
|
+
function sourcePluginAppFactWithInventory(app, info) {
|
|
1575
|
+
if (!info) return app;
|
|
1807
1576
|
return {
|
|
1808
1577
|
...app,
|
|
1809
|
-
isAccessible: info.isAccessible
|
|
1810
|
-
isEnabled: info.isEnabled
|
|
1811
|
-
...!installedApp.callable ? { isCallable: false } : {}
|
|
1578
|
+
isAccessible: info.isAccessible,
|
|
1579
|
+
isEnabled: info.isEnabled
|
|
1812
1580
|
};
|
|
1813
1581
|
}
|
|
1814
1582
|
function migrationBlockCodeForApps(apps) {
|
|
@@ -1817,7 +1585,7 @@ function migrationBlockCodeForApps(apps) {
|
|
|
1817
1585
|
if (apps.some((app) => app.isAccessible === void 0 || app.isEnabled === void 0)) return "app_missing";
|
|
1818
1586
|
}
|
|
1819
1587
|
function appInventoryBlockMessage(plugin, apps, code) {
|
|
1820
|
-
const status = code === "app_inaccessible" ?
|
|
1588
|
+
const status = code === "app_inaccessible" ? "inaccessible" : code === "app_disabled" ? "disabled" : "missing";
|
|
1821
1589
|
const blocking = apps.find((app) => code === "app_inaccessible" ? app.isAccessible === false : code === "app_disabled" ? app.isEnabled === false : app.isAccessible === void 0 || app.isEnabled === void 0) ?? apps[0];
|
|
1822
1590
|
const appLabel = blocking ? ` app "${blocking.name}"` : " an owned app";
|
|
1823
1591
|
return `Codex plugin "${plugin.pluginName ?? plugin.name}" owns${appLabel} but the source app inventory reports it is ${status}; authenticate or enable the app in Codex before migrating it to OpenClaw.`;
|
|
@@ -1829,20 +1597,16 @@ function codexSubscriptionRequiredMessage(plugin) {
|
|
|
1829
1597
|
return `Codex plugin "${plugin.pluginName ?? plugin.name}" owns apps, but ${codexPluginMigrationSubscriptionWarning()}`;
|
|
1830
1598
|
}
|
|
1831
1599
|
function pluginNameFromSummary(summary) {
|
|
1832
|
-
const candidates = [summary.
|
|
1600
|
+
const candidates = [summary.id, summary.name];
|
|
1833
1601
|
for (const candidate of candidates) {
|
|
1834
1602
|
const trimmed = candidate.trim();
|
|
1835
1603
|
if (!trimmed) continue;
|
|
1836
|
-
const
|
|
1837
|
-
`@${CODEX_PLUGINS_MARKETPLACE_NAME}-remote`,
|
|
1838
|
-
`@openai-api-curated`,
|
|
1839
|
-
`@${CODEX_PLUGINS_MARKETPLACE_NAME}`
|
|
1840
|
-
].find((suffix) => trimmed.endsWith(suffix));
|
|
1841
|
-
const normalized = ((marketplaceSuffix ? trimmed.slice(0, -marketplaceSuffix.length) : trimmed).split("/").at(-1)?.trim())?.toLowerCase().replaceAll(/\s+/gu, "-");
|
|
1604
|
+
const normalized = ((trimmed.endsWith(`@openai-curated`) ? trimmed.slice(0, -`@${CODEX_PLUGINS_MARKETPLACE_NAME}`.length) : trimmed).split("/").at(-1)?.trim())?.toLowerCase().replaceAll(/\s+/gu, "-");
|
|
1842
1605
|
if (normalized) return normalized;
|
|
1843
1606
|
}
|
|
1844
1607
|
}
|
|
1845
|
-
async function discoverCodexSource(
|
|
1608
|
+
async function discoverCodexSource(inputOrOptions) {
|
|
1609
|
+
const options = typeof inputOrOptions === "string" || inputOrOptions === void 0 ? { input: inputOrOptions } : inputOrOptions;
|
|
1846
1610
|
const codexHome = resolveHomePath(options.input?.trim() || defaultCodexHome());
|
|
1847
1611
|
const codexSkillsDir = path.join(codexHome, "skills");
|
|
1848
1612
|
const agentsSkillsDir = personalAgentsSkillsDir();
|
|
@@ -1850,39 +1614,38 @@ async function discoverCodexSource(options = {}) {
|
|
|
1850
1614
|
const authPath = path.join(codexHome, "auth.json");
|
|
1851
1615
|
const modelsCachePath = path.join(codexHome, "models_cache.json");
|
|
1852
1616
|
const hooksPath = path.join(codexHome, "hooks", "hooks.json");
|
|
1853
|
-
const
|
|
1854
|
-
const codexSkills = options.memoryOnly ? [] : await discoverSkillDirs({
|
|
1617
|
+
const codexSkills = await discoverSkillDirs({
|
|
1855
1618
|
root: codexSkillsDir,
|
|
1856
1619
|
sourceLabel: "Codex skill",
|
|
1857
1620
|
excludeSystem: true
|
|
1858
1621
|
});
|
|
1859
|
-
const personalAgentSkills =
|
|
1622
|
+
const personalAgentSkills = await discoverSkillDirs({
|
|
1860
1623
|
root: agentsSkillsDir,
|
|
1861
1624
|
sourceLabel: "personal AgentSkill"
|
|
1862
1625
|
});
|
|
1863
|
-
const sourcePluginDiscovery =
|
|
1626
|
+
const sourcePluginDiscovery = await discoverInstalledCuratedPlugins(codexHome, options);
|
|
1864
1627
|
const sourcePluginNames = new Set(sourcePluginDiscovery.plugins.flatMap((plugin) => plugin.pluginName ? [plugin.pluginName] : []));
|
|
1865
|
-
const cachedPlugins = (
|
|
1628
|
+
const cachedPlugins = (await discoverPluginDirs(codexHome)).filter((plugin) => {
|
|
1866
1629
|
const normalizedName = sanitizePluginName(plugin.name);
|
|
1867
1630
|
return !sourcePluginNames.has(normalizedName);
|
|
1868
1631
|
});
|
|
1869
1632
|
const plugins = [...sourcePluginDiscovery.plugins, ...cachedPlugins].toSorted((a, b) => a.source.localeCompare(b.source));
|
|
1870
1633
|
const archivePaths = [];
|
|
1871
|
-
if (
|
|
1634
|
+
if (await exists(configPath)) archivePaths.push({
|
|
1872
1635
|
id: "archive:config.toml",
|
|
1873
1636
|
path: configPath,
|
|
1874
1637
|
relativePath: "config.toml",
|
|
1875
1638
|
message: "Codex config is archived for manual review; it is not activated automatically"
|
|
1876
1639
|
});
|
|
1877
|
-
if (
|
|
1640
|
+
if (await exists(hooksPath)) archivePaths.push({
|
|
1878
1641
|
id: "archive:hooks/hooks.json",
|
|
1879
1642
|
path: hooksPath,
|
|
1880
1643
|
relativePath: "hooks/hooks.json",
|
|
1881
1644
|
message: "Codex native hooks are archived for manual review because they can execute commands"
|
|
1882
1645
|
});
|
|
1883
1646
|
const skills = [...codexSkills, ...personalAgentSkills].toSorted((a, b) => a.source.localeCompare(b.source));
|
|
1884
|
-
const hasAuth =
|
|
1885
|
-
const high = Boolean(
|
|
1647
|
+
const hasAuth = await exists(authPath);
|
|
1648
|
+
const high = Boolean(codexSkills.length || plugins.length || archivePaths.length || hasAuth);
|
|
1886
1649
|
const medium = personalAgentSkills.length > 0;
|
|
1887
1650
|
return {
|
|
1888
1651
|
root: codexHome,
|
|
@@ -1890,9 +1653,10 @@ async function discoverCodexSource(options = {}) {
|
|
|
1890
1653
|
codexHome,
|
|
1891
1654
|
...await isDirectory(codexSkillsDir) ? { codexSkillsDir } : {},
|
|
1892
1655
|
...await isDirectory(agentsSkillsDir) ? { personalAgentsSkillsDir: agentsSkillsDir } : {},
|
|
1656
|
+
...await exists(configPath) ? { configPath } : {},
|
|
1893
1657
|
...hasAuth ? { authPath } : {},
|
|
1894
1658
|
...await exists(modelsCachePath) ? { modelsCachePath } : {},
|
|
1895
|
-
|
|
1659
|
+
...await exists(hooksPath) ? { hooksPath } : {},
|
|
1896
1660
|
skills,
|
|
1897
1661
|
plugins,
|
|
1898
1662
|
...sourcePluginDiscovery.error ? { pluginDiscoveryError: sourcePluginDiscovery.error } : {},
|
|
@@ -1909,7 +1673,7 @@ function sanitizePluginName(value) {
|
|
|
1909
1673
|
//#region extensions/codex/src/migration/targets.ts
|
|
1910
1674
|
function resolveCodexMigrationTargets(ctx) {
|
|
1911
1675
|
const cfg = ctx.config;
|
|
1912
|
-
const agentId =
|
|
1676
|
+
const agentId = resolveDefaultAgentId(cfg);
|
|
1913
1677
|
const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
|
|
1914
1678
|
const configuredAgentDir = resolveAgentConfig(cfg, agentId)?.agentDir?.trim();
|
|
1915
1679
|
return {
|
|
@@ -1940,52 +1704,6 @@ const CODEX_PLUGIN_NATIVE_CONFIG_PATH = [
|
|
|
1940
1704
|
];
|
|
1941
1705
|
const MIGRATION_REASON_PLUGIN_EXISTS = "plugin exists";
|
|
1942
1706
|
const CODEX_PLUGIN_SOURCE_APP_VERIFICATION_UNVERIFIED = "not_run";
|
|
1943
|
-
const MIGRATION_REASON_TARGET_NOT_REGULAR = "target is not a regular file";
|
|
1944
|
-
async function lstatIfExists(filePath) {
|
|
1945
|
-
try {
|
|
1946
|
-
return await fs.lstat(filePath);
|
|
1947
|
-
} catch (error) {
|
|
1948
|
-
const code = extractErrorCode(error);
|
|
1949
|
-
if (code === "ENOENT" || code === "ENOTDIR") return;
|
|
1950
|
-
throw error;
|
|
1951
|
-
}
|
|
1952
|
-
}
|
|
1953
|
-
async function buildCodexMemoryItems(params) {
|
|
1954
|
-
const items = [];
|
|
1955
|
-
for (const memory of params.memoryFiles) {
|
|
1956
|
-
const target = path.join(params.workspaceDir, "memory", "imports", "codex", path.basename(memory.path));
|
|
1957
|
-
const targetStat = await lstatIfExists(target);
|
|
1958
|
-
const targetNotRegular = targetStat !== void 0 && !targetStat.isFile();
|
|
1959
|
-
if (!targetNotRegular) {
|
|
1960
|
-
const [source, workspace, destination] = await Promise.all([
|
|
1961
|
-
fs.realpath(path.dirname(memory.path)),
|
|
1962
|
-
canonicalPathFromExistingAncestor(params.workspaceDir),
|
|
1963
|
-
canonicalPathFromExistingAncestor(target)
|
|
1964
|
-
]);
|
|
1965
|
-
if (!isPathInside(workspace, destination)) throw new Error("Codex memory import destination must stay in the selected workspace.");
|
|
1966
|
-
if (isPathInside(source, destination) || isPathInside(destination, source)) throw new Error("Codex memory source and OpenClaw import destination must be separate paths.");
|
|
1967
|
-
}
|
|
1968
|
-
const targetConflict = targetStat !== void 0 && !params.overwrite;
|
|
1969
|
-
items.push(createMigrationItem({
|
|
1970
|
-
id: memory.id,
|
|
1971
|
-
kind: "memory",
|
|
1972
|
-
action: "copy",
|
|
1973
|
-
source: memory.path,
|
|
1974
|
-
target,
|
|
1975
|
-
status: targetNotRegular || targetConflict ? "conflict" : "planned",
|
|
1976
|
-
reason: targetNotRegular ? MIGRATION_REASON_TARGET_NOT_REGULAR : targetConflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
|
|
1977
|
-
message: "Copy consolidated Codex memory into the OpenClaw memory index.",
|
|
1978
|
-
details: {
|
|
1979
|
-
sourceType: "codex-memory",
|
|
1980
|
-
sourceLabel: memory.label,
|
|
1981
|
-
collectionId: "codex",
|
|
1982
|
-
collectionLabel: "Codex",
|
|
1983
|
-
relativePath: path.basename(memory.path)
|
|
1984
|
-
}
|
|
1985
|
-
}));
|
|
1986
|
-
}
|
|
1987
|
-
return items;
|
|
1988
|
-
}
|
|
1989
1707
|
function uniqueSkillName(skill, counts) {
|
|
1990
1708
|
const base = sanitizeName(skill.name) || "codex-skill";
|
|
1991
1709
|
if ((counts.get(base) ?? 0) <= 1) return base;
|
|
@@ -1995,44 +1713,50 @@ function uniqueSkillName(skill, counts) {
|
|
|
1995
1713
|
base
|
|
1996
1714
|
].filter(Boolean).join("-")) || base;
|
|
1997
1715
|
}
|
|
1998
|
-
async function
|
|
1999
|
-
const
|
|
1716
|
+
async function buildSkillItems(params) {
|
|
1717
|
+
const baseCounts = /* @__PURE__ */ new Map();
|
|
2000
1718
|
for (const skill of params.skills) {
|
|
2001
1719
|
const base = sanitizeName(skill.name) || "codex-skill";
|
|
2002
|
-
|
|
1720
|
+
baseCounts.set(base, (baseCounts.get(base) ?? 0) + 1);
|
|
2003
1721
|
}
|
|
1722
|
+
const resolvedCounts = /* @__PURE__ */ new Map();
|
|
2004
1723
|
const planned = params.skills.map((skill) => {
|
|
2005
|
-
const name = uniqueSkillName(skill,
|
|
1724
|
+
const name = uniqueSkillName(skill, baseCounts);
|
|
1725
|
+
resolvedCounts.set(name, (resolvedCounts.get(name) ?? 0) + 1);
|
|
2006
1726
|
return {
|
|
2007
1727
|
skill,
|
|
2008
1728
|
name,
|
|
2009
1729
|
target: path.join(params.workspaceDir, "skills", name)
|
|
2010
1730
|
};
|
|
2011
1731
|
});
|
|
2012
|
-
const
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
}, /* @__PURE__ */ new Map());
|
|
2016
|
-
return await Promise.all(planned.map(async (item) => {
|
|
2017
|
-
const collision = (resolvedCounts.get(item.name) ?? 0) > 1;
|
|
1732
|
+
const items = [];
|
|
1733
|
+
for (const item of planned) {
|
|
1734
|
+
const collides = (resolvedCounts.get(item.name) ?? 0) > 1;
|
|
2018
1735
|
const targetExists = await exists(item.target);
|
|
2019
|
-
|
|
2020
|
-
return createMigrationItem({
|
|
1736
|
+
items.push(createMigrationItem({
|
|
2021
1737
|
id: `skill:${item.name}`,
|
|
2022
1738
|
kind: "skill",
|
|
2023
1739
|
action: "copy",
|
|
2024
1740
|
source: item.skill.source,
|
|
2025
1741
|
target: item.target,
|
|
2026
|
-
status: conflict ? "conflict" : "planned",
|
|
2027
|
-
reason:
|
|
1742
|
+
status: collides ? "conflict" : targetExists && !params.overwrite ? "conflict" : "planned",
|
|
1743
|
+
reason: collides ? `multiple Codex skills normalize to "${item.name}"` : targetExists && !params.overwrite ? MIGRATION_REASON_TARGET_EXISTS : void 0,
|
|
2028
1744
|
message: `Copy ${item.skill.sourceLabel} into this OpenClaw agent workspace.`,
|
|
2029
1745
|
details: {
|
|
2030
1746
|
skillName: item.name,
|
|
2031
1747
|
sourceLabel: item.skill.sourceLabel
|
|
2032
1748
|
}
|
|
2033
|
-
});
|
|
2034
|
-
}
|
|
2035
|
-
|
|
1749
|
+
}));
|
|
1750
|
+
}
|
|
1751
|
+
return items;
|
|
1752
|
+
}
|
|
1753
|
+
function uniquePluginConfigKey(plugin, counts, usedCounts) {
|
|
1754
|
+
const base = sanitizeName(plugin.pluginName ?? plugin.name) || "codex-plugin";
|
|
1755
|
+
if ((counts.get(base) ?? 0) <= 1) return base;
|
|
1756
|
+
const next = (usedCounts.get(base) ?? 0) + 1;
|
|
1757
|
+
usedCounts.set(base, next);
|
|
1758
|
+
return sanitizeName(`${base}-${next}`) || base;
|
|
1759
|
+
}
|
|
2036
1760
|
function readExistingCodexPluginEntries(config) {
|
|
2037
1761
|
const entries = readMigrationConfigPath(config, [...CODEX_PLUGIN_NATIVE_CONFIG_PATH, "plugins"]);
|
|
2038
1762
|
return isRecord(entries) ? entries : {};
|
|
@@ -2062,12 +1786,18 @@ function readExistingPluginAllowDestructiveActions(existing, pluginName) {
|
|
|
2062
1786
|
return normalized === "auto" || normalized === "ask" ? normalized : void 0;
|
|
2063
1787
|
}
|
|
2064
1788
|
function buildPluginItems(ctx, plugins) {
|
|
1789
|
+
const baseCounts = /* @__PURE__ */ new Map();
|
|
1790
|
+
for (const plugin of plugins.filter((entry) => entry.migratable)) {
|
|
1791
|
+
const base = sanitizeName(plugin.pluginName ?? plugin.name) || "codex-plugin";
|
|
1792
|
+
baseCounts.set(base, (baseCounts.get(base) ?? 0) + 1);
|
|
1793
|
+
}
|
|
2065
1794
|
const existingPluginEntries = readExistingCodexPluginEntries(ctx.config);
|
|
1795
|
+
const usedCounts = /* @__PURE__ */ new Map();
|
|
2066
1796
|
let manualIndex = 0;
|
|
2067
1797
|
const items = [];
|
|
2068
1798
|
for (const plugin of plugins) {
|
|
2069
1799
|
if (plugin.migratable && plugin.marketplaceName === "openai-curated" && plugin.pluginName) {
|
|
2070
|
-
const configKey = plugin
|
|
1800
|
+
const configKey = uniquePluginConfigKey(plugin, baseCounts, usedCounts);
|
|
2071
1801
|
const plannedEntry = {
|
|
2072
1802
|
enabled: true,
|
|
2073
1803
|
marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
|
|
@@ -2084,7 +1814,6 @@ function buildPluginItems(ctx, plugins) {
|
|
|
2084
1814
|
action: "install",
|
|
2085
1815
|
status: conflict ? "conflict" : "planned",
|
|
2086
1816
|
reason: conflict ? MIGRATION_REASON_PLUGIN_EXISTS : void 0,
|
|
2087
|
-
applyPhase: "after-promotion",
|
|
2088
1817
|
source: plugin.source,
|
|
2089
1818
|
target: `plugins.entries.codex.config.codexPlugins.plugins.${configKey}`,
|
|
2090
1819
|
message: `Install Codex plugin "${plugin.pluginName}" in the OpenClaw-managed Codex app-server runtime.`,
|
|
@@ -2102,6 +1831,12 @@ function buildPluginItems(ctx, plugins) {
|
|
|
2102
1831
|
}
|
|
2103
1832
|
manualIndex += 1;
|
|
2104
1833
|
if (plugin.migrationBlock && plugin.pluginName) {
|
|
1834
|
+
const details = {
|
|
1835
|
+
pluginName: plugin.pluginName,
|
|
1836
|
+
marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
|
|
1837
|
+
...plugin.migrationBlock.apps ? { apps: plugin.migrationBlock.apps } : {},
|
|
1838
|
+
...plugin.migrationBlock.error ? { error: plugin.migrationBlock.error } : {}
|
|
1839
|
+
};
|
|
2105
1840
|
items.push(createMigrationItem({
|
|
2106
1841
|
id: `plugin:${sanitizeName(plugin.name) || sanitizeName(path.basename(plugin.source))}:${manualIndex}`,
|
|
2107
1842
|
kind: "manual",
|
|
@@ -2110,12 +1845,7 @@ function buildPluginItems(ctx, plugins) {
|
|
|
2110
1845
|
status: "skipped",
|
|
2111
1846
|
reason: plugin.migrationBlock.code,
|
|
2112
1847
|
message: plugin.message ?? `Codex native plugin "${plugin.name}" was found but not activated automatically.`,
|
|
2113
|
-
details: {
|
|
2114
|
-
pluginName: plugin.pluginName,
|
|
2115
|
-
marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
|
|
2116
|
-
...plugin.migrationBlock.apps ? { apps: plugin.migrationBlock.apps } : {},
|
|
2117
|
-
...plugin.migrationBlock.error ? { error: plugin.migrationBlock.error } : {}
|
|
2118
|
-
}
|
|
1848
|
+
details: { ...details }
|
|
2119
1849
|
}));
|
|
2120
1850
|
continue;
|
|
2121
1851
|
}
|
|
@@ -2123,7 +1853,7 @@ function buildPluginItems(ctx, plugins) {
|
|
|
2123
1853
|
id: `plugin:${sanitizeName(plugin.name) || sanitizeName(path.basename(plugin.source))}:${manualIndex}`,
|
|
2124
1854
|
source: plugin.source,
|
|
2125
1855
|
message: plugin.message ?? `Codex native plugin "${plugin.name}" was found but not activated automatically.`,
|
|
2126
|
-
recommendation: "Review the plugin bundle first, then install trusted compatible plugins with openclaw plugins install <path
|
|
1856
|
+
recommendation: "Review the plugin bundle first, then install trusted compatible plugins with openclaw plugins install <path>."
|
|
2127
1857
|
}));
|
|
2128
1858
|
}
|
|
2129
1859
|
return items;
|
|
@@ -2153,6 +1883,7 @@ function normalizeExistingAllowDestructiveActions(value) {
|
|
|
2153
1883
|
return asBoolean(value);
|
|
2154
1884
|
}
|
|
2155
1885
|
function readExistingPluginPolicyRepairs(config) {
|
|
1886
|
+
if (config === void 0) return {};
|
|
2156
1887
|
return Object.fromEntries(Object.entries(readExistingCodexPluginEntries(config)).flatMap(([configKey, entry]) => {
|
|
2157
1888
|
const pluginEntry = isRecord(entry) ? entry : void 0;
|
|
2158
1889
|
if (pluginEntry?.allow_destructive_actions !== "on-request") return [];
|
|
@@ -2162,9 +1893,9 @@ function readExistingPluginPolicyRepairs(config) {
|
|
|
2162
1893
|
}]];
|
|
2163
1894
|
}));
|
|
2164
1895
|
}
|
|
2165
|
-
function buildCodexPluginsConfigValue(entries,
|
|
1896
|
+
function buildCodexPluginsConfigValue(entries, params = {}) {
|
|
2166
1897
|
const plugins = {
|
|
2167
|
-
...readExistingPluginPolicyRepairs(config),
|
|
1898
|
+
...readExistingPluginPolicyRepairs(params.config),
|
|
2168
1899
|
...Object.fromEntries(entries.toSorted((a, b) => a.configKey.localeCompare(b.configKey)).map((entry) => [entry.configKey, {
|
|
2169
1900
|
enabled: entry.enabled,
|
|
2170
1901
|
marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
|
|
@@ -2176,7 +1907,7 @@ function buildCodexPluginsConfigValue(entries, config) {
|
|
|
2176
1907
|
enabled: true,
|
|
2177
1908
|
config: { codexPlugins: {
|
|
2178
1909
|
enabled: true,
|
|
2179
|
-
allow_destructive_actions: readExistingAllowDestructiveActions(config) ?? true,
|
|
1910
|
+
allow_destructive_actions: params.config === void 0 ? true : readExistingAllowDestructiveActions(params.config) ?? true,
|
|
2180
1911
|
plugins
|
|
2181
1912
|
} }
|
|
2182
1913
|
};
|
|
@@ -2203,7 +1934,7 @@ function hasCodexPluginConfigConflict(config, value) {
|
|
|
2203
1934
|
function buildPluginConfigItem(ctx, pluginItems) {
|
|
2204
1935
|
const entries = pluginItems.filter((item) => item.status === "planned").map((item) => readCodexPluginMigrationConfigEntry(item, true)).filter((entry) => entry !== void 0);
|
|
2205
1936
|
if (entries.length === 0) return;
|
|
2206
|
-
const value = buildCodexPluginsConfigValue(entries, ctx.config);
|
|
1937
|
+
const value = buildCodexPluginsConfigValue(entries, { config: ctx.config });
|
|
2207
1938
|
const conflict = !ctx.overwrite && hasCodexPluginConfigConflict(ctx.config, value);
|
|
2208
1939
|
return createMigrationItem({
|
|
2209
1940
|
id: CODEX_PLUGIN_CONFIG_ITEM_ID,
|
|
@@ -2212,7 +1943,6 @@ function buildPluginConfigItem(ctx, pluginItems) {
|
|
|
2212
1943
|
target: "plugins.entries.codex.config.codexPlugins",
|
|
2213
1944
|
status: conflict ? "conflict" : "planned",
|
|
2214
1945
|
reason: conflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
|
|
2215
|
-
applyPhase: "after-promotion",
|
|
2216
1946
|
message: "Enable OpenClaw's Codex plugin integration and record migrated source-installed curated plugins.",
|
|
2217
1947
|
details: {
|
|
2218
1948
|
path: [...CODEX_PLUGIN_CONFIG_PATH],
|
|
@@ -2222,44 +1952,35 @@ function buildPluginConfigItem(ctx, pluginItems) {
|
|
|
2222
1952
|
}
|
|
2223
1953
|
async function buildCodexMigrationPlan(ctx) {
|
|
2224
1954
|
const targets = resolveCodexMigrationTargets(ctx);
|
|
2225
|
-
const memoryOnly = ctx.itemKinds !== void 0 && ctx.itemKinds.length > 0 && ctx.itemKinds.every((kind) => kind === "memory");
|
|
2226
1955
|
const source = await discoverCodexSource({
|
|
2227
1956
|
input: ctx.source,
|
|
2228
|
-
|
|
2229
|
-
evaluatePluginMigrationEligibility: !memoryOnly,
|
|
1957
|
+
evaluatePluginMigrationEligibility: true,
|
|
2230
1958
|
verifyPluginApps: shouldVerifyPluginApps(ctx)
|
|
2231
1959
|
});
|
|
2232
1960
|
if (!hasCodexSource(source)) throw new Error(`Codex state was not found at ${source.root}. Pass --from <path> if it lives elsewhere.`);
|
|
2233
1961
|
const items = [];
|
|
2234
|
-
items.push(...await
|
|
2235
|
-
|
|
1962
|
+
items.push(...await buildCodexAuthItems({
|
|
1963
|
+
ctx,
|
|
1964
|
+
source,
|
|
1965
|
+
targets
|
|
1966
|
+
}));
|
|
1967
|
+
items.push(...await buildSkillItems({
|
|
1968
|
+
skills: source.skills,
|
|
2236
1969
|
workspaceDir: targets.workspaceDir,
|
|
2237
1970
|
overwrite: ctx.overwrite
|
|
2238
1971
|
}));
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
|
-
items.push(...pluginItems);
|
|
2252
|
-
const pluginConfigItem = buildPluginConfigItem(ctx, pluginItems);
|
|
2253
|
-
if (pluginConfigItem) items.push(pluginConfigItem);
|
|
2254
|
-
for (const archivePath of source.archivePaths) items.push(createMigrationItem({
|
|
2255
|
-
id: archivePath.id,
|
|
2256
|
-
kind: "archive",
|
|
2257
|
-
action: "archive",
|
|
2258
|
-
source: archivePath.path,
|
|
2259
|
-
message: archivePath.message ?? "Archived in the migration report for manual review; not imported into live config.",
|
|
2260
|
-
details: { archiveRelativePath: archivePath.relativePath }
|
|
2261
|
-
}));
|
|
2262
|
-
}
|
|
1972
|
+
const pluginItems = buildPluginItems(ctx, source.plugins);
|
|
1973
|
+
items.push(...pluginItems);
|
|
1974
|
+
const pluginConfigItem = buildPluginConfigItem(ctx, pluginItems);
|
|
1975
|
+
if (pluginConfigItem) items.push(pluginConfigItem);
|
|
1976
|
+
for (const archivePath of source.archivePaths) items.push(createMigrationItem({
|
|
1977
|
+
id: archivePath.id,
|
|
1978
|
+
kind: "archive",
|
|
1979
|
+
action: "archive",
|
|
1980
|
+
source: archivePath.path,
|
|
1981
|
+
message: archivePath.message ?? "Archived in the migration report for manual review; not imported into live config.",
|
|
1982
|
+
details: { archiveRelativePath: archivePath.relativePath }
|
|
1983
|
+
}));
|
|
2263
1984
|
const warnings = [
|
|
2264
1985
|
...!ctx.includeSecrets && items.some((item) => item.kind === "auth") ? ["Auth credentials were detected but skipped. Re-run interactively or pass --include-secrets to import supported credentials."] : [],
|
|
2265
1986
|
...items.some((item) => item.status === "conflict") ? ["Conflicts were found. Re-run with --overwrite to replace conflicting migration targets after item-level backups."] : [],
|
|
@@ -2273,7 +1994,7 @@ async function buildCodexMigrationPlan(ctx) {
|
|
|
2273
1994
|
summary: summarizeMigrationItems(items),
|
|
2274
1995
|
items,
|
|
2275
1996
|
warnings,
|
|
2276
|
-
nextSteps:
|
|
1997
|
+
nextSteps: ["Run openclaw doctor after applying the migration.", "Review skipped or auth-required Codex plugin/config/hook items before exposing them in OpenClaw sessions."],
|
|
2277
1998
|
metadata: {
|
|
2278
1999
|
agentDir: targets.agentDir,
|
|
2279
2000
|
codexHome: source.codexHome,
|
|
@@ -2329,9 +2050,14 @@ async function applyCodexMigrationPlan(params) {
|
|
|
2329
2050
|
const targets = resolveCodexMigrationTargets(params.ctx);
|
|
2330
2051
|
const codexHome = typeof plan.metadata?.codexHome === "string" && plan.metadata.codexHome.trim() ? plan.metadata.codexHome : plan.source;
|
|
2331
2052
|
const authSource = {
|
|
2053
|
+
root: plan.source,
|
|
2054
|
+
confidence: "high",
|
|
2332
2055
|
codexHome,
|
|
2333
2056
|
authPath: path.join(codexHome, "auth.json"),
|
|
2334
|
-
modelsCachePath: path.join(codexHome, "models_cache.json")
|
|
2057
|
+
modelsCachePath: path.join(codexHome, "models_cache.json"),
|
|
2058
|
+
skills: [],
|
|
2059
|
+
plugins: [],
|
|
2060
|
+
archivePaths: []
|
|
2335
2061
|
};
|
|
2336
2062
|
const runtime = withCachedMigrationConfigRuntime(params.ctx.runtime ?? params.runtime, params.ctx.config);
|
|
2337
2063
|
const applyCtx = {
|
|
@@ -2344,19 +2070,22 @@ async function applyCodexMigrationPlan(params) {
|
|
|
2344
2070
|
continue;
|
|
2345
2071
|
}
|
|
2346
2072
|
if (item.id === "config:codex-plugins") items.push(await applyCodexPluginConfigItem(applyCtx, item, items));
|
|
2347
|
-
else if (item.kind === "auth")
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2073
|
+
else if (item.kind === "auth") {
|
|
2074
|
+
const authItem = await applyCodexAuthItem({
|
|
2075
|
+
ctx: applyCtx,
|
|
2076
|
+
item,
|
|
2077
|
+
source: authSource,
|
|
2078
|
+
targets
|
|
2079
|
+
});
|
|
2080
|
+
items.push(authItem);
|
|
2081
|
+
items.push(...await buildCodexAuthConfigPatchItems({
|
|
2082
|
+
ctx: applyCtx,
|
|
2083
|
+
item: authItem,
|
|
2084
|
+
source: authSource
|
|
2085
|
+
}));
|
|
2086
|
+
} else if (item.kind === "plugin" && item.action === "install") items.push(await applyCodexPluginInstallItem(applyCtx, item));
|
|
2354
2087
|
else if (item.kind === "manual") items.push(applyMigrationManualItem(item));
|
|
2355
2088
|
else if (item.action === "archive") items.push(await archiveMigrationItem(item, reportDir));
|
|
2356
|
-
else if (item.kind === "memory") items.push(await copyMemoryMigrationFileItem(item, reportDir, {
|
|
2357
|
-
workspaceDir: targets.workspaceDir,
|
|
2358
|
-
overwrite: params.ctx.overwrite
|
|
2359
|
-
}));
|
|
2360
2089
|
else items.push(await copyMigrationFileItem(item, reportDir, { overwrite: params.ctx.overwrite }));
|
|
2361
2090
|
}
|
|
2362
2091
|
const result = {
|
|
@@ -2488,7 +2217,8 @@ async function requestTargetCodexAppServerJson(params) {
|
|
|
2488
2217
|
});
|
|
2489
2218
|
if (hasOpenAiCuratedMarketplace(lastResponse)) return lastResponse;
|
|
2490
2219
|
if (Date.now() >= discoveryDeadline) return lastResponse;
|
|
2491
|
-
|
|
2220
|
+
const waitMs = Math.min(TARGET_CODEX_MARKETPLACE_DISCOVERY_POLL_MS, discoveryDeadline - Date.now());
|
|
2221
|
+
await sleep(waitMs);
|
|
2492
2222
|
} while (Date.now() < discoveryDeadline);
|
|
2493
2223
|
return lastResponse;
|
|
2494
2224
|
}
|
|
@@ -2527,21 +2257,13 @@ async function buildTargetCodexPluginAppCacheKey(ctx) {
|
|
|
2527
2257
|
});
|
|
2528
2258
|
}
|
|
2529
2259
|
async function applyCodexPluginConfigItem(ctx, item, appliedItems) {
|
|
2530
|
-
if (appliedItems.filter((candidate) => candidate.kind === "plugin" && candidate.action === "install" && readCodexPluginPolicy(candidate) !== void 0 && !isCodexPluginConfigTerminal(candidate)).length > 0) return {
|
|
2531
|
-
...item,
|
|
2532
|
-
status: "warning",
|
|
2533
|
-
reason: "selected Codex plugin activation is incomplete"
|
|
2534
|
-
};
|
|
2535
2260
|
const entries = appliedItems.map(readAppliedPluginConfigEntry).filter((entry) => entry !== void 0);
|
|
2536
|
-
if (entries.length === 0) return
|
|
2537
|
-
...markMigrationItemSkipped(item, "no selected Codex plugins"),
|
|
2538
|
-
deferredCompletion: true
|
|
2539
|
-
};
|
|
2261
|
+
if (entries.length === 0) return markMigrationItemSkipped(item, "no selected Codex plugins");
|
|
2540
2262
|
const returnPatch = shouldReturnCodexPluginConfigPatch(ctx);
|
|
2541
|
-
const configApi =
|
|
2263
|
+
const configApi = ctx.runtime?.config;
|
|
2542
2264
|
const currentConfig = returnPatch ? ctx.config : configApi?.current?.();
|
|
2543
2265
|
if (!currentConfig) return markMigrationItemError(item, "config runtime unavailable");
|
|
2544
|
-
const value = buildCodexPluginsConfigValue(entries, currentConfig);
|
|
2266
|
+
const value = buildCodexPluginsConfigValue(entries, { config: currentConfig });
|
|
2545
2267
|
if (!ctx.overwrite && hasCodexPluginConfigConflict(currentConfig, value)) return markMigrationItemConflict(item, MIGRATION_REASON_TARGET_EXISTS);
|
|
2546
2268
|
const migratedItem = {
|
|
2547
2269
|
...item,
|
|
@@ -2569,42 +2291,65 @@ async function applyCodexPluginConfigItem(ctx, item, appliedItems) {
|
|
|
2569
2291
|
return markMigrationItemError(item, error instanceof Error ? error.message : String(error));
|
|
2570
2292
|
}
|
|
2571
2293
|
}
|
|
2572
|
-
function isCodexPluginConfigTerminal(item) {
|
|
2573
|
-
return item.status === "migrated" || item.status === "skipped" && (item.deferredCompletion === true || item.reason === CODEX_PLUGIN_NOT_SELECTED_REASON || item.reason === CODEX_PLUGIN_AUTH_REQUIRED_REASON);
|
|
2574
|
-
}
|
|
2575
2294
|
function readAppliedPluginConfigEntry(item) {
|
|
2576
|
-
if (item.status === "migrated"
|
|
2577
|
-
if (item.status === "skipped" && item.reason === CODEX_PLUGIN_AUTH_REQUIRED_REASON) return readCodexPluginMigrationConfigEntry(item, false);
|
|
2295
|
+
if (item.status === "migrated") return readCodexPluginMigrationConfigEntry(item, true);
|
|
2296
|
+
if (item.status === "skipped" && item.reason !== CODEX_PLUGIN_NOT_SELECTED_REASON && item.reason === CODEX_PLUGIN_AUTH_REQUIRED_REASON) return readCodexPluginMigrationConfigEntry(item, false);
|
|
2578
2297
|
}
|
|
2579
2298
|
function readCodexPluginPolicy(item) {
|
|
2580
|
-
const
|
|
2581
|
-
|
|
2299
|
+
const configKey = item.details?.configKey;
|
|
2300
|
+
const marketplaceName = item.details?.marketplaceName;
|
|
2301
|
+
const pluginName = item.details?.pluginName;
|
|
2302
|
+
if (typeof configKey !== "string" || marketplaceName !== "openai-curated" || typeof pluginName !== "string") return;
|
|
2582
2303
|
return {
|
|
2583
|
-
configKey
|
|
2304
|
+
configKey,
|
|
2584
2305
|
marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
|
|
2585
|
-
pluginName
|
|
2306
|
+
pluginName,
|
|
2586
2307
|
enabled: true,
|
|
2587
2308
|
allowDestructiveActions: true,
|
|
2588
2309
|
destructiveApprovalMode: "allow"
|
|
2589
2310
|
};
|
|
2590
2311
|
}
|
|
2312
|
+
function codexPluginActivationReportState(result) {
|
|
2313
|
+
switch (result.reason) {
|
|
2314
|
+
case "already_active":
|
|
2315
|
+
case "installed": return {
|
|
2316
|
+
installed: true,
|
|
2317
|
+
enabled: true
|
|
2318
|
+
};
|
|
2319
|
+
case "auth_required": return {
|
|
2320
|
+
installed: true,
|
|
2321
|
+
enabled: false
|
|
2322
|
+
};
|
|
2323
|
+
case "disabled":
|
|
2324
|
+
case "marketplace_missing":
|
|
2325
|
+
case "plugin_missing": return {
|
|
2326
|
+
installed: false,
|
|
2327
|
+
enabled: false
|
|
2328
|
+
};
|
|
2329
|
+
case "refresh_failed": return {
|
|
2330
|
+
installed: true,
|
|
2331
|
+
enabled: false
|
|
2332
|
+
};
|
|
2333
|
+
}
|
|
2334
|
+
return result.reason;
|
|
2335
|
+
}
|
|
2336
|
+
function sanitizeAppsNeedingAuth(apps) {
|
|
2337
|
+
return apps.map((app) => ({
|
|
2338
|
+
id: app.id,
|
|
2339
|
+
name: app.name,
|
|
2340
|
+
needsAuth: app.needsAuth
|
|
2341
|
+
}));
|
|
2342
|
+
}
|
|
2591
2343
|
//#endregion
|
|
2592
2344
|
//#region extensions/codex/src/migration/provider.ts
|
|
2593
|
-
function isMemoryOnlyMigration(ctx) {
|
|
2594
|
-
return Boolean(ctx.itemKinds && ctx.itemKinds.length > 0 && ctx.itemKinds.every((kind) => kind === "memory"));
|
|
2595
|
-
}
|
|
2596
2345
|
function buildCodexMigrationProvider(params = {}) {
|
|
2597
2346
|
return {
|
|
2598
2347
|
id: "codex",
|
|
2599
2348
|
label: "Codex",
|
|
2600
|
-
description: "
|
|
2601
|
-
supportedItemKinds: ["memory"],
|
|
2349
|
+
description: "Inventory and promote Codex CLI skills while keeping Codex native plugins and hooks explicit.",
|
|
2602
2350
|
async detect(ctx) {
|
|
2603
|
-
const source = await discoverCodexSource({
|
|
2604
|
-
|
|
2605
|
-
memoryOnly: isMemoryOnlyMigration(ctx)
|
|
2606
|
-
});
|
|
2607
|
-
const found = isMemoryOnlyMigration(ctx) ? source.memoryFiles.length > 0 : hasCodexSource(source);
|
|
2351
|
+
const source = await discoverCodexSource({ input: ctx.source });
|
|
2352
|
+
const found = hasCodexSource(source);
|
|
2608
2353
|
return {
|
|
2609
2354
|
found,
|
|
2610
2355
|
source: source.root,
|
|
@@ -2614,9 +2359,7 @@ function buildCodexMigrationProvider(params = {}) {
|
|
|
2614
2359
|
};
|
|
2615
2360
|
},
|
|
2616
2361
|
plan: buildCodexMigrationPlan,
|
|
2617
|
-
deferredApply: { retrySafe: true },
|
|
2618
2362
|
prepareApply(ctx) {
|
|
2619
|
-
if (isMemoryOnlyMigration(ctx)) return;
|
|
2620
2363
|
return prepareTargetCodexAppServer(ctx);
|
|
2621
2364
|
},
|
|
2622
2365
|
async apply(ctx, plan) {
|
|
@@ -2678,7 +2421,7 @@ const CodexThreadsParamsSchema = Type.Union([
|
|
|
2678
2421
|
ArchiveParamsSchema,
|
|
2679
2422
|
UnarchiveParamsSchema
|
|
2680
2423
|
]);
|
|
2681
|
-
function asRecord
|
|
2424
|
+
function asRecord(value) {
|
|
2682
2425
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2683
2426
|
}
|
|
2684
2427
|
function readBoolean(value, fallback = false) {
|
|
@@ -2697,9 +2440,13 @@ function resolveToolSession(context, runtime) {
|
|
|
2697
2440
|
});
|
|
2698
2441
|
const sessionId = context.sessionId?.trim() || entry?.sessionId?.trim();
|
|
2699
2442
|
if (!sessionId) return;
|
|
2443
|
+
const storePath = runtime.agent.session.resolveStorePath(void 0, { agentId: context.agentId });
|
|
2700
2444
|
return {
|
|
2701
2445
|
sessionId,
|
|
2702
|
-
|
|
2446
|
+
sessionFile: runtime.agent.session.resolveSessionFilePath(sessionId, entry, {
|
|
2447
|
+
agentId: context.agentId,
|
|
2448
|
+
sessionsDir: path.dirname(storePath)
|
|
2449
|
+
})
|
|
2703
2450
|
};
|
|
2704
2451
|
}
|
|
2705
2452
|
function readThreadId(params) {
|
|
@@ -2712,106 +2459,42 @@ function readThreadStatusType(value) {
|
|
|
2712
2459
|
if (!isJsonObject(value) || !isJsonObject(value.thread) || !isJsonObject(value.thread.status)) return;
|
|
2713
2460
|
return typeof value.thread.status.type === "string" ? value.thread.status.type : void 0;
|
|
2714
2461
|
}
|
|
2715
|
-
|
|
2716
|
-
if (!isJsonObject(value) || !isJsonObject(value.thread)) throw new Error("Codex app-server returned an invalid thread/read response");
|
|
2717
|
-
if (value.thread.id !== expectedThreadId) throw new Error("Codex app-server returned a different thread than requested");
|
|
2718
|
-
const status = readThreadStatusType(value);
|
|
2719
|
-
if (status === "active") throw new Error("cannot archive an active Codex thread; wait for its turn to finish");
|
|
2720
|
-
if (status !== "idle" && status !== "notLoaded") throw new Error("cannot verify that the Codex thread is idle; refusing to archive");
|
|
2721
|
-
}
|
|
2722
|
-
function assertThreadMayBeForked(value, expectedThreadId) {
|
|
2723
|
-
if (!isJsonObject(value) || !isJsonObject(value.thread)) throw new Error("Codex app-server returned an invalid thread/read response");
|
|
2724
|
-
if (value.thread.id !== expectedThreadId) throw new Error("Codex app-server returned a different thread than requested");
|
|
2725
|
-
const status = readThreadStatusType(value);
|
|
2726
|
-
if (status !== "idle" && status !== "notLoaded") throw new Error("cannot fork a Codex thread unless it is idle or not loaded");
|
|
2727
|
-
}
|
|
2728
|
-
function redactNativeThreadTranscriptFields(value) {
|
|
2729
|
-
if (!isJsonObject(value)) return value;
|
|
2730
|
-
const redacted = { ...value };
|
|
2731
|
-
delete redacted.preview;
|
|
2732
|
-
delete redacted.turns;
|
|
2733
|
-
return redacted;
|
|
2734
|
-
}
|
|
2735
|
-
function redactNativeThreadResponse(value) {
|
|
2736
|
-
if (!isJsonObject(value)) return value;
|
|
2737
|
-
const redacted = { ...value };
|
|
2738
|
-
if (Array.isArray(redacted.data)) redacted.data = redacted.data.map(redactNativeThreadTranscriptFields);
|
|
2739
|
-
if (isJsonObject(redacted.thread)) redacted.thread = redactNativeThreadTranscriptFields(redacted.thread);
|
|
2740
|
-
return redacted;
|
|
2741
|
-
}
|
|
2742
|
-
/** Builds the native Codex thread tool only for owner runs with native-home access. */
|
|
2462
|
+
/** Builds the native Codex thread tool only for owner runs in shared-user-home mode. */
|
|
2743
2463
|
function createCodexThreadsTool(options) {
|
|
2744
2464
|
if (options.context.senderIsOwner !== true) return null;
|
|
2745
|
-
|
|
2746
|
-
if (configured.appServer?.homeScope !== "user" && configured.supervision?.enabled !== true) return null;
|
|
2465
|
+
if (readCodexPluginConfig(options.getPluginConfig()).appServer?.homeScope !== "user") return null;
|
|
2747
2466
|
const request = options.request ?? codexControlRequest;
|
|
2748
|
-
const
|
|
2749
|
-
const baseRequestOptions = () => ({
|
|
2467
|
+
const requestOptions = () => ({
|
|
2750
2468
|
agentDir: options.context.agentDir,
|
|
2751
|
-
config:
|
|
2469
|
+
config: options.context.getRuntimeConfig?.() ?? options.context.runtimeConfig ?? options.context.config,
|
|
2752
2470
|
sessionId: options.context.sessionId,
|
|
2753
2471
|
sessionKey: options.context.sessionKey
|
|
2754
2472
|
});
|
|
2755
2473
|
const currentSession = () => resolveToolSession(options.context, options.runtime);
|
|
2756
|
-
const currentIdentity = (sessionId) =>
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
const requestOptions = async (pluginConfig) => {
|
|
2764
|
-
const plugin = readCodexPluginConfig(pluginConfig);
|
|
2765
|
-
const session = currentSession();
|
|
2766
|
-
const binding = await currentBinding(session);
|
|
2767
|
-
if (binding?.connectionScope === "supervision") {
|
|
2768
|
-
const connection = resolveCodexBindingAppServerConnection({
|
|
2769
|
-
binding,
|
|
2770
|
-
pluginConfig
|
|
2771
|
-
});
|
|
2772
|
-
return {
|
|
2773
|
-
...baseRequestOptions(),
|
|
2774
|
-
startOptions: connection.appServer.start,
|
|
2775
|
-
authProfileId: connection.clientAuthProfileId
|
|
2776
|
-
};
|
|
2777
|
-
}
|
|
2778
|
-
if (plugin.appServer?.homeScope === "user") {
|
|
2779
|
-
const connection = resolveCodexBindingAppServerConnection({
|
|
2780
|
-
binding,
|
|
2781
|
-
pluginConfig
|
|
2782
|
-
});
|
|
2783
|
-
return {
|
|
2784
|
-
...baseRequestOptions(),
|
|
2785
|
-
startOptions: connection.appServer.start,
|
|
2786
|
-
authProfileId: null
|
|
2787
|
-
};
|
|
2788
|
-
}
|
|
2789
|
-
if (plugin.supervision?.enabled !== true) throw new Error("Codex native thread access is disabled for this run.");
|
|
2790
|
-
return {
|
|
2791
|
-
...baseRequestOptions(),
|
|
2792
|
-
startOptions: resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig }).start,
|
|
2793
|
-
authProfileId: null
|
|
2794
|
-
};
|
|
2474
|
+
const currentIdentity = (sessionId) => {
|
|
2475
|
+
return sessionBindingIdentity({
|
|
2476
|
+
sessionId,
|
|
2477
|
+
sessionKey: options.context.sessionKey,
|
|
2478
|
+
agentId: options.context.agentId,
|
|
2479
|
+
config: requestOptions().config
|
|
2480
|
+
});
|
|
2795
2481
|
};
|
|
2482
|
+
const currentBinding = async (session) => session ? await options.bindingStore.read(currentIdentity(session.sessionId)) : void 0;
|
|
2796
2483
|
return {
|
|
2797
2484
|
name: "codex_threads",
|
|
2798
2485
|
label: "Codex Threads",
|
|
2799
|
-
description: "
|
|
2486
|
+
description: "List, read, fork, rename, archive, or restore native Codex threads. Fork to continue a thread safely across Codex clients; do not resume the same thread from two clients.",
|
|
2800
2487
|
parameters: CodexThreadsParamsSchema,
|
|
2801
2488
|
async execute(_toolCallId, rawParams) {
|
|
2802
|
-
const params = asRecord
|
|
2489
|
+
const params = asRecord(rawParams);
|
|
2803
2490
|
const action = readStringParam(params, "action", {
|
|
2804
2491
|
required: true,
|
|
2805
2492
|
label: "action"
|
|
2806
2493
|
});
|
|
2807
2494
|
const pluginConfig = options.getPluginConfig();
|
|
2808
|
-
const plugin = readCodexPluginConfig(pluginConfig);
|
|
2809
|
-
const supervision = plugin.supervision;
|
|
2810
|
-
const mayReadRawTranscripts = supervision?.enabled !== true || supervision.allowRawTranscripts === true;
|
|
2811
2495
|
if (action === "list") {
|
|
2812
2496
|
const cursor = readStringParam(params, "cursor");
|
|
2813
2497
|
const searchTerm = readStringParam(params, "search");
|
|
2814
|
-
if (searchTerm && !mayReadRawTranscripts) throw new Error("Codex native thread search is disabled while raw transcript access is disabled.");
|
|
2815
2498
|
const response = await request(pluginConfig, CODEX_CONTROL_METHODS.listThreads, {
|
|
2816
2499
|
archived: readBoolean(params.archived),
|
|
2817
2500
|
limit: readLimit(params.limit) ?? 20,
|
|
@@ -2821,20 +2504,17 @@ function createCodexThreadsTool(options) {
|
|
|
2821
2504
|
sourceKinds: [...CODEX_INTERACTIVE_THREAD_SOURCE_KINDS],
|
|
2822
2505
|
...cursor ? { cursor } : {},
|
|
2823
2506
|
...searchTerm ? { searchTerm } : {}
|
|
2824
|
-
},
|
|
2825
|
-
return jsonResult(
|
|
2507
|
+
}, requestOptions());
|
|
2508
|
+
return jsonResult(response);
|
|
2826
2509
|
}
|
|
2827
2510
|
const threadId = readThreadId(params);
|
|
2828
2511
|
if (action === "read") {
|
|
2829
|
-
const includeTurns = readBoolean(params.include_turns);
|
|
2830
|
-
if (includeTurns && !mayReadRawTranscripts) throw new Error("Codex raw transcript reads are disabled for this codex plugin supervision config.");
|
|
2831
2512
|
const response = await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
|
|
2832
2513
|
threadId,
|
|
2833
|
-
includeTurns
|
|
2834
|
-
},
|
|
2835
|
-
return jsonResult(
|
|
2514
|
+
includeTurns: readBoolean(params.include_turns)
|
|
2515
|
+
}, requestOptions());
|
|
2516
|
+
return jsonResult(response);
|
|
2836
2517
|
}
|
|
2837
|
-
if ((action === "fork" || action === "rename" || action === "archive" || action === "unarchive") && supervision?.enabled === true && supervision.allowWriteControls !== true) throw new Error("Codex native thread mutations are disabled for this codex plugin supervision config.");
|
|
2838
2518
|
if (action === "rename") {
|
|
2839
2519
|
const name = readStringParam(params, "name", {
|
|
2840
2520
|
required: true,
|
|
@@ -2843,7 +2523,7 @@ function createCodexThreadsTool(options) {
|
|
|
2843
2523
|
await request(pluginConfig, CODEX_CONTROL_METHODS.renameThread, {
|
|
2844
2524
|
threadId,
|
|
2845
2525
|
name
|
|
2846
|
-
},
|
|
2526
|
+
}, requestOptions());
|
|
2847
2527
|
return jsonResult({
|
|
2848
2528
|
action,
|
|
2849
2529
|
threadId,
|
|
@@ -2851,42 +2531,23 @@ function createCodexThreadsTool(options) {
|
|
|
2851
2531
|
});
|
|
2852
2532
|
}
|
|
2853
2533
|
if (action === "unarchive") {
|
|
2854
|
-
const response = await request(pluginConfig, CODEX_CONTROL_METHODS.unarchiveThread, { threadId },
|
|
2855
|
-
return jsonResult(
|
|
2534
|
+
const response = await request(pluginConfig, CODEX_CONTROL_METHODS.unarchiveThread, { threadId }, requestOptions());
|
|
2535
|
+
return jsonResult(response);
|
|
2856
2536
|
}
|
|
2857
2537
|
const session = currentSession();
|
|
2858
2538
|
const binding = await currentBinding(session);
|
|
2859
2539
|
if (action === "archive") {
|
|
2860
2540
|
if (params.confirm !== true) throw new Error("confirm=true is required to archive a native Codex thread");
|
|
2861
|
-
if (
|
|
2862
|
-
|
|
2863
|
-
await options.bindingStore.withThreadArchiveFence(async () => {
|
|
2864
|
-
const archivedBinding = await currentBinding(session);
|
|
2865
|
-
if (archivedBinding?.threadId === threadId) {
|
|
2866
|
-
if (session.modelSelectionLocked) throw new ModelSelectionLockedError();
|
|
2867
|
-
assertCodexBindingMayBeReplaced(archivedBinding, "archiving its bound native thread");
|
|
2868
|
-
}
|
|
2869
|
-
assertThreadMayBeArchived(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
|
|
2541
|
+
if (binding?.threadId === threadId) {
|
|
2542
|
+
if (readThreadStatusType(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
|
|
2870
2543
|
threadId,
|
|
2871
2544
|
includeTurns: false
|
|
2872
|
-
},
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
assertDescendantIdle: async (descendantThreadId) => {
|
|
2879
|
-
assertThreadMayBeArchived(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
|
|
2880
|
-
threadId: descendantThreadId,
|
|
2881
|
-
includeTurns: false
|
|
2882
|
-
}, await requestOptions(pluginConfig)), descendantThreadId);
|
|
2883
|
-
}
|
|
2884
|
-
});
|
|
2885
|
-
await request(pluginConfig, CODEX_CONTROL_METHODS.archiveThread, { threadId }, await requestOptions(pluginConfig));
|
|
2886
|
-
if (archivedBinding?.threadId === threadId) await options.bindingStore.mutate(identity, {
|
|
2887
|
-
kind: "clear",
|
|
2888
|
-
threadId
|
|
2889
|
-
});
|
|
2545
|
+
}, requestOptions())) === "active") throw new Error("cannot archive the Codex thread active in this OpenClaw session");
|
|
2546
|
+
}
|
|
2547
|
+
await request(pluginConfig, CODEX_CONTROL_METHODS.archiveThread, { threadId }, requestOptions());
|
|
2548
|
+
if (session && binding?.threadId === threadId) await options.bindingStore.mutate(currentIdentity(session.sessionId), {
|
|
2549
|
+
kind: "clear",
|
|
2550
|
+
threadId
|
|
2890
2551
|
});
|
|
2891
2552
|
return jsonResult({
|
|
2892
2553
|
action,
|
|
@@ -2896,21 +2557,16 @@ function createCodexThreadsTool(options) {
|
|
|
2896
2557
|
if (action !== "fork") throw new Error(`unsupported codex_threads action: ${action}`);
|
|
2897
2558
|
const attach = readBoolean(params.attach, true);
|
|
2898
2559
|
if (attach && !session) throw new Error("cannot attach a Codex fork without an active OpenClaw session");
|
|
2899
|
-
if (attach &&
|
|
2900
|
-
|
|
2901
|
-
if (attach && usesSupervisionConnection) throw new Error("Supervised Codex forks must stay detached; set attach=false.");
|
|
2902
|
-
if (attach) {
|
|
2903
|
-
assertCodexBindingMayBeReplaced(binding, "attaching a different native fork");
|
|
2904
|
-
assertThreadMayBeForked(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
|
|
2560
|
+
if (attach && binding?.threadId === threadId) {
|
|
2561
|
+
if (readThreadStatusType(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
|
|
2905
2562
|
threadId,
|
|
2906
2563
|
includeTurns: false
|
|
2907
|
-
},
|
|
2564
|
+
}, requestOptions())) === "active") throw new Error("cannot replace the Codex thread active in this OpenClaw turn");
|
|
2908
2565
|
}
|
|
2909
2566
|
const response = await request(pluginConfig, CODEX_CONTROL_METHODS.forkThread, {
|
|
2910
2567
|
threadId,
|
|
2911
|
-
threadSource: "user"
|
|
2912
|
-
|
|
2913
|
-
}, await requestOptions(pluginConfig));
|
|
2568
|
+
threadSource: "user"
|
|
2569
|
+
}, requestOptions());
|
|
2914
2570
|
if (!isJsonObject(response) || !isJsonObject(response.thread)) throw new Error("Codex app-server returned an invalid thread/fork response");
|
|
2915
2571
|
const forkThreadId = typeof response.thread.id === "string" && response.thread.id.trim() ? response.thread.id : void 0;
|
|
2916
2572
|
if (!forkThreadId) throw new Error("Codex app-server thread/fork response did not include a thread id");
|
|
@@ -2926,802 +2582,18 @@ function createCodexThreadsTool(options) {
|
|
|
2926
2582
|
}
|
|
2927
2583
|
})) throw new Error("Codex session binding changed before the fork could be attached");
|
|
2928
2584
|
}
|
|
2929
|
-
|
|
2585
|
+
return jsonResult({
|
|
2930
2586
|
action,
|
|
2931
2587
|
sourceThreadId: threadId,
|
|
2932
2588
|
thread: response.thread,
|
|
2933
2589
|
attached: attach
|
|
2934
|
-
};
|
|
2935
|
-
return jsonResult(mayReadRawTranscripts ? result : redactNativeThreadResponse(result));
|
|
2936
|
-
}
|
|
2937
|
-
};
|
|
2938
|
-
}
|
|
2939
|
-
//#endregion
|
|
2940
|
-
//#region extensions/codex/src/supervision-tools.ts
|
|
2941
|
-
/**
|
|
2942
|
-
* Compatibility tools for the retired Codex Supervisor plugin.
|
|
2943
|
-
*
|
|
2944
|
-
* Read operations and active-turn controls use the Codex plugin's canonical
|
|
2945
|
-
* shared app-server client. Idle threads are never resumed or started here:
|
|
2946
|
-
* continuation belongs to the Codex harness, which installs approval and tool
|
|
2947
|
-
* handlers before it starts or resumes the harness-owned Codex thread.
|
|
2948
|
-
*/
|
|
2949
|
-
/** Legacy endpoint env retained for the shipped Supervisor tool contract. */
|
|
2950
|
-
const LEGACY_CODEX_SUPERVISOR_ENDPOINTS_ENV = "OPENCLAW_CODEX_SUPERVISOR_ENDPOINTS";
|
|
2951
|
-
/** Legacy standalone-MCP transcript gate. Agent tools use canonical config. */
|
|
2952
|
-
const LEGACY_CODEX_SUPERVISOR_RAW_TRANSCRIPTS_ENV = "OPENCLAW_CODEX_SUPERVISOR_ALLOW_RAW_TRANSCRIPTS";
|
|
2953
|
-
/** Legacy standalone-MCP write gate. Agent tools use canonical config. */
|
|
2954
|
-
const LEGACY_CODEX_SUPERVISOR_WRITE_CONTROLS_ENV = "OPENCLAW_CODEX_SUPERVISOR_ALLOW_WRITE_CONTROLS";
|
|
2955
|
-
const CODEX_SUPERVISION_COMPAT_TOOL_NAMES = [
|
|
2956
|
-
"codex_endpoint_probe",
|
|
2957
|
-
"codex_sessions_list",
|
|
2958
|
-
"codex_session_read",
|
|
2959
|
-
"codex_session_send",
|
|
2960
|
-
"codex_session_interrupt"
|
|
2961
|
-
];
|
|
2962
|
-
const EmptyParamsSchema = Type.Object({}, { additionalProperties: false });
|
|
2963
|
-
const SessionsListParamsSchema = Type.Object({
|
|
2964
|
-
include_stored: Type.Optional(Type.Boolean()),
|
|
2965
|
-
max_stored_sessions: Type.Optional(Type.Integer({
|
|
2966
|
-
minimum: 1,
|
|
2967
|
-
maximum: 1e3
|
|
2968
|
-
}))
|
|
2969
|
-
}, { additionalProperties: false });
|
|
2970
|
-
const SessionReadParamsSchema = Type.Object({
|
|
2971
|
-
endpoint_id: Type.Optional(Type.String()),
|
|
2972
|
-
thread_id: Type.String(),
|
|
2973
|
-
include_turns: Type.Optional(Type.Boolean())
|
|
2974
|
-
}, { additionalProperties: false });
|
|
2975
|
-
const SessionSendParamsSchema = Type.Object({
|
|
2976
|
-
endpoint_id: Type.Optional(Type.String()),
|
|
2977
|
-
thread_id: Type.String(),
|
|
2978
|
-
text: Type.String(),
|
|
2979
|
-
mode: Type.Optional(Type.Union([
|
|
2980
|
-
Type.Literal("auto"),
|
|
2981
|
-
Type.Literal("start"),
|
|
2982
|
-
Type.Literal("steer")
|
|
2983
|
-
]))
|
|
2984
|
-
}, { additionalProperties: false });
|
|
2985
|
-
const SessionInterruptParamsSchema = Type.Object({
|
|
2986
|
-
endpoint_id: Type.Optional(Type.String()),
|
|
2987
|
-
thread_id: Type.String(),
|
|
2988
|
-
turn_id: Type.Optional(Type.String())
|
|
2989
|
-
}, { additionalProperties: false });
|
|
2990
|
-
const ALL_CODEX_THREAD_SOURCE_KINDS = [
|
|
2991
|
-
"cli",
|
|
2992
|
-
"vscode",
|
|
2993
|
-
"exec",
|
|
2994
|
-
"appServer",
|
|
2995
|
-
"subAgent",
|
|
2996
|
-
"subAgentReview",
|
|
2997
|
-
"subAgentCompact",
|
|
2998
|
-
"subAgentThreadSpawn",
|
|
2999
|
-
"subAgentOther",
|
|
3000
|
-
"unknown"
|
|
3001
|
-
];
|
|
3002
|
-
const DEFAULT_MAX_STORED_SESSIONS = 200;
|
|
3003
|
-
const PAGE_LIMIT = 100;
|
|
3004
|
-
const MAX_COMPAT_PAGINATION_PAGES = 100;
|
|
3005
|
-
const MAX_COMPAT_CURSOR_LENGTH = 4096;
|
|
3006
|
-
const MAX_COMPAT_THREAD_ID_LENGTH = 4096;
|
|
3007
|
-
var CodexSupervisionPolicyError = class extends Error {};
|
|
3008
|
-
function asRecord(value) {
|
|
3009
|
-
return isRecord(value) ? value : {};
|
|
3010
|
-
}
|
|
3011
|
-
function asRecordArray(value) {
|
|
3012
|
-
return Array.isArray(value) ? value.filter(isRecord) : [];
|
|
3013
|
-
}
|
|
3014
|
-
function readCompatNextCursor(value, method) {
|
|
3015
|
-
if (value === null || value === void 0) return;
|
|
3016
|
-
if (typeof value !== "string" || value.trim().length === 0 || value.length > MAX_COMPAT_CURSOR_LENGTH) throw new Error(`Codex ${method} returned an invalid nextCursor`);
|
|
3017
|
-
return value;
|
|
3018
|
-
}
|
|
3019
|
-
function readCompatThreadId(value, method, index) {
|
|
3020
|
-
if (typeof value !== "string" || value.trim().length === 0 || value.length > MAX_COMPAT_THREAD_ID_LENGTH) throw new Error(`Codex ${method} returned an invalid thread id at data[${index}]`);
|
|
3021
|
-
return value;
|
|
3022
|
-
}
|
|
3023
|
-
function readLoadedThreadIds(data) {
|
|
3024
|
-
if (data.length > PAGE_LIMIT) throw new Error(`Codex thread/loaded/list returned more than ${PAGE_LIMIT} entries`);
|
|
3025
|
-
return data.map((entry, index) => readCompatThreadId(entry, "thread/loaded/list", index));
|
|
3026
|
-
}
|
|
3027
|
-
function readStoredThreads(data, maxEntries) {
|
|
3028
|
-
if (data.length > maxEntries) throw new Error(`Codex thread/list returned more than ${maxEntries} entries`);
|
|
3029
|
-
return data.map((entry, index) => {
|
|
3030
|
-
if (!isRecord(entry)) throw new Error(`Codex thread/list returned an invalid entry at data[${index}]`);
|
|
3031
|
-
readCompatThreadId(entry.id, "thread/list", index);
|
|
3032
|
-
return entry;
|
|
3033
|
-
});
|
|
3034
|
-
}
|
|
3035
|
-
function readBooleanParam(params, key) {
|
|
3036
|
-
return params[key] === true;
|
|
3037
|
-
}
|
|
3038
|
-
function readIntegerParam(params, key) {
|
|
3039
|
-
const value = params[key];
|
|
3040
|
-
if (value === void 0) return;
|
|
3041
|
-
if (typeof value !== "number" || !Number.isInteger(value)) throw new Error(`${key} must be an integer`);
|
|
3042
|
-
if (value < 1 || value > 1e3) throw new Error(`${key} must be between 1 and 1000`);
|
|
3043
|
-
return value;
|
|
3044
|
-
}
|
|
3045
|
-
function readModeParam(params) {
|
|
3046
|
-
const mode = readStringParam(params, "mode");
|
|
3047
|
-
if (!mode) return;
|
|
3048
|
-
if (mode === "auto" || mode === "start" || mode === "steer") return mode;
|
|
3049
|
-
throw new Error("mode must be auto, start, or steer");
|
|
3050
|
-
}
|
|
3051
|
-
function normalizeEndpointId(value, index) {
|
|
3052
|
-
const trimmed = value.trim();
|
|
3053
|
-
return trimmed ? trimmed.replace(/[^a-zA-Z0-9_.:-]/g, "-") : `endpoint-${index + 1}`;
|
|
3054
|
-
}
|
|
3055
|
-
function normalizeConfiguredEndpoint(endpoint, index) {
|
|
3056
|
-
return {
|
|
3057
|
-
id: normalizeEndpointId(endpoint.id ?? endpoint.label ?? "", index),
|
|
3058
|
-
...endpoint.label?.trim() ? { label: endpoint.label.trim() } : {},
|
|
3059
|
-
configured: endpoint
|
|
3060
|
-
};
|
|
3061
|
-
}
|
|
3062
|
-
function parseEndpointRecord(value) {
|
|
3063
|
-
if (!isRecord(value)) return;
|
|
3064
|
-
const transport = typeof value.transport === "string" ? value.transport : void 0;
|
|
3065
|
-
const common = {
|
|
3066
|
-
...typeof value.id === "string" ? { id: value.id } : {},
|
|
3067
|
-
...typeof value.label === "string" ? { label: value.label } : {}
|
|
3068
|
-
};
|
|
3069
|
-
if (transport === "websocket" && typeof value.url === "string") return {
|
|
3070
|
-
...common,
|
|
3071
|
-
transport,
|
|
3072
|
-
url: value.url,
|
|
3073
|
-
...typeof value.authTokenEnv === "string" ? { authTokenEnv: value.authTokenEnv } : {}
|
|
3074
|
-
};
|
|
3075
|
-
if (transport === "stdio-proxy" || transport === void 0) {
|
|
3076
|
-
const args = Array.isArray(value.args) ? value.args.filter((entry) => typeof entry === "string") : void 0;
|
|
3077
|
-
return {
|
|
3078
|
-
...common,
|
|
3079
|
-
transport: "stdio-proxy",
|
|
3080
|
-
...typeof value.command === "string" ? { command: value.command } : {},
|
|
3081
|
-
...args && args.length > 0 ? { args } : {},
|
|
3082
|
-
...typeof value.cwd === "string" ? { cwd: value.cwd } : {}
|
|
3083
|
-
};
|
|
3084
|
-
}
|
|
3085
|
-
}
|
|
3086
|
-
function endpointFromToken(token, index) {
|
|
3087
|
-
const trimmed = token.trim();
|
|
3088
|
-
if (!trimmed) return;
|
|
3089
|
-
if (trimmed.startsWith("ws://") || trimmed.startsWith("wss://") || trimmed.startsWith("unix://")) return {
|
|
3090
|
-
id: normalizeEndpointId("", index),
|
|
3091
|
-
transport: "websocket",
|
|
3092
|
-
url: trimmed
|
|
3093
|
-
};
|
|
3094
|
-
if (trimmed === "local" || trimmed === "proxy" || trimmed === "stdio") return {
|
|
3095
|
-
id: "local",
|
|
3096
|
-
label: "local Codex app-server",
|
|
3097
|
-
transport: "stdio-proxy"
|
|
3098
|
-
};
|
|
3099
|
-
const separatorIndex = trimmed.indexOf("=");
|
|
3100
|
-
const id = separatorIndex >= 0 ? trimmed.slice(0, separatorIndex) : trimmed;
|
|
3101
|
-
const url = separatorIndex >= 0 ? trimmed.slice(separatorIndex + 1) : void 0;
|
|
3102
|
-
if (url?.startsWith("ws://") || url?.startsWith("wss://") || url?.startsWith("unix://")) return {
|
|
3103
|
-
id: normalizeEndpointId(id, index),
|
|
3104
|
-
transport: "websocket",
|
|
3105
|
-
url
|
|
3106
|
-
};
|
|
3107
|
-
}
|
|
3108
|
-
function requireUniqueEndpointIds(endpoints) {
|
|
3109
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3110
|
-
for (const endpoint of endpoints) {
|
|
3111
|
-
if (seen.has(endpoint.id)) throw new Error(`duplicate Codex supervisor endpoint id: ${endpoint.id}`);
|
|
3112
|
-
seen.add(endpoint.id);
|
|
3113
|
-
}
|
|
3114
|
-
return endpoints;
|
|
3115
|
-
}
|
|
3116
|
-
function readLegacyEnvEndpoints(env) {
|
|
3117
|
-
const raw = env[LEGACY_CODEX_SUPERVISOR_ENDPOINTS_ENV]?.trim();
|
|
3118
|
-
if (!raw) return;
|
|
3119
|
-
if (raw.startsWith("[")) {
|
|
3120
|
-
const parsed = JSON.parse(raw);
|
|
3121
|
-
if (!Array.isArray(parsed)) throw new Error(`${LEGACY_CODEX_SUPERVISOR_ENDPOINTS_ENV} must be a JSON array`);
|
|
3122
|
-
return parsed.map((entry) => parseEndpointRecord(entry)).filter((entry) => Boolean(entry));
|
|
3123
|
-
}
|
|
3124
|
-
return raw.split(",").map(endpointFromToken).filter((entry) => Boolean(entry));
|
|
3125
|
-
}
|
|
3126
|
-
function resolveEndpoints(pluginConfig, env, runtimeConfig) {
|
|
3127
|
-
const configured = readCodexPluginConfig(pluginConfig).supervision?.endpoints;
|
|
3128
|
-
const endpoints = configured?.length ? configured : readLegacyEnvEndpoints(env);
|
|
3129
|
-
return (endpoints ? requireUniqueEndpointIds(endpoints.map(normalizeConfiguredEndpoint)) : [{
|
|
3130
|
-
id: "local",
|
|
3131
|
-
label: "local Codex app-server"
|
|
3132
|
-
}]).map((endpoint) => {
|
|
3133
|
-
const resolved = {
|
|
3134
|
-
id: endpoint.id,
|
|
3135
|
-
connectionKey: supervisionEndpointConnectionKey({
|
|
3136
|
-
endpoint,
|
|
3137
|
-
pluginConfig,
|
|
3138
|
-
env,
|
|
3139
|
-
runtimeConfig
|
|
3140
|
-
})
|
|
3141
|
-
};
|
|
3142
|
-
if (endpoint.label !== void 0) resolved.label = endpoint.label;
|
|
3143
|
-
if (endpoint.configured !== void 0) resolved.configured = endpoint.configured;
|
|
3144
|
-
return resolved;
|
|
3145
|
-
});
|
|
3146
|
-
}
|
|
3147
|
-
function resolveEndpointStartOptions(params) {
|
|
3148
|
-
const base = resolveCodexSupervisionAppServerRuntimeOptions({
|
|
3149
|
-
pluginConfig: params.pluginConfig,
|
|
3150
|
-
env: params.env
|
|
3151
|
-
}).start;
|
|
3152
|
-
const configured = params.endpoint.configured;
|
|
3153
|
-
if (!configured) return base;
|
|
3154
|
-
if (!("url" in configured)) return {
|
|
3155
|
-
transport: "stdio",
|
|
3156
|
-
homeScope: "user",
|
|
3157
|
-
command: configured.command?.trim() || "codex",
|
|
3158
|
-
commandSource: "config",
|
|
3159
|
-
args: configured.args?.length ? [...configured.args] : [
|
|
3160
|
-
"app-server",
|
|
3161
|
-
"--listen",
|
|
3162
|
-
"stdio://"
|
|
3163
|
-
],
|
|
3164
|
-
...configured.cwd !== void 0 ? { cwd: configured.cwd } : {},
|
|
3165
|
-
headers: {}
|
|
3166
|
-
};
|
|
3167
|
-
const tokenEnv = configured.authTokenEnv?.trim();
|
|
3168
|
-
const authToken = tokenEnv ? params.env[tokenEnv]?.trim() : void 0;
|
|
3169
|
-
const startOptions = {
|
|
3170
|
-
transport: configured.url.startsWith("unix://") ? "unix" : "websocket",
|
|
3171
|
-
...configured.url.startsWith("unix://") ? { homeScope: "user" } : {},
|
|
3172
|
-
command: base.command,
|
|
3173
|
-
...base.commandSource ? { commandSource: base.commandSource } : {},
|
|
3174
|
-
...base.managedFallbackCommandPaths ? { managedFallbackCommandPaths: [...base.managedFallbackCommandPaths] } : {},
|
|
3175
|
-
args: [...base.args],
|
|
3176
|
-
url: configured.url,
|
|
3177
|
-
...authToken ? { authToken } : {},
|
|
3178
|
-
headers: {}
|
|
3179
|
-
};
|
|
3180
|
-
if (params.validateSecurity !== false) assertCodexAppServerConnectionSecurity(startOptions);
|
|
3181
|
-
return startOptions;
|
|
3182
|
-
}
|
|
3183
|
-
function supervisionEndpointConnectionKey(params) {
|
|
3184
|
-
const startOptions = resolveEndpointStartOptions({
|
|
3185
|
-
...params,
|
|
3186
|
-
validateSecurity: false
|
|
3187
|
-
});
|
|
3188
|
-
const usesNativeAuth = params.endpoint.configured !== void 0 || startOptions.homeScope === "user";
|
|
3189
|
-
const agentDir = usesNativeAuth ? void 0 : resolveDefaultAgentDir(params.runtimeConfig ?? {});
|
|
3190
|
-
const authProfileId = usesNativeAuth ? void 0 : resolveCodexAppServerAuthProfileIdForAgent({
|
|
3191
|
-
agentDir,
|
|
3192
|
-
config: params.runtimeConfig
|
|
3193
|
-
});
|
|
3194
|
-
const fallbackApiKeyCacheKey = authProfileId ? void 0 : resolveCodexAppServerFallbackApiKeyCacheKey({ startOptions });
|
|
3195
|
-
return JSON.stringify({
|
|
3196
|
-
homeScope: startOptions.homeScope ?? null,
|
|
3197
|
-
startOptions: codexAppServerStartOptionsKey(startOptions, {
|
|
3198
|
-
authProfileId,
|
|
3199
|
-
agentDir,
|
|
3200
|
-
fallbackApiKeyCacheKey
|
|
3201
|
-
})
|
|
3202
|
-
});
|
|
3203
|
-
}
|
|
3204
|
-
function createCanonicalEndpointRequest(options) {
|
|
3205
|
-
return async (endpoint, method, requestParams) => {
|
|
3206
|
-
const pluginConfig = options.getPluginConfig();
|
|
3207
|
-
const env = options.env ?? process.env;
|
|
3208
|
-
const runtime = resolveCodexSupervisionAppServerRuntimeOptions({
|
|
3209
|
-
pluginConfig,
|
|
3210
|
-
env
|
|
3211
|
-
});
|
|
3212
|
-
const config = options.getRuntimeConfig?.();
|
|
3213
|
-
const startOptions = resolveEndpointStartOptions({
|
|
3214
|
-
endpoint,
|
|
3215
|
-
pluginConfig,
|
|
3216
|
-
env
|
|
3217
|
-
});
|
|
3218
|
-
return await requestCodexAppServerJson({
|
|
3219
|
-
method,
|
|
3220
|
-
requestParams,
|
|
3221
|
-
timeoutMs: runtime.requestTimeoutMs,
|
|
3222
|
-
startOptions,
|
|
3223
|
-
...endpoint.configured || startOptions.homeScope === "user" ? { authProfileId: null } : {},
|
|
3224
|
-
...config ? { config } : {}
|
|
3225
|
-
});
|
|
3226
|
-
};
|
|
3227
|
-
}
|
|
3228
|
-
function statusType(thread) {
|
|
3229
|
-
const status = isRecord(thread.status) ? thread.status.type : thread.status;
|
|
3230
|
-
return typeof status === "string" ? status : "unknown";
|
|
3231
|
-
}
|
|
3232
|
-
function sourceLabel(value) {
|
|
3233
|
-
if (typeof value === "string") return value;
|
|
3234
|
-
if (!isRecord(value)) return;
|
|
3235
|
-
if (typeof value.custom === "string") return `custom:${value.custom}`;
|
|
3236
|
-
return Object.keys(value).toSorted()[0];
|
|
3237
|
-
}
|
|
3238
|
-
function toSession(endpointId, thread, humanAttached) {
|
|
3239
|
-
if (typeof thread.id !== "string") return;
|
|
3240
|
-
const source = sourceLabel(thread.source);
|
|
3241
|
-
return {
|
|
3242
|
-
endpointId,
|
|
3243
|
-
threadId: thread.id,
|
|
3244
|
-
status: statusType(thread),
|
|
3245
|
-
...typeof thread.sessionId === "string" ? { sessionId: thread.sessionId } : {},
|
|
3246
|
-
...typeof thread.cwd === "string" ? { cwd: thread.cwd } : {},
|
|
3247
|
-
...typeof thread.preview === "string" ? { preview: thread.preview } : {},
|
|
3248
|
-
...typeof thread.name === "string" || thread.name === null ? { name: thread.name } : {},
|
|
3249
|
-
...source ? { source } : {},
|
|
3250
|
-
...typeof thread.updatedAt === "number" ? { updatedAt: thread.updatedAt } : {},
|
|
3251
|
-
...humanAttached !== void 0 ? { humanAttached } : {}
|
|
3252
|
-
};
|
|
3253
|
-
}
|
|
3254
|
-
function threadFromRead(value) {
|
|
3255
|
-
return isRecord(value) && isRecord(value.thread) ? value.thread : void 0;
|
|
3256
|
-
}
|
|
3257
|
-
function isLoadedThreadReadMiss(error) {
|
|
3258
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
3259
|
-
return message.includes("thread not found") || message.includes("thread not loaded");
|
|
3260
|
-
}
|
|
3261
|
-
async function readThread(params) {
|
|
3262
|
-
try {
|
|
3263
|
-
const thread = threadFromRead(await params.request(params.endpoint, "thread/read", {
|
|
3264
|
-
threadId: params.threadId,
|
|
3265
|
-
includeTurns: params.includeTurns
|
|
3266
|
-
}));
|
|
3267
|
-
if (!thread) throw new Error("Codex thread/read returned an invalid response");
|
|
3268
|
-
return thread;
|
|
3269
|
-
} catch (error) {
|
|
3270
|
-
if (!params.includeTurns || !String(error).includes("not materialized yet")) throw error;
|
|
3271
|
-
const thread = threadFromRead(await params.request(params.endpoint, "thread/read", {
|
|
3272
|
-
threadId: params.threadId,
|
|
3273
|
-
includeTurns: false
|
|
3274
|
-
}));
|
|
3275
|
-
if (!thread) throw new Error("Codex thread/read returned an invalid response", { cause: error });
|
|
3276
|
-
return thread;
|
|
3277
|
-
}
|
|
3278
|
-
}
|
|
3279
|
-
async function listLoadedSessions(request, endpoint) {
|
|
3280
|
-
const sessions = [];
|
|
3281
|
-
const seenCursors = /* @__PURE__ */ new Set();
|
|
3282
|
-
let cursor;
|
|
3283
|
-
for (let pageIndex = 0; pageIndex < MAX_COMPAT_PAGINATION_PAGES; pageIndex += 1) {
|
|
3284
|
-
const listed = await request(endpoint, "thread/loaded/list", {
|
|
3285
|
-
limit: PAGE_LIMIT,
|
|
3286
|
-
...cursor ? { cursor } : {}
|
|
3287
|
-
});
|
|
3288
|
-
if (!isRecord(listed) || !Array.isArray(listed.data)) throw new Error("Codex thread/loaded/list returned an invalid response");
|
|
3289
|
-
const threadIds = readLoadedThreadIds(listed.data);
|
|
3290
|
-
for (const threadId of threadIds) {
|
|
3291
|
-
if (sessions.some((entry) => entry.threadId === threadId)) continue;
|
|
3292
|
-
try {
|
|
3293
|
-
const thread = await readThread({
|
|
3294
|
-
request,
|
|
3295
|
-
endpoint,
|
|
3296
|
-
threadId,
|
|
3297
|
-
includeTurns: false
|
|
3298
|
-
});
|
|
3299
|
-
const session = toSession(endpoint.id, thread, true);
|
|
3300
|
-
if (session) sessions.push(session);
|
|
3301
|
-
} catch (error) {
|
|
3302
|
-
if (!isLoadedThreadReadMiss(error)) throw error;
|
|
3303
|
-
}
|
|
3304
|
-
}
|
|
3305
|
-
const nextCursor = readCompatNextCursor(listed.nextCursor, "thread/loaded/list");
|
|
3306
|
-
if (nextCursor && seenCursors.has(nextCursor)) throw new Error(`Codex thread/loaded/list returned repeated cursor ${nextCursor}`);
|
|
3307
|
-
if (nextCursor) seenCursors.add(nextCursor);
|
|
3308
|
-
cursor = nextCursor;
|
|
3309
|
-
if (!cursor) break;
|
|
3310
|
-
}
|
|
3311
|
-
if (cursor) throw new Error(`Codex thread/loaded/list exceeded ${MAX_COMPAT_PAGINATION_PAGES} pages with a continuation cursor`);
|
|
3312
|
-
return sessions;
|
|
3313
|
-
}
|
|
3314
|
-
async function listStoredSessions(params) {
|
|
3315
|
-
const sessions = [];
|
|
3316
|
-
const seenCursors = /* @__PURE__ */ new Set();
|
|
3317
|
-
let cursor;
|
|
3318
|
-
for (let pageIndex = 0; pageIndex < MAX_COMPAT_PAGINATION_PAGES; pageIndex += 1) {
|
|
3319
|
-
const remaining = params.limit - sessions.length;
|
|
3320
|
-
if (remaining <= 0) break;
|
|
3321
|
-
const pageLimit = Math.min(PAGE_LIMIT, remaining);
|
|
3322
|
-
const listed = await params.request(params.endpoint, "thread/list", {
|
|
3323
|
-
archived: false,
|
|
3324
|
-
limit: pageLimit,
|
|
3325
|
-
sourceKinds: [...ALL_CODEX_THREAD_SOURCE_KINDS],
|
|
3326
|
-
modelProviders: [],
|
|
3327
|
-
sortKey: "recency_at",
|
|
3328
|
-
sortDirection: "desc",
|
|
3329
|
-
useStateDbOnly: true,
|
|
3330
|
-
...cursor ? { cursor } : {}
|
|
3331
|
-
});
|
|
3332
|
-
if (!isRecord(listed) || !Array.isArray(listed.data)) throw new Error("Codex thread/list returned an invalid response");
|
|
3333
|
-
for (const thread of readStoredThreads(listed.data, pageLimit)) {
|
|
3334
|
-
if (sessions.length >= params.limit) break;
|
|
3335
|
-
const session = toSession(params.endpoint.id, thread);
|
|
3336
|
-
if (session && !sessions.some((entry) => entry.threadId === session.threadId)) sessions.push(session);
|
|
3337
|
-
}
|
|
3338
|
-
const nextCursor = readCompatNextCursor(listed.nextCursor, "thread/list");
|
|
3339
|
-
if (nextCursor && sessions.length < params.limit && seenCursors.has(nextCursor)) throw new Error(`Codex thread/list returned repeated cursor ${nextCursor}`);
|
|
3340
|
-
if (nextCursor) seenCursors.add(nextCursor);
|
|
3341
|
-
cursor = nextCursor;
|
|
3342
|
-
if (!cursor || sessions.length >= params.limit) break;
|
|
3343
|
-
}
|
|
3344
|
-
if (cursor && sessions.length < params.limit) throw new Error(`Codex thread/list exceeded ${MAX_COMPAT_PAGINATION_PAGES} pages with a continuation cursor`);
|
|
3345
|
-
return sessions;
|
|
3346
|
-
}
|
|
3347
|
-
async function listSessionSnapshot(params) {
|
|
3348
|
-
const sessions = [];
|
|
3349
|
-
const errors = [];
|
|
3350
|
-
for (const endpoint of params.endpoints) try {
|
|
3351
|
-
const loaded = await listLoadedSessions(params.request, endpoint);
|
|
3352
|
-
sessions.push(...loaded);
|
|
3353
|
-
if (params.includeStored) {
|
|
3354
|
-
const stored = await listStoredSessions({
|
|
3355
|
-
request: params.request,
|
|
3356
|
-
endpoint,
|
|
3357
|
-
limit: params.maxStoredSessions ?? DEFAULT_MAX_STORED_SESSIONS
|
|
3358
2590
|
});
|
|
3359
|
-
for (const session of stored) if (!sessions.some((entry) => entry.endpointId === endpoint.id && entry.threadId === session.threadId)) sessions.push(session);
|
|
3360
2591
|
}
|
|
3361
|
-
} catch (error) {
|
|
3362
|
-
if (error instanceof CodexSupervisionPolicyError) throw error;
|
|
3363
|
-
errors.push({
|
|
3364
|
-
endpointId: endpoint.id,
|
|
3365
|
-
ok: false,
|
|
3366
|
-
detail: error instanceof Error ? error.message : String(error)
|
|
3367
|
-
});
|
|
3368
|
-
}
|
|
3369
|
-
return {
|
|
3370
|
-
sessions,
|
|
3371
|
-
errors
|
|
3372
|
-
};
|
|
3373
|
-
}
|
|
3374
|
-
async function resolveEndpointForThread(params) {
|
|
3375
|
-
if (params.endpointId) {
|
|
3376
|
-
const endpoint = params.endpoints.find((entry) => entry.id === params.endpointId);
|
|
3377
|
-
if (!endpoint) throw new Error(`Unknown Codex supervisor endpoint: ${params.endpointId}`);
|
|
3378
|
-
return endpoint;
|
|
3379
|
-
}
|
|
3380
|
-
const matches = [];
|
|
3381
|
-
for (const endpoint of params.endpoints) try {
|
|
3382
|
-
if ((await readThread({
|
|
3383
|
-
request: params.request,
|
|
3384
|
-
endpoint,
|
|
3385
|
-
threadId: params.threadId,
|
|
3386
|
-
includeTurns: false
|
|
3387
|
-
})).id === params.threadId) matches.push(endpoint);
|
|
3388
|
-
} catch (error) {
|
|
3389
|
-
if (error instanceof CodexSupervisionPolicyError) throw error;
|
|
3390
|
-
if (!isLoadedThreadReadMiss(error)) continue;
|
|
3391
|
-
}
|
|
3392
|
-
if (matches.length === 1) return expectDefined(matches[0], "single matching Codex supervision endpoint");
|
|
3393
|
-
if (matches.length > 1) throw new Error(`Codex thread id is ambiguous across endpoints: ${params.threadId}`);
|
|
3394
|
-
throw new Error(`Codex thread not found: ${params.threadId}`);
|
|
3395
|
-
}
|
|
3396
|
-
function findInProgressTurnId(thread) {
|
|
3397
|
-
const turns = asRecordArray(thread.turns);
|
|
3398
|
-
for (const turn of turns.toReversed()) if (turn.status === "inProgress" && typeof turn.id === "string") return turn.id;
|
|
3399
|
-
}
|
|
3400
|
-
async function resolveInProgressTurnId(params) {
|
|
3401
|
-
const inline = findInProgressTurnId(params.thread);
|
|
3402
|
-
if (inline) return inline;
|
|
3403
|
-
try {
|
|
3404
|
-
const response = await params.request(params.endpoint, "thread/turns/list", {
|
|
3405
|
-
threadId: params.threadId,
|
|
3406
|
-
limit: 10,
|
|
3407
|
-
sortDirection: "desc",
|
|
3408
|
-
itemsView: "summary"
|
|
3409
|
-
});
|
|
3410
|
-
return isRecord(response) ? findInProgressTurnId({ turns: response.data }) : void 0;
|
|
3411
|
-
} catch (error) {
|
|
3412
|
-
if (error instanceof CodexSupervisionPolicyError) throw error;
|
|
3413
|
-
return;
|
|
3414
|
-
}
|
|
3415
|
-
}
|
|
3416
|
-
function redactString(value) {
|
|
3417
|
-
return value.replace(/\b(?:sk|glpat|xox[baprs])-[-_a-zA-Z0-9]{12,}\b/g, "[redacted]").replace(/\b(?:ghp|gho|ghu|ghs)_[-_a-zA-Z0-9]{12,}\b/g, "[redacted]").replace(/\bBearer\s+[-._~+/a-zA-Z0-9]+=*/g, "Bearer [redacted]");
|
|
3418
|
-
}
|
|
3419
|
-
/** Redacts secret-bearing fields before legacy tool results leave the plugin. */
|
|
3420
|
-
function redactCodexSupervisionValue(value, key = "") {
|
|
3421
|
-
if (typeof value === "string") return /authorization|password|secret|token|api[-_]?key/i.test(key) ? "[redacted]" : redactString(value);
|
|
3422
|
-
if (Array.isArray(value)) return value.map((entry) => redactCodexSupervisionValue(entry));
|
|
3423
|
-
if (!isRecord(value)) return value;
|
|
3424
|
-
return Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => [entryKey, redactCodexSupervisionValue(entryValue, entryKey)]));
|
|
3425
|
-
}
|
|
3426
|
-
function redactEndpointUrl(value) {
|
|
3427
|
-
if (value.startsWith("unix://")) return "unix://";
|
|
3428
|
-
try {
|
|
3429
|
-
const url = new URL(value);
|
|
3430
|
-
url.username = "";
|
|
3431
|
-
url.password = "";
|
|
3432
|
-
if (url.search) url.search = "?[redacted]";
|
|
3433
|
-
return url.toString();
|
|
3434
|
-
} catch {
|
|
3435
|
-
return "[redacted]";
|
|
3436
|
-
}
|
|
3437
|
-
}
|
|
3438
|
-
function endpointResult(endpoint, pluginConfig, env) {
|
|
3439
|
-
const configured = endpoint.configured;
|
|
3440
|
-
if (configured && (configured.transport === "stdio-proxy" || configured.transport === void 0)) return {
|
|
3441
|
-
id: endpoint.id,
|
|
3442
|
-
transport: "stdio-proxy",
|
|
3443
|
-
...endpoint.label ? { label: endpoint.label } : {}
|
|
3444
|
-
};
|
|
3445
|
-
if (configured?.transport === "websocket") return {
|
|
3446
|
-
id: endpoint.id,
|
|
3447
|
-
transport: "websocket",
|
|
3448
|
-
...endpoint.label ? { label: endpoint.label } : {},
|
|
3449
|
-
url: redactEndpointUrl(configured.url)
|
|
3450
|
-
};
|
|
3451
|
-
const start = resolveCodexSupervisionAppServerRuntimeOptions({
|
|
3452
|
-
pluginConfig,
|
|
3453
|
-
env
|
|
3454
|
-
}).start;
|
|
3455
|
-
return {
|
|
3456
|
-
id: endpoint.id,
|
|
3457
|
-
transport: start.transport === "stdio" ? "stdio-proxy" : "websocket",
|
|
3458
|
-
...endpoint.label ? { label: endpoint.label } : {},
|
|
3459
|
-
...start.transport === "stdio" ? {} : { url: redactEndpointUrl(start.transport === "unix" ? start.url ?? "unix://" : start.url ?? "") }
|
|
3460
|
-
};
|
|
3461
|
-
}
|
|
3462
|
-
function sanitizeSessionListResult(result, includeTranscriptDerivedFields) {
|
|
3463
|
-
return {
|
|
3464
|
-
sessions: result.sessions.map((session) => {
|
|
3465
|
-
const sanitized = redactCodexSupervisionValue(session);
|
|
3466
|
-
if (!includeTranscriptDerivedFields) {
|
|
3467
|
-
delete sanitized.preview;
|
|
3468
|
-
delete sanitized.name;
|
|
3469
|
-
}
|
|
3470
|
-
return sanitized;
|
|
3471
|
-
}),
|
|
3472
|
-
errors: includeTranscriptDerivedFields ? redactCodexSupervisionValue(result.errors) : result.errors.map(({ endpointId, ok }) => ({
|
|
3473
|
-
endpointId,
|
|
3474
|
-
ok
|
|
3475
|
-
}))
|
|
3476
|
-
};
|
|
3477
|
-
}
|
|
3478
|
-
function requireSupervisionEnabled(pluginConfig) {
|
|
3479
|
-
if (readCodexPluginConfig(pluginConfig).supervision?.enabled !== true) throw new CodexSupervisionPolicyError("Codex supervision is disabled in the codex plugin config.");
|
|
3480
|
-
}
|
|
3481
|
-
function requireOwnerAccess(options) {
|
|
3482
|
-
if (!options.senderIsOwner) throw new CodexSupervisionPolicyError("Codex supervision compatibility tools require an owner-authorized sender.");
|
|
3483
|
-
}
|
|
3484
|
-
function resolveToolPolicy(options, pluginConfig) {
|
|
3485
|
-
const config = readCodexPluginConfig(pluginConfig).supervision;
|
|
3486
|
-
const env = options.env ?? process.env;
|
|
3487
|
-
return {
|
|
3488
|
-
allowRawTranscripts: config?.allowRawTranscripts === true || options.useLegacyMcpPolicyEnv === true && env[LEGACY_CODEX_SUPERVISOR_RAW_TRANSCRIPTS_ENV] === "1",
|
|
3489
|
-
allowWriteControls: config?.allowWriteControls === true || options.useLegacyMcpPolicyEnv === true && env[LEGACY_CODEX_SUPERVISOR_WRITE_CONTROLS_ENV] === "1"
|
|
3490
|
-
};
|
|
3491
|
-
}
|
|
3492
|
-
function requireRawTranscriptAccess(options, pluginConfig) {
|
|
3493
|
-
if (!resolveToolPolicy(options, pluginConfig).allowRawTranscripts) throw new CodexSupervisionPolicyError("Codex session reads are disabled for this codex plugin supervision config.");
|
|
3494
|
-
}
|
|
3495
|
-
function requireWriteAccess(options, pluginConfig) {
|
|
3496
|
-
if (!resolveToolPolicy(options, pluginConfig).allowWriteControls) throw new CodexSupervisionPolicyError("Codex write controls are disabled for this codex plugin supervision config.");
|
|
3497
|
-
}
|
|
3498
|
-
function requireLiveToolPolicy(options, policy) {
|
|
3499
|
-
requireOwnerAccess(options);
|
|
3500
|
-
const pluginConfig = options.getPluginConfig();
|
|
3501
|
-
requireSupervisionEnabled(pluginConfig);
|
|
3502
|
-
if (policy === "raw-transcripts") requireRawTranscriptAccess(options, pluginConfig);
|
|
3503
|
-
else if (policy === "write-controls") requireWriteAccess(options, pluginConfig);
|
|
3504
|
-
return {
|
|
3505
|
-
pluginConfig,
|
|
3506
|
-
endpoints: resolveEndpoints(pluginConfig, options.env ?? process.env, options.getRuntimeConfig?.())
|
|
3507
|
-
};
|
|
3508
|
-
}
|
|
3509
|
-
function requireCurrentEndpoint(options, policy, endpoint) {
|
|
3510
|
-
const { endpoints } = requireLiveToolPolicy(options, policy);
|
|
3511
|
-
const currentEndpoint = endpoints.find((candidate) => candidate.id === endpoint.id);
|
|
3512
|
-
if (!currentEndpoint || currentEndpoint.connectionKey !== endpoint.connectionKey) throw new CodexSupervisionPolicyError(`Codex supervision endpoint ${endpoint.id} was removed or changed during the request.`);
|
|
3513
|
-
return currentEndpoint;
|
|
3514
|
-
}
|
|
3515
|
-
function requireCurrentEndpointSet(options, expected) {
|
|
3516
|
-
const current = requireLiveToolPolicy(options, "enabled");
|
|
3517
|
-
if (!(current.endpoints.length === expected.length && expected.every((endpoint) => current.endpoints.some((candidate) => candidate.id === endpoint.id && candidate.connectionKey === endpoint.connectionKey)))) throw new CodexSupervisionPolicyError("Codex supervision endpoint configuration changed during the request.");
|
|
3518
|
-
return { pluginConfig: current.pluginConfig };
|
|
3519
|
-
}
|
|
3520
|
-
function createPolicyGuardedRequest(options, request, policy) {
|
|
3521
|
-
return async (endpoint, method, params) => {
|
|
3522
|
-
return await request(requireCurrentEndpoint(options, policy, endpoint), method, params);
|
|
3523
|
-
};
|
|
3524
|
-
}
|
|
3525
|
-
function idleContinuationError(threadId) {
|
|
3526
|
-
return /* @__PURE__ */ new Error(`Codex thread ${threadId} is idle. Continue it from Codex Sessions so OpenClaw can install the Codex harness approval and tool handlers before resume.`);
|
|
3527
|
-
}
|
|
3528
|
-
/** Builds the five shipped Codex Supervisor compatibility tools. */
|
|
3529
|
-
function createCodexSupervisionTools(options) {
|
|
3530
|
-
const baseRequest = options.request ?? createCanonicalEndpointRequest(options);
|
|
3531
|
-
const request = createPolicyGuardedRequest(options, baseRequest, "enabled");
|
|
3532
|
-
const rawTranscriptRequest = createPolicyGuardedRequest(options, baseRequest, "raw-transcripts");
|
|
3533
|
-
const writeRequest = createPolicyGuardedRequest(options, baseRequest, "write-controls");
|
|
3534
|
-
const current = () => {
|
|
3535
|
-
return requireLiveToolPolicy(options, "enabled");
|
|
3536
2592
|
};
|
|
3537
|
-
return [
|
|
3538
|
-
{
|
|
3539
|
-
name: "codex_endpoint_probe",
|
|
3540
|
-
label: "Codex Endpoint Probe",
|
|
3541
|
-
description: "Check configured Codex app-server endpoints.",
|
|
3542
|
-
parameters: EmptyParamsSchema,
|
|
3543
|
-
execute: async () => {
|
|
3544
|
-
const { pluginConfig, endpoints } = current();
|
|
3545
|
-
const health = [];
|
|
3546
|
-
for (const endpoint of endpoints) try {
|
|
3547
|
-
await request(endpoint, "thread/loaded/list", { limit: 1 });
|
|
3548
|
-
health.push({
|
|
3549
|
-
endpointId: endpoint.id,
|
|
3550
|
-
ok: true
|
|
3551
|
-
});
|
|
3552
|
-
} catch (error) {
|
|
3553
|
-
if (error instanceof CodexSupervisionPolicyError) throw error;
|
|
3554
|
-
health.push({
|
|
3555
|
-
endpointId: endpoint.id,
|
|
3556
|
-
ok: false
|
|
3557
|
-
});
|
|
3558
|
-
}
|
|
3559
|
-
requireCurrentEndpointSet(options, endpoints);
|
|
3560
|
-
return jsonResult({
|
|
3561
|
-
summary: `codex endpoints: ${health.filter((entry) => entry.ok).length}/${health.length} ok`,
|
|
3562
|
-
endpoints: endpoints.map((endpoint) => endpointResult(endpoint, pluginConfig, options.env ?? process.env)),
|
|
3563
|
-
health
|
|
3564
|
-
});
|
|
3565
|
-
}
|
|
3566
|
-
},
|
|
3567
|
-
{
|
|
3568
|
-
name: "codex_sessions_list",
|
|
3569
|
-
label: "Codex Sessions List",
|
|
3570
|
-
description: "List Codex sessions visible to the OpenClaw supervisor.",
|
|
3571
|
-
parameters: SessionsListParamsSchema,
|
|
3572
|
-
execute: async (_toolCallId, rawParams) => {
|
|
3573
|
-
const params = asRecord(rawParams);
|
|
3574
|
-
const { endpoints } = current();
|
|
3575
|
-
const result = await listSessionSnapshot({
|
|
3576
|
-
endpoints,
|
|
3577
|
-
request,
|
|
3578
|
-
includeStored: readBooleanParam(params, "include_stored"),
|
|
3579
|
-
maxStoredSessions: readIntegerParam(params, "max_stored_sessions")
|
|
3580
|
-
});
|
|
3581
|
-
const { pluginConfig } = requireCurrentEndpointSet(options, endpoints);
|
|
3582
|
-
return jsonResult({
|
|
3583
|
-
summary: `codex sessions: ${result.sessions.length}`,
|
|
3584
|
-
...sanitizeSessionListResult(result, resolveToolPolicy(options, pluginConfig).allowRawTranscripts)
|
|
3585
|
-
});
|
|
3586
|
-
}
|
|
3587
|
-
},
|
|
3588
|
-
{
|
|
3589
|
-
name: "codex_session_read",
|
|
3590
|
-
label: "Codex Session Read",
|
|
3591
|
-
description: "Read one Codex session transcript from app-server.",
|
|
3592
|
-
parameters: SessionReadParamsSchema,
|
|
3593
|
-
execute: async (_toolCallId, rawParams) => {
|
|
3594
|
-
const { endpoints, pluginConfig } = current();
|
|
3595
|
-
requireRawTranscriptAccess(options, pluginConfig);
|
|
3596
|
-
const params = asRecord(rawParams);
|
|
3597
|
-
const threadId = readStringParam(params, "thread_id", { required: true });
|
|
3598
|
-
const endpoint = await resolveEndpointForThread({
|
|
3599
|
-
endpoints,
|
|
3600
|
-
request: rawTranscriptRequest,
|
|
3601
|
-
endpointId: readStringParam(params, "endpoint_id"),
|
|
3602
|
-
threadId
|
|
3603
|
-
});
|
|
3604
|
-
const thread = await readThread({
|
|
3605
|
-
request: rawTranscriptRequest,
|
|
3606
|
-
endpoint,
|
|
3607
|
-
threadId,
|
|
3608
|
-
includeTurns: readBooleanParam(params, "include_turns")
|
|
3609
|
-
});
|
|
3610
|
-
requireCurrentEndpoint(options, "raw-transcripts", endpoint);
|
|
3611
|
-
return jsonResult({
|
|
3612
|
-
summary: `codex session: ${threadId}`,
|
|
3613
|
-
response: redactCodexSupervisionValue({ thread })
|
|
3614
|
-
});
|
|
3615
|
-
}
|
|
3616
|
-
},
|
|
3617
|
-
{
|
|
3618
|
-
name: "codex_session_send",
|
|
3619
|
-
label: "Codex Session Send",
|
|
3620
|
-
description: "Steer an active Codex turn. Idle sessions must be continued through Codex Sessions.",
|
|
3621
|
-
parameters: SessionSendParamsSchema,
|
|
3622
|
-
execute: async (_toolCallId, rawParams) => {
|
|
3623
|
-
const { endpoints, pluginConfig } = current();
|
|
3624
|
-
requireWriteAccess(options, pluginConfig);
|
|
3625
|
-
const params = asRecord(rawParams);
|
|
3626
|
-
const threadId = readStringParam(params, "thread_id", { required: true });
|
|
3627
|
-
const text = readStringParam(params, "text", {
|
|
3628
|
-
required: true,
|
|
3629
|
-
allowEmpty: false
|
|
3630
|
-
});
|
|
3631
|
-
if ((readModeParam(params) ?? "auto") === "start") throw idleContinuationError(threadId);
|
|
3632
|
-
const endpoint = await resolveEndpointForThread({
|
|
3633
|
-
endpoints,
|
|
3634
|
-
request: writeRequest,
|
|
3635
|
-
endpointId: readStringParam(params, "endpoint_id"),
|
|
3636
|
-
threadId
|
|
3637
|
-
});
|
|
3638
|
-
const thread = await readThread({
|
|
3639
|
-
request: writeRequest,
|
|
3640
|
-
endpoint,
|
|
3641
|
-
threadId,
|
|
3642
|
-
includeTurns: true
|
|
3643
|
-
});
|
|
3644
|
-
requireCurrentEndpoint(options, "write-controls", endpoint);
|
|
3645
|
-
if (statusType(thread) !== "active") throw idleContinuationError(threadId);
|
|
3646
|
-
const turnId = await resolveInProgressTurnId({
|
|
3647
|
-
request: writeRequest,
|
|
3648
|
-
endpoint,
|
|
3649
|
-
thread,
|
|
3650
|
-
threadId
|
|
3651
|
-
});
|
|
3652
|
-
if (!turnId) throw new Error(`Codex thread ${threadId} is active but no in-progress turn is readable`);
|
|
3653
|
-
await writeRequest(endpoint, "turn/steer", {
|
|
3654
|
-
threadId,
|
|
3655
|
-
expectedTurnId: turnId,
|
|
3656
|
-
input: [{
|
|
3657
|
-
type: "text",
|
|
3658
|
-
text,
|
|
3659
|
-
text_elements: []
|
|
3660
|
-
}]
|
|
3661
|
-
});
|
|
3662
|
-
const result = {
|
|
3663
|
-
endpointId: endpoint.id,
|
|
3664
|
-
threadId,
|
|
3665
|
-
mode: "steer",
|
|
3666
|
-
turnId
|
|
3667
|
-
};
|
|
3668
|
-
return jsonResult({
|
|
3669
|
-
summary: `codex steer: ${turnId}`,
|
|
3670
|
-
result
|
|
3671
|
-
});
|
|
3672
|
-
}
|
|
3673
|
-
},
|
|
3674
|
-
{
|
|
3675
|
-
name: "codex_session_interrupt",
|
|
3676
|
-
label: "Codex Session Interrupt",
|
|
3677
|
-
description: "Interrupt an active Codex turn.",
|
|
3678
|
-
parameters: SessionInterruptParamsSchema,
|
|
3679
|
-
execute: async (_toolCallId, rawParams) => {
|
|
3680
|
-
const { endpoints, pluginConfig } = current();
|
|
3681
|
-
requireWriteAccess(options, pluginConfig);
|
|
3682
|
-
const params = asRecord(rawParams);
|
|
3683
|
-
const threadId = readStringParam(params, "thread_id", { required: true });
|
|
3684
|
-
const endpoint = await resolveEndpointForThread({
|
|
3685
|
-
endpoints,
|
|
3686
|
-
request: writeRequest,
|
|
3687
|
-
endpointId: readStringParam(params, "endpoint_id"),
|
|
3688
|
-
threadId
|
|
3689
|
-
});
|
|
3690
|
-
const thread = await readThread({
|
|
3691
|
-
request: writeRequest,
|
|
3692
|
-
endpoint,
|
|
3693
|
-
threadId,
|
|
3694
|
-
includeTurns: true
|
|
3695
|
-
});
|
|
3696
|
-
requireCurrentEndpoint(options, "write-controls", endpoint);
|
|
3697
|
-
if (statusType(thread) !== "active") throw new Error(`Codex thread ${threadId} has no active turn to interrupt`);
|
|
3698
|
-
const turnId = readStringParam(params, "turn_id") ?? await resolveInProgressTurnId({
|
|
3699
|
-
request: writeRequest,
|
|
3700
|
-
endpoint,
|
|
3701
|
-
thread,
|
|
3702
|
-
threadId
|
|
3703
|
-
});
|
|
3704
|
-
if (!turnId) throw new Error(`Codex thread ${threadId} has no readable in-progress turn`);
|
|
3705
|
-
await writeRequest(endpoint, "turn/interrupt", {
|
|
3706
|
-
threadId,
|
|
3707
|
-
turnId
|
|
3708
|
-
});
|
|
3709
|
-
const result = {
|
|
3710
|
-
endpointId: endpoint.id,
|
|
3711
|
-
threadId,
|
|
3712
|
-
turnId
|
|
3713
|
-
};
|
|
3714
|
-
return jsonResult({
|
|
3715
|
-
summary: `codex interrupted: ${turnId}`,
|
|
3716
|
-
result
|
|
3717
|
-
});
|
|
3718
|
-
}
|
|
3719
|
-
}
|
|
3720
|
-
];
|
|
3721
2593
|
}
|
|
3722
2594
|
//#endregion
|
|
3723
2595
|
//#region extensions/codex/src/web-search-provider.ts
|
|
3724
|
-
const loadCodexWebSearchRuntime = createLazyRuntimeModule(() => import("
|
|
2596
|
+
const loadCodexWebSearchRuntime = createLazyRuntimeModule(() => import("./.setup/web-search-provider.runtime-Ce2zOHxW.mjs"));
|
|
3725
2597
|
const CodexWebSearchSchema = {
|
|
3726
2598
|
type: "object",
|
|
3727
2599
|
properties: { query: {
|
|
@@ -3763,68 +2635,21 @@ const ENDED_SESSION_REASONS = /* @__PURE__ */ new Set([
|
|
|
3763
2635
|
var codex_default = definePluginEntry({
|
|
3764
2636
|
id: "codex",
|
|
3765
2637
|
name: "Codex",
|
|
3766
|
-
description: "Codex app-server harness and
|
|
2638
|
+
description: "Codex app-server harness and Codex-managed GPT model catalog.",
|
|
3767
2639
|
register(api) {
|
|
3768
2640
|
const resolveCurrentConfig = () => api.runtime.config?.current ? api.runtime.config.current() : void 0;
|
|
3769
|
-
const
|
|
3770
|
-
|
|
3771
|
-
if (!liveConfig) return api.pluginConfig;
|
|
3772
|
-
const livePluginConfig = resolveLivePluginConfigObject(() => liveConfig, "codex", api.pluginConfig);
|
|
3773
|
-
if (!resolveEffectiveEnableState({
|
|
3774
|
-
id: "codex",
|
|
3775
|
-
origin: "bundled",
|
|
3776
|
-
config: normalizePluginsConfig(liveConfig.plugins),
|
|
3777
|
-
rootConfig: liveConfig,
|
|
3778
|
-
enabledByDefault: livePluginConfig !== void 0
|
|
3779
|
-
}).enabled) return;
|
|
3780
|
-
return livePluginConfig;
|
|
3781
|
-
};
|
|
3782
|
-
const resolveCurrentPluginConfig = () => resolvePluginConfig(resolveCurrentConfig);
|
|
3783
|
-
let bindingStateStore;
|
|
3784
|
-
const openBindingStateStore = () => bindingStateStore ??= api.runtime.state.openSyncKeyedStore({
|
|
2641
|
+
const resolveCurrentPluginConfig = () => resolveLivePluginConfigObject(resolveCurrentConfig, "codex", api.pluginConfig) ?? api.pluginConfig;
|
|
2642
|
+
const bindingStore = createLazyCodexAppServerBindingStore(api.runtime.state.openSyncKeyedStore({
|
|
3785
2643
|
namespace: CODEX_APP_SERVER_BINDING_NAMESPACE,
|
|
3786
2644
|
maxEntries: CODEX_APP_SERVER_BINDING_MAX_ENTRIES,
|
|
3787
2645
|
overflowPolicy: "reject-new"
|
|
3788
|
-
});
|
|
3789
|
-
const bindingStore = createLazyCodexAppServerBindingStore({
|
|
3790
|
-
entries: () => openBindingStateStore().entries(),
|
|
3791
|
-
lookup: (key) => openBindingStateStore().lookup(key),
|
|
3792
|
-
get update() {
|
|
3793
|
-
const store = openBindingStateStore();
|
|
3794
|
-
return store.update?.bind(store);
|
|
3795
|
-
}
|
|
3796
|
-
});
|
|
3797
|
-
registerCodexCliMetadata(api);
|
|
3798
|
-
const sessionCatalogControl = createCodexSessionCatalogControl({
|
|
3799
|
-
getPluginConfig: resolveCurrentPluginConfig,
|
|
3800
|
-
getRuntimeConfig: resolveCurrentConfig
|
|
3801
|
-
});
|
|
3802
|
-
if (readCodexPluginConfig(resolveCurrentPluginConfig()).sessionCatalog?.enabled !== false) {
|
|
3803
|
-
codexSessionCatalogRuntime.register({
|
|
3804
|
-
api,
|
|
3805
|
-
bindingStore,
|
|
3806
|
-
control: sessionCatalogControl,
|
|
3807
|
-
getRuntimeConfig: resolveCurrentConfig
|
|
3808
|
-
});
|
|
3809
|
-
for (const command of createCodexSessionCatalogNodeHostCommands(sessionCatalogControl)) api.registerNodeHostCommand(command);
|
|
3810
|
-
}
|
|
3811
|
-
for (const policy of createCodexSessionCatalogNodeInvokePolicies()) api.registerNodeInvokePolicy(policy);
|
|
3812
|
-
if (readCodexPluginConfig(resolveCurrentPluginConfig()).supervision?.enabled === true) api.registerTool((context) => {
|
|
3813
|
-
if (context.senderIsOwner !== true) return [];
|
|
3814
|
-
const resolveToolRuntimeConfig = () => context.getRuntimeConfig?.() ?? context.runtimeConfig ?? context.config ?? resolveCurrentConfig();
|
|
3815
|
-
return createCodexSupervisionTools({
|
|
3816
|
-
getPluginConfig: () => resolvePluginConfig(resolveToolRuntimeConfig),
|
|
3817
|
-
getRuntimeConfig: resolveToolRuntimeConfig,
|
|
3818
|
-
senderIsOwner: context.senderIsOwner
|
|
3819
|
-
});
|
|
3820
|
-
}, { names: [...CODEX_SUPERVISION_COMPAT_TOOL_NAMES] });
|
|
2646
|
+
}));
|
|
3821
2647
|
api.registerAgentHarness(createCodexAppServerAgentHarness({
|
|
3822
2648
|
bindingStore,
|
|
3823
|
-
sessionCatalogControl,
|
|
3824
2649
|
resolveConfig: resolveCurrentConfig,
|
|
3825
|
-
resolvePluginConfig: resolveCurrentPluginConfig
|
|
3826
|
-
runtime: api.runtime
|
|
2650
|
+
resolvePluginConfig: resolveCurrentPluginConfig
|
|
3827
2651
|
}));
|
|
2652
|
+
api.registerProvider(buildCodexProvider({ pluginConfig: api.pluginConfig }));
|
|
3828
2653
|
api.registerMediaUnderstandingProvider(buildCodexMediaUnderstandingProvider({ pluginConfig: api.pluginConfig }));
|
|
3829
2654
|
api.registerWebSearchProvider(createCodexWebSearchProvider({ resolvePluginConfig: resolveCurrentPluginConfig }));
|
|
3830
2655
|
api.registerMigrationProvider(buildCodexMigrationProvider({ runtime: api.runtime }));
|
|
@@ -3895,7 +2720,7 @@ var codex_default = definePluginEntry({
|
|
|
3895
2720
|
}
|
|
3896
2721
|
}
|
|
3897
2722
|
}));
|
|
3898
|
-
api.on("inbound_claim", (event, ctx) =>
|
|
2723
|
+
api.on("inbound_claim", (event, ctx) => handleCodexConversationInboundClaim(event, ctx, {
|
|
3899
2724
|
bindingStore,
|
|
3900
2725
|
pluginConfig: resolveCurrentPluginConfig(),
|
|
3901
2726
|
config: resolveCurrentConfig(),
|
|
@@ -3904,14 +2729,14 @@ var codex_default = definePluginEntry({
|
|
|
3904
2729
|
...params
|
|
3905
2730
|
})
|
|
3906
2731
|
}));
|
|
3907
|
-
api.onConversationBindingResolved?.((event) =>
|
|
2732
|
+
api.onConversationBindingResolved?.((event) => handleCodexConversationBindingResolved(event, { bindingStore }));
|
|
3908
2733
|
api.on("after_compaction", async (event, ctx) => {
|
|
3909
2734
|
const previousSessionId = event.previousSessionId?.trim();
|
|
3910
2735
|
const sessionId = ctx.sessionId?.trim();
|
|
3911
2736
|
if (!previousSessionId || !sessionId || previousSessionId === sessionId) return;
|
|
3912
2737
|
const config = resolveCurrentConfig();
|
|
3913
2738
|
const sessionKey = ctx.sessionKey?.trim();
|
|
3914
|
-
const { sessionBindingIdentity } = await import("
|
|
2739
|
+
const { sessionBindingIdentity } = await import("./.setup/session-binding-DuihlMJc.mjs").then((n) => n.c);
|
|
3915
2740
|
const identity = sessionBindingIdentity({
|
|
3916
2741
|
sessionId,
|
|
3917
2742
|
...sessionKey ? { sessionKey } : {},
|
|
@@ -3924,12 +2749,8 @@ var codex_default = definePluginEntry({
|
|
|
3924
2749
|
api.on("session_end", async (event, ctx) => {
|
|
3925
2750
|
if (!event.reason || !ENDED_SESSION_REASONS.has(event.reason)) return;
|
|
3926
2751
|
const sessionKey = event.sessionKey ?? ctx.sessionKey;
|
|
3927
|
-
const endedSessionKey = sessionKey?.trim();
|
|
3928
|
-
const nextSessionKey = event.nextSessionKey?.trim();
|
|
3929
|
-
if (endedSessionKey && nextSessionKey && nextSessionKey !== endedSessionKey) return;
|
|
3930
|
-
if (event.nextSessionId?.trim() === event.sessionId.trim()) return;
|
|
3931
2752
|
const config = resolveCurrentConfig();
|
|
3932
|
-
const { sessionBindingIdentity } = await import("
|
|
2753
|
+
const { sessionBindingIdentity } = await import("./.setup/session-binding-DuihlMJc.mjs").then((n) => n.c);
|
|
3933
2754
|
await bindingStore.retireSessionGeneration(sessionBindingIdentity({
|
|
3934
2755
|
sessionId: event.sessionId,
|
|
3935
2756
|
...sessionKey ? { sessionKey } : {},
|