@openclaw/codex 2026.7.2-beta.7 → 2026.8.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{app-server-policy-BSaO2JIT.js → app-server-policy-zcBDc1JU.js} +1 -1
- package/dist/attempt-client-cleanup-DMLXUfa8.js +735 -0
- package/dist/{notification-correlation-CU6_AnOb.js → binding-connection-I5Bo-AsX.js} +3 -37
- package/dist/{bounded-turn-D2ZAgZVz.js → bounded-turn-qgZmzy_4.js} +50 -49
- package/dist/{capabilities-BFDTVYr4.js → capabilities-CybGI1oz.js} +8 -2
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters-C336eRVS.js → command-formatters-B1AEz8ZA.js} +18 -18
- package/dist/{command-handlers-DAHUSry9.js → command-handlers-Db8pu_ds.js} +484 -95
- package/dist/{compact-D_uvHurS.js → compact-LspQaGfl.js} +213 -199
- package/dist/{conversation-binding-data-BHRpYbX3.js → conversation-binding-data-CW2GuupV.js} +6 -10
- package/dist/doctor-contract-api.js +40 -43
- package/dist/{dynamic-tools-D2gYkSBO.js → dynamic-tools-BVdyWkUD.js} +2597 -1221
- package/dist/{effective-mcp-catalog-Bbd98Inw.js → effective-mcp-catalog-DIIhAtOZ.js} +20 -15
- package/dist/{event-projector-assistant-message-DTgnPPVF.js → event-projector-assistant-message-BwxV4d6N.js} +11 -23
- package/dist/{attempt-notifications-AP-a-Q5k.js → event-projector-usage-e5huIQWu.js} +113 -29
- package/dist/harness.js +80 -14
- package/dist/image-payload-sanitizer-CnfoUPCE.js +65 -0
- package/dist/index.js +591 -240
- package/dist/isolated-completion-C58NbL40.js +75 -0
- package/dist/local-runtime-attribution-B_2mnqhb.js +19 -0
- package/dist/media-understanding-provider.js +5 -5
- package/dist/{models-BBMKU4jA.js → models-BFlpn1Ex.js} +12 -17
- package/dist/native-subagent-monitor-DyTGmTUx.js +3111 -0
- package/dist/{plugin-app-cache-key-DztPafOA.js → plugin-app-cache-key-C3H2CVp6.js} +5 -5
- package/dist/plugin-marketplace-discovery-CzrGJdCR.js +385 -0
- package/dist/{computer-use-CYEc8U3w.js → plugin-metadata-cache-CNJPj4_b.js} +105 -5
- package/dist/protocol-validators-B4F7L8Rf.js +2346 -0
- package/dist/{rate-limits-CU-gVCCS.js → rate-limits-DZXmOL8-.js} +17 -35
- package/dist/{request-qKh331yu.js → request-BRl5_UfX.js} +3 -3
- package/dist/{run-attempt-CY9WlDLc.js → run-attempt-P8BdxC1I.js} +4837 -9047
- package/dist/{runtime-artifact-CmtG_t1L.js → runtime-artifact-BVSPdd6g.js} +8 -23
- package/dist/{sandbox-guard-NuhpXmPB.js → sandbox-guard-Cmwc9eIk.js} +6 -13
- package/dist/{session-binding-BXpo9ymA.js → session-binding-DPxIvMgE.js} +246 -265
- package/dist/{session-catalog-DyOgR4WH.js → session-catalog-xwNi_SDM.js} +159 -109
- package/dist/{session-cli-2PZaFLRh.js → session-cli-7hgXZDV7.js} +1 -1
- package/dist/session-retirement-DoMMJFKm.js +36 -0
- package/dist/{session-upstream-marker-B6U7tLw5.js → session-upstream-marker-CrIFX06J.js} +1 -1
- package/dist/{settled-turn-finalizer-CXY2Z9dT.js → settled-turn-finalizer-BRWBO4MO.js} +21 -13
- package/dist/{shared-client-DTbvH-XV.js → shared-client-kIBbGjk4.js} +587 -223
- package/dist/{side-question-_ulBk1AL.js → side-question-9HAL26l9.js} +101 -52
- package/dist/{thread-lifecycle-DF53ogiA.js → thread-lifecycle-Bu1BTDXr.js} +694 -753
- package/dist/thread-ownership-6kOQHlyC.js +92 -0
- package/dist/thread-resume-CPrJNQVM.js +30 -0
- package/dist/transcript-mirror-B9gGgnJS.js +570 -0
- package/dist/{transport-stdio-DXgGZ7Ib.js → transport-stdio-DIImwDVS.js} +11 -0
- package/dist/upstream-prompt-provenance-ClRBije0.js +41 -0
- package/dist/{upstream-session-fork-Dy4Xvuoj.js → upstream-session-fork-CE-_LdnP.js} +5 -5
- package/dist/{usage-CQ6GhD5-.js → usage-CHlV0e-o.js} +3 -3
- package/dist/{web-search-provider.runtime-CCFhg2z5.js → web-search-provider.runtime-DRw2Vl42.js} +8 -9
- package/openclaw.plugin.json +18 -3
- package/package.json +5 -5
- package/dist/conversation-control-xsIeXbo4.js +0 -287
- package/dist/event-projector-usage-Xp4AlR2k.js +0 -86
- package/dist/transcript-mirror-DzsdNCf5.js +0 -6924
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { t as listAllCodexAppServerModels } from "./models-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { i as
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { n as
|
|
1
|
+
import { D as CODEX_PLUGINS_MARKETPLACE_NAME, P as isCodexFastServiceTier, c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, g as resolveCodexAppServerRuntimeOptions, i as createCodexSessionGenerationSupersededError, n as assertCodexBindingMayBeReplaced, r as bindingStoreKey, s as normalizeCodexAppServerBindingModelProvider } from "./session-binding-DPxIvMgE.js";
|
|
2
|
+
import { Q as resolveCodexAppServerAuthAccountCacheKey, V as consumeCodexAppServerLiveThread, a as getLeasedSharedCodexAppServerClient, ct as isJsonObject, et as resolveCodexAppServerAuthProfileIdForAgent, l as releaseLeasedSharedCodexAppServerClient, tt as resolveCodexAppServerFallbackApiKeyCacheKey } from "./shared-client-kIBbGjk4.js";
|
|
3
|
+
import { i as assertCodexThreadResumeResponse } from "./protocol-validators-B4F7L8Rf.js";
|
|
4
|
+
import { t as listAllCodexAppServerModels } from "./models-BFlpn1Ex.js";
|
|
5
|
+
import { i as defaultCodexAppInventoryCache, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-C3H2CVp6.js";
|
|
6
|
+
import { Y as isOpenAiCuratedMarketplaceName, q as refreshCodexPluginRuntimeState } from "./thread-lifecycle-Bu1BTDXr.js";
|
|
7
|
+
import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
|
|
8
|
+
import { t as CODEX_CONTROL_METHODS } from "./capabilities-CybGI1oz.js";
|
|
9
|
+
import { s as summarizeCodexAccountUsage } from "./rate-limits-DZXmOL8-.js";
|
|
10
|
+
import { a as formatComputerUseStatus, c as formatSkills, i as formatCodexStatus, l as formatThreads, n as formatAccount, o as formatList, r as formatCodexDisplayText, s as formatModels, t as buildHelp } from "./command-formatters-B1AEz8ZA.js";
|
|
11
|
+
import { n as resolveCodexNativeExecutionBlock, r as resolveCodexNativeSandboxBlock } from "./sandbox-guard-Cmwc9eIk.js";
|
|
12
|
+
import { a as rollbackCodexAppServerBindingSubscription, i as retireCodexConversationThreadBinding, n as releaseCodexAppServerBindingSubscription, o as withCodexConversationThreadActivity, r as retainCodexAppServerBindingSubscription, s as withExclusiveCodexAppServerThread, t as isSameCodexAppServerThreadOwner } from "./thread-ownership-6kOQHlyC.js";
|
|
13
|
+
import { a as parseCodexPermissionsModeArg, c as setCodexConversationModel, d as stopCodexConversationTurn, i as parseCodexFastModeArg, l as setCodexConversationPermissions, m as canMutateCodexHost, n as parseCodexPluginMarketplaceId, o as readCodexConversationActiveTurn, p as CODEX_NATIVE_EXECUTION_AUTH_ERROR, r as formatPermissionsMode, s as setCodexConversationFastMode, t as discoverCodexMarketplacePlugins, u as steerCodexConversationTurn } from "./plugin-marketplace-discovery-CzrGJdCR.js";
|
|
14
|
+
import { i as readCodexConversationBindingData, n as createCodexCliNodeConversationBindingData, o as resolveCodexDefaultWorkspaceDir, r as createCodexConversationBindingData } from "./conversation-binding-data-CW2GuupV.js";
|
|
15
|
+
import { n as resolveCodexBindingAppServerConnection } from "./binding-connection-I5Bo-AsX.js";
|
|
16
|
+
import { c as readCodexStatusProbes, l as requestOptions, p as formatCodexCliSessions, s as codexControlRequest, u as safeCodexControlRequest } from "./session-catalog-xwNi_SDM.js";
|
|
17
|
+
import { i as readCodexComputerUseStatus, r as installCodexComputerUse, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-CNJPj4_b.js";
|
|
18
|
+
import { normalizeOptionalString, normalizeUniqueStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
14
19
|
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
15
20
|
import crypto from "node:crypto";
|
|
16
21
|
import { ensureAuthProfileStore, findNormalizedProviderValue, resolveAgentDir, resolveAuthProfileEligibility, resolveAuthProfileOrder, resolveProfileUnusableUntilForDisplay, resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
|
|
17
22
|
import { parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
|
18
|
-
import { normalizeOptionalString, normalizeUniqueStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
19
23
|
import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
20
24
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
21
25
|
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
22
|
-
import { MODEL_SELECTION_LOCKED_MESSAGE, isModelSelectionLocked } from "openclaw/plugin-sdk/model-session-runtime";
|
|
26
|
+
import { MODEL_SELECTION_LOCKED_MESSAGE, isModelSelectionLocked, resolvePersistedSessionRuntimeId } from "openclaw/plugin-sdk/model-session-runtime";
|
|
23
27
|
//#region extensions/codex/src/command-account.ts
|
|
24
28
|
const OPENAI_PROVIDER_ID = "openai";
|
|
25
29
|
const OPENAI_CODEX_PROVIDER_ID = OPENAI_PROVIDER_ID;
|
|
@@ -148,9 +152,9 @@ function isActiveProfileCandidate(params, profileId) {
|
|
|
148
152
|
function resolveLiveAccountProfileId(params) {
|
|
149
153
|
if (!params.account.ok || !isJsonObject(params.account.value)) return;
|
|
150
154
|
const account = isJsonObject(params.account.value.account) ? params.account.value.account : params.account.value;
|
|
151
|
-
const type =
|
|
155
|
+
const type = normalizeOptionalString(account.type)?.toLowerCase();
|
|
152
156
|
if (type === "chatgpt") {
|
|
153
|
-
const email =
|
|
157
|
+
const email = normalizeOptionalString(account.email)?.toLowerCase();
|
|
154
158
|
const firstSubscription = params.order.find((profileId) => isChatGptSubscriptionProfile(params.store.profiles[profileId]));
|
|
155
159
|
if (!email) return firstSubscription;
|
|
156
160
|
return params.order.find((profileId) => {
|
|
@@ -231,10 +235,6 @@ function formatSubscriptionUsageLine(usage) {
|
|
|
231
235
|
function formatUsageLineForDisplay(value) {
|
|
232
236
|
return value.replace(/^weekly\b/u, "Weekly").replace(/\bshort-term\b/u, "Short-term");
|
|
233
237
|
}
|
|
234
|
-
function readString(record, key) {
|
|
235
|
-
const value = record[key];
|
|
236
|
-
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
237
|
-
}
|
|
238
238
|
function isChatGptSubscriptionProfile(credential) {
|
|
239
239
|
return credential?.type === "oauth" || credential?.type === "token";
|
|
240
240
|
}
|
|
@@ -1276,7 +1276,7 @@ async function sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, n
|
|
|
1276
1276
|
});
|
|
1277
1277
|
continue;
|
|
1278
1278
|
}
|
|
1279
|
-
const responseThreadId = isJsonObject(response.value) ?
|
|
1279
|
+
const responseThreadId = isJsonObject(response.value) ? normalizeOptionalString(response.value.threadId) : void 0;
|
|
1280
1280
|
sent.push({
|
|
1281
1281
|
...target,
|
|
1282
1282
|
threadId: responseThreadId ?? target.threadId
|
|
@@ -1442,10 +1442,35 @@ async function bindConversation(deps, ctx, pluginConfig, args) {
|
|
|
1442
1442
|
async function detachConversation(deps, ctx) {
|
|
1443
1443
|
if (isCurrentSessionModelSelectionLocked(ctx)) return MODEL_SELECTION_LOCKED_MESSAGE;
|
|
1444
1444
|
const data = readCodexConversationBindingData(await ctx.getCurrentConversationBinding());
|
|
1445
|
-
|
|
1446
|
-
const
|
|
1447
|
-
|
|
1448
|
-
|
|
1445
|
+
const identity = data?.kind === "codex-app-server-session" ? conversationBindingIdentity(data.bindingId) : void 0;
|
|
1446
|
+
const sourceSessionKey = data?.kind === "codex-app-server-session" ? data.source?.sessionKey : void 0;
|
|
1447
|
+
let expectedThreadId;
|
|
1448
|
+
let expectedStartId;
|
|
1449
|
+
if (data?.kind === "codex-app-server-session") {
|
|
1450
|
+
const binding = await deps.bindingStore.read(identity);
|
|
1451
|
+
assertCodexBindingMayBeReplaced(binding, "detaching its conversation binding");
|
|
1452
|
+
if (deps.readCodexConversationActiveTurn(identity)) return "This Codex conversation has an active run; use /codex stop before detaching it.";
|
|
1453
|
+
expectedThreadId = binding?.threadId;
|
|
1454
|
+
expectedStartId = binding?.conversationStartId;
|
|
1455
|
+
}
|
|
1456
|
+
const detachPublicConversation = async () => {
|
|
1457
|
+
return (await ctx.detachConversationBinding()).removed ? "Detached this conversation from Codex." : "No Codex conversation binding was attached.";
|
|
1458
|
+
};
|
|
1459
|
+
if (identity && expectedThreadId) return await withCodexConversationThreadActivity(identity.bindingId, async () => {
|
|
1460
|
+
let detachedPublicConversation;
|
|
1461
|
+
if (!await retireCodexConversationThreadBinding({
|
|
1462
|
+
bindingStore: deps.bindingStore,
|
|
1463
|
+
identity,
|
|
1464
|
+
expectedThreadId,
|
|
1465
|
+
...expectedStartId ? { expectedStartId } : {},
|
|
1466
|
+
...isIncognitoSessionKey(sourceSessionKey) ? { allowUntracked: true } : {},
|
|
1467
|
+
afterClear: async () => {
|
|
1468
|
+
detachedPublicConversation = await detachPublicConversation();
|
|
1469
|
+
}
|
|
1470
|
+
})) return "This Codex conversation binding changed while detaching; try again.";
|
|
1471
|
+
return detachedPublicConversation;
|
|
1472
|
+
});
|
|
1473
|
+
return await detachPublicConversation();
|
|
1449
1474
|
}
|
|
1450
1475
|
async function describeConversationBinding(deps, ctx) {
|
|
1451
1476
|
const current = await ctx.getCurrentConversationBinding();
|
|
@@ -1507,52 +1532,83 @@ async function resumeThread(deps, ctx, pluginConfig, args) {
|
|
|
1507
1532
|
agentId: scope.agentId,
|
|
1508
1533
|
config: ctx.config
|
|
1509
1534
|
});
|
|
1510
|
-
return await
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1535
|
+
return await withExclusiveCodexAppServerThread({
|
|
1536
|
+
bindingStore: deps.bindingStore,
|
|
1537
|
+
identity,
|
|
1538
|
+
threadId: normalizedThreadId,
|
|
1539
|
+
run: async () => await deps.bindingStore.withLease(identity, async () => {
|
|
1540
|
+
if (!await reclaimCurrentCodexSessionGeneration({
|
|
1541
|
+
bindingStore: deps.bindingStore,
|
|
1542
|
+
identity,
|
|
1543
|
+
config: ctx.config
|
|
1544
|
+
})) throw createCodexSessionGenerationSupersededError(identity.sessionId);
|
|
1545
|
+
const currentBinding = await deps.bindingStore.read(identity);
|
|
1546
|
+
assertCodexBindingMayBeReplaced(currentBinding, "attaching a different resumed thread");
|
|
1547
|
+
const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
|
|
1548
|
+
authProfileId: currentBinding?.authProfileId,
|
|
1549
|
+
agentDir: scope.agentDir,
|
|
1550
|
+
config: ctx.config
|
|
1551
|
+
});
|
|
1552
|
+
let committedResponse = false;
|
|
1553
|
+
const commitResumedThread = async (value, client) => {
|
|
1554
|
+
const response = assertCodexThreadResumeResponse(value);
|
|
1555
|
+
const effectiveThreadId = response.thread.id;
|
|
1556
|
+
if (effectiveThreadId !== normalizedThreadId) throw new Error(`Codex thread/resume returned ${effectiveThreadId} for ${normalizedThreadId}`);
|
|
1557
|
+
const resumedCwd = response.thread.cwd;
|
|
1558
|
+
if (typeof resumedCwd !== "string") throw new Error(`Codex thread/resume returned no cwd for ${normalizedThreadId}`);
|
|
1559
|
+
const modelProvider = normalizeCodexAppServerBindingModelProvider({
|
|
1560
|
+
authProfileId,
|
|
1561
|
+
modelProvider: response.modelProvider ?? void 0,
|
|
1562
|
+
agentDir: scope.agentDir,
|
|
1563
|
+
config: ctx.config
|
|
1564
|
+
});
|
|
1565
|
+
const bindingBeforeCommit = await deps.bindingStore.read(identity);
|
|
1566
|
+
assertCodexBindingMayBeReplaced(bindingBeforeCommit, "committing a different resumed thread");
|
|
1567
|
+
const clientId = client?.getInstanceId();
|
|
1568
|
+
const sameOwner = client ? isSameCodexAppServerThreadOwner(bindingBeforeCommit, {
|
|
1569
|
+
threadId: effectiveThreadId,
|
|
1570
|
+
clientId
|
|
1571
|
+
}) : bindingBeforeCommit?.threadId === effectiveThreadId;
|
|
1572
|
+
let retained = false;
|
|
1573
|
+
try {
|
|
1574
|
+
if (client) {
|
|
1575
|
+
retained = await retainCodexAppServerBindingSubscription(client, effectiveThreadId, sameOwner ? await consumeCodexAppServerLiveThread(client, effectiveThreadId) : void 0);
|
|
1576
|
+
if (!retained) throw new Error("Codex resumed thread lost its native subscription owner.");
|
|
1577
|
+
}
|
|
1578
|
+
if (bindingBeforeCommit && !sameOwner) await releaseCodexAppServerBindingSubscription(bindingBeforeCommit);
|
|
1579
|
+
if (!await deps.bindingStore.mutate(identity, {
|
|
1580
|
+
kind: "set",
|
|
1581
|
+
binding: {
|
|
1582
|
+
...bindingBeforeCommit?.threadId === effectiveThreadId ? bindingBeforeCommit : {},
|
|
1583
|
+
threadId: effectiveThreadId,
|
|
1584
|
+
...clientId ? { clientId } : {},
|
|
1585
|
+
cwd: resumedCwd,
|
|
1586
|
+
authProfileId,
|
|
1587
|
+
model: response.model,
|
|
1588
|
+
modelProvider,
|
|
1589
|
+
historyCoveredThrough: (/* @__PURE__ */ new Date()).toISOString()
|
|
1590
|
+
}
|
|
1591
|
+
})) throw new Error("Codex thread binding changed while attaching the resumed thread.");
|
|
1592
|
+
} catch (error) {
|
|
1593
|
+
if (client && !sameOwner) await rollbackCodexAppServerBindingSubscription(client, effectiveThreadId, retained);
|
|
1594
|
+
throw error;
|
|
1595
|
+
}
|
|
1596
|
+
committedResponse = true;
|
|
1597
|
+
};
|
|
1598
|
+
const response = await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.resumeThread, {
|
|
1599
|
+
threadId: normalizedThreadId,
|
|
1600
|
+
excludeTurns: true
|
|
1601
|
+
}, {
|
|
1602
|
+
config: ctx.config,
|
|
1603
|
+
agentDir: scope.agentDir,
|
|
1549
1604
|
authProfileId,
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
|
|
1605
|
+
sessionKey: ctx.sessionKey,
|
|
1606
|
+
sessionId: ctx.sessionId,
|
|
1607
|
+
onResponse: commitResumedThread
|
|
1608
|
+
});
|
|
1609
|
+
if (!committedResponse) await commitResumedThread(response);
|
|
1610
|
+
return `Attached this OpenClaw session to Codex thread ${formatCodexDisplayText(normalizedThreadId)}.`;
|
|
1611
|
+
})
|
|
1556
1612
|
});
|
|
1557
1613
|
}
|
|
1558
1614
|
async function bindCodexCliNodeSession(deps, ctx, parsed) {
|
|
@@ -1717,8 +1773,8 @@ async function handleNativeGoal(deps, ctx, pluginConfig, args) {
|
|
|
1717
1773
|
function formatNativeGoal(response) {
|
|
1718
1774
|
const goal = isJsonObject(response) && isJsonObject(response.goal) ? response.goal : void 0;
|
|
1719
1775
|
if (!goal) return "No Codex goal is active.";
|
|
1720
|
-
const objective =
|
|
1721
|
-
const status =
|
|
1776
|
+
const objective = normalizeOptionalString(goal.objective) ?? "unknown";
|
|
1777
|
+
const status = normalizeOptionalString(goal.status) ?? "unknown";
|
|
1722
1778
|
const tokensUsed = typeof goal.tokensUsed === "number" ? goal.tokensUsed : 0;
|
|
1723
1779
|
const tokenBudget = typeof goal.tokenBudget === "number" ? goal.tokenBudget : void 0;
|
|
1724
1780
|
return [
|
|
@@ -1758,8 +1814,16 @@ async function setConversationModel(deps, ctx, pluginConfig, args) {
|
|
|
1758
1814
|
const target = await resolveControlTarget(ctx);
|
|
1759
1815
|
if (!target) return "Cannot set Codex model because this command did not include a stable binding identity.";
|
|
1760
1816
|
if (!normalized) {
|
|
1817
|
+
const currentSession = ctx.sessionId && ctx.sessionKey ? getSessionEntry({
|
|
1818
|
+
storePath: resolveStorePath(ctx.config.session?.store, { agentId: target.agentId }),
|
|
1819
|
+
sessionKey: ctx.sessionKey,
|
|
1820
|
+
hydrateSkillPromptRefs: false,
|
|
1821
|
+
readConsistency: "latest"
|
|
1822
|
+
}) : void 0;
|
|
1823
|
+
const selectedModel = currentSession && currentSession.sessionId === ctx.sessionId ? currentSession.modelOverride : void 0;
|
|
1761
1824
|
const binding = await deps.bindingStore.read(target.identity);
|
|
1762
|
-
|
|
1825
|
+
const activeModel = selectedModel ?? binding?.model;
|
|
1826
|
+
return activeModel ? `Codex model: ${formatCodexDisplayText(activeModel)}` : "Usage: /codex model <model>";
|
|
1763
1827
|
}
|
|
1764
1828
|
return await deps.setCodexConversationModel({
|
|
1765
1829
|
identity: target.identity,
|
|
@@ -1767,7 +1831,12 @@ async function setConversationModel(deps, ctx, pluginConfig, args) {
|
|
|
1767
1831
|
pluginConfig,
|
|
1768
1832
|
model: normalized,
|
|
1769
1833
|
agentDir: target.agentDir,
|
|
1770
|
-
config: ctx.config
|
|
1834
|
+
config: ctx.config,
|
|
1835
|
+
...ctx.sessionId && ctx.sessionKey ? { session: {
|
|
1836
|
+
agentId: target.agentId,
|
|
1837
|
+
sessionId: ctx.sessionId,
|
|
1838
|
+
sessionKey: ctx.sessionKey
|
|
1839
|
+
} } : {}
|
|
1771
1840
|
});
|
|
1772
1841
|
}
|
|
1773
1842
|
async function setConversationFastMode(deps, ctx, args) {
|
|
@@ -1797,32 +1866,54 @@ async function setConversationPermissions(deps, ctx, args) {
|
|
|
1797
1866
|
});
|
|
1798
1867
|
}
|
|
1799
1868
|
async function startThreadAction(deps, ctx, pluginConfig, kind, args) {
|
|
1800
|
-
|
|
1801
|
-
if (args.length > 0) return `Usage: /codex ${label === "compaction" ? "compact" : label}`;
|
|
1869
|
+
if (args.length > 0) return `Usage: /codex ${kind}`;
|
|
1802
1870
|
const target = await resolveControlTarget(ctx);
|
|
1803
|
-
if (!target) return `Cannot start Codex ${
|
|
1871
|
+
if (!target) return `Cannot start Codex ${kind === "compact" ? "compaction" : "review"} because this command did not include a stable binding identity.`;
|
|
1804
1872
|
const binding = await deps.bindingStore.read(target.identity);
|
|
1805
1873
|
if (!binding?.threadId) return `No Codex thread is attached to this OpenClaw session yet.`;
|
|
1874
|
+
if (kind === "compact") {
|
|
1875
|
+
const sessionTarget = ctx.sessionTarget;
|
|
1876
|
+
if (!ctx.sessionId || !ctx.sessionKey || !sessionTarget || sessionTarget.sessionId !== ctx.sessionId || sessionTarget.sessionKey !== ctx.sessionKey) return "Codex compaction is unavailable because this command is not bound to a complete session identity.";
|
|
1877
|
+
const currentSession = getSessionEntry({
|
|
1878
|
+
storePath: sessionTarget.storePath,
|
|
1879
|
+
sessionKey: ctx.sessionKey,
|
|
1880
|
+
hydrateSkillPromptRefs: false,
|
|
1881
|
+
readConsistency: "latest"
|
|
1882
|
+
});
|
|
1883
|
+
if (currentSession?.sessionId !== ctx.sessionId || resolvePersistedSessionRuntimeId(currentSession) !== "codex") return "Codex compaction is unavailable because the current OpenClaw session is not using the Codex runtime.";
|
|
1884
|
+
if (target.identity.kind === "conversation") {
|
|
1885
|
+
if (!isSameCodexAppServerThreadOwner(binding, ctx.sessionId ? await deps.bindingStore.read(sessionBindingIdentity({
|
|
1886
|
+
sessionId: ctx.sessionId,
|
|
1887
|
+
sessionKey: ctx.sessionKey,
|
|
1888
|
+
agentId: sessionTarget.agentId,
|
|
1889
|
+
config: ctx.config
|
|
1890
|
+
})) : void 0)) return "Codex compaction is unavailable because the conversation-bound thread differs from the current session binding. Resume that thread into the session first.";
|
|
1891
|
+
}
|
|
1892
|
+
const compactCurrent = ctx.runtimeContext?.compactCurrent;
|
|
1893
|
+
if (!compactCurrent) return "Codex compaction is unavailable because this command is not bound to a session.";
|
|
1894
|
+
const result = await compactCurrent();
|
|
1895
|
+
return result.compacted ? `Compacted Codex session (${result.tokensAfter ?? "unknown"} tokens after).` : `Codex compaction did not complete: ${formatCodexDisplayText(result.reason ?? "no reason returned")}.`;
|
|
1896
|
+
}
|
|
1806
1897
|
const connection = resolveCodexBindingAppServerConnection({
|
|
1807
1898
|
binding,
|
|
1808
1899
|
authProfileId: binding.authProfileId,
|
|
1809
1900
|
pluginConfig
|
|
1810
1901
|
});
|
|
1811
|
-
await deps.codexControlRequest(pluginConfig,
|
|
1902
|
+
await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.review, {
|
|
1812
1903
|
threadId: binding.threadId,
|
|
1813
1904
|
target: { type: "uncommittedChanges" }
|
|
1814
|
-
}
|
|
1905
|
+
}, {
|
|
1815
1906
|
agentDir: target.agentDir,
|
|
1816
1907
|
authProfileId: connection.clientAuthProfileId,
|
|
1817
1908
|
config: ctx.config,
|
|
1818
1909
|
...connection.usesSupervisionConnection ? { startOptions: connection.appServer.start } : {}
|
|
1819
1910
|
});
|
|
1820
|
-
return `Started Codex
|
|
1911
|
+
return `Started Codex review for thread ${formatCodexDisplayText(binding.threadId)}.`;
|
|
1821
1912
|
}
|
|
1822
1913
|
//#endregion
|
|
1823
1914
|
//#region extensions/codex/src/command-plugins-management.ts
|
|
1824
1915
|
const POLICY_REFRESH_HINT = "New Codex conversations pick this up automatically. Use /new or /reset to refresh the current one.";
|
|
1825
|
-
async function handleCodexPluginsSubcommand(ctx, rest, io) {
|
|
1916
|
+
async function handleCodexPluginsSubcommand(ctx, rest, io, runtime) {
|
|
1826
1917
|
const [verb = "list", ...args] = rest;
|
|
1827
1918
|
const normalized = verb.toLowerCase();
|
|
1828
1919
|
if (normalized === "menu") {
|
|
@@ -1838,22 +1929,51 @@ async function handleCodexPluginsSubcommand(ctx, rest, io) {
|
|
|
1838
1929
|
const current = await io.readConfig();
|
|
1839
1930
|
return { text: formatPluginList(current.plugins ?? {}, { globalEnabled: current.enabled === true }) };
|
|
1840
1931
|
}
|
|
1932
|
+
if (normalized === "available") {
|
|
1933
|
+
if (args.length > 0) return { text: "Usage: /codex plugins available" };
|
|
1934
|
+
if (!canMutateCodexHost(ctx)) return { text: "Only an owner or operator.admin gateway client can list available Codex plugins." };
|
|
1935
|
+
if (!runtime) return { text: "Codex plugin discovery is unavailable for this command." };
|
|
1936
|
+
try {
|
|
1937
|
+
const discovered = await discoverCodexMarketplacePlugins({
|
|
1938
|
+
request: runtime.list,
|
|
1939
|
+
workspaceDir: await runtime.workspaceDir()
|
|
1940
|
+
});
|
|
1941
|
+
return { text: formatAvailablePlugins(discovered.plugins, discovered.warnings) };
|
|
1942
|
+
} catch (error) {
|
|
1943
|
+
return { text: `Could not list Codex plugins: ${formatCodexDisplayText(errorMessage(error))}` };
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
if (normalized === "install") {
|
|
1947
|
+
if (args.length !== 1 || !args[0]) return { text: "Usage: /codex plugins install <plugin>@<marketplace>" };
|
|
1948
|
+
if (!canMutateCodexHost(ctx)) return { text: "Only an owner or operator.admin gateway client can run /codex plugins install." };
|
|
1949
|
+
if (!runtime) return { text: "Codex plugin installation is unavailable for this command." };
|
|
1950
|
+
return await installCodexPlugin(args[0], io, runtime);
|
|
1951
|
+
}
|
|
1841
1952
|
const target = args[0];
|
|
1842
1953
|
if (normalized === "enable" || normalized === "disable") {
|
|
1843
1954
|
if (args.length === 0) return buildPluginNamePickerReply(normalized, await io.readConfig());
|
|
1844
1955
|
if (!target || args.length > 1) return { text: `Usage: /codex plugins ${normalized} <name>` };
|
|
1845
1956
|
if (!canMutateCodexHost(ctx)) return { text: `Only an owner or operator.admin gateway client can run /codex plugins ${normalized}.` };
|
|
1846
1957
|
const wantEnabled = normalized === "enable";
|
|
1847
|
-
|
|
1958
|
+
const current = (await io.readConfig()).plugins ?? {};
|
|
1959
|
+
const exact = current[target];
|
|
1960
|
+
const requested = parseCodexPluginMarketplaceId(target);
|
|
1961
|
+
const configured = exact && requested && !matchesConfiguredPluginIdentity(exact, requested, target) ? {
|
|
1962
|
+
status: "matched",
|
|
1963
|
+
configKey: target
|
|
1964
|
+
} : resolveConfiguredPluginKey(current, target);
|
|
1965
|
+
if (configured.status === "ambiguous" || configured.status === "mismatched") return { text: describeConfiguredPluginIdentityConflict(target, configured.status) };
|
|
1966
|
+
if (configured.status === "missing") return { text: `Codex sub-plugin '${formatCodexDisplayText(target)}' is not configured. Run '/codex plugins list' to see configured plugins.` };
|
|
1967
|
+
const configKey = configured.configKey;
|
|
1848
1968
|
await io.mutate((block) => {
|
|
1849
1969
|
if (wantEnabled) block.enabled = true;
|
|
1850
1970
|
block.plugins ??= {};
|
|
1851
|
-
block.plugins[
|
|
1852
|
-
...block.plugins[
|
|
1971
|
+
block.plugins[configKey] = {
|
|
1972
|
+
...block.plugins[configKey],
|
|
1853
1973
|
enabled: wantEnabled
|
|
1854
1974
|
};
|
|
1855
1975
|
});
|
|
1856
|
-
return { text: `${formatCodexDisplayText(
|
|
1976
|
+
return { text: `${formatCodexDisplayText(configKey)}: ${wantEnabled ? "enabled" : "disabled"} in openclaw.json. ${POLICY_REFRESH_HINT}` };
|
|
1857
1977
|
}
|
|
1858
1978
|
return { text: `Unknown /codex plugins subcommand: ${formatCodexDisplayText(verb)}\n\n${buildPluginsHelp()}` };
|
|
1859
1979
|
}
|
|
@@ -1863,9 +1983,10 @@ function buildPluginsMenuReply() {
|
|
|
1863
1983
|
"Codex sub-plugins. Pick a sub-action or type:",
|
|
1864
1984
|
"",
|
|
1865
1985
|
" 1. /codex plugins list",
|
|
1866
|
-
" 2. /codex plugins
|
|
1867
|
-
" 3. /codex plugins
|
|
1868
|
-
" 4. /codex plugins
|
|
1986
|
+
" 2. /codex plugins available",
|
|
1987
|
+
" 3. /codex plugins enable",
|
|
1988
|
+
" 4. /codex plugins disable",
|
|
1989
|
+
" 5. /codex plugins help",
|
|
1869
1990
|
"",
|
|
1870
1991
|
"Type '/codex' to go back to the main menu."
|
|
1871
1992
|
].join("\n"),
|
|
@@ -1874,6 +1995,10 @@ function buildPluginsMenuReply() {
|
|
|
1874
1995
|
label: "list",
|
|
1875
1996
|
command: "/codex plugins list"
|
|
1876
1997
|
},
|
|
1998
|
+
{
|
|
1999
|
+
label: "available",
|
|
2000
|
+
command: "/codex plugins available"
|
|
2001
|
+
},
|
|
1877
2002
|
{
|
|
1878
2003
|
label: "enable",
|
|
1879
2004
|
command: "/codex plugins enable"
|
|
@@ -1935,13 +2060,195 @@ function buildPluginNamePickerReply(verb, current) {
|
|
|
1935
2060
|
}
|
|
1936
2061
|
function buildPluginsHelp() {
|
|
1937
2062
|
return [
|
|
1938
|
-
"Codex
|
|
1939
|
-
"- /codex plugins
|
|
1940
|
-
"- /codex plugins list
|
|
1941
|
-
"- /codex plugins
|
|
1942
|
-
"- /codex plugins
|
|
2063
|
+
"Codex plugin discovery and owner-approved installation:",
|
|
2064
|
+
"- /codex plugins (alias for list)",
|
|
2065
|
+
"- /codex plugins list show explicitly configured plugins",
|
|
2066
|
+
"- /codex plugins available list discoverable Codex marketplaces",
|
|
2067
|
+
"- /codex plugins install <name>@<marketplace> install and authorize one plugin",
|
|
2068
|
+
"- /codex plugins enable <name> enable a configured plugin",
|
|
2069
|
+
"- /codex plugins disable <name> disable a configured plugin",
|
|
2070
|
+
"Only an owner or operator.admin can discover, install, enable, or disable plugins."
|
|
1943
2071
|
].join("\n");
|
|
1944
2072
|
}
|
|
2073
|
+
async function installCodexPlugin(requestedId, io, runtime) {
|
|
2074
|
+
const requested = parseCodexPluginMarketplaceId(requestedId);
|
|
2075
|
+
if (!requested) return { text: "Invalid plugin identifier. Use /codex plugins install <plugin>@<marketplace> with ASCII letters, digits, underscores, or hyphens." };
|
|
2076
|
+
let plugin;
|
|
2077
|
+
let workspaceDir;
|
|
2078
|
+
try {
|
|
2079
|
+
workspaceDir = await runtime.workspaceDir();
|
|
2080
|
+
const matching = (await discoverCodexMarketplacePlugins({
|
|
2081
|
+
request: runtime.list,
|
|
2082
|
+
workspaceDir
|
|
2083
|
+
})).plugins.filter((candidate) => candidate.pluginName === requested.pluginName && marketplaceNamesRepresentSameCatalog(candidate.marketplaceName, requested.marketplaceName));
|
|
2084
|
+
if (matching.length > 1) {
|
|
2085
|
+
plugin = resolveCuratedMarketplaceAliases(matching, requested.marketplaceName);
|
|
2086
|
+
if (!plugin) return { text: `Multiple available Codex plugins match '${formatCodexDisplayText(requestedId)}'; the marketplace identity must be unique.` };
|
|
2087
|
+
} else plugin = matching[0];
|
|
2088
|
+
} catch (error) {
|
|
2089
|
+
return { text: `Could not verify the requested Codex plugin: ${formatCodexDisplayText(errorMessage(error))}` };
|
|
2090
|
+
}
|
|
2091
|
+
if (!plugin) return { text: `${formatCodexDisplayText(requestedId)} was not found. Run /codex plugins available to inspect the current marketplaces.` };
|
|
2092
|
+
if (!plugin.available) return { text: `${formatCodexDisplayText(requestedId)} is unavailable or disabled by its marketplace administrator.` };
|
|
2093
|
+
const alreadyInstalled = plugin.installed && plugin.enabled;
|
|
2094
|
+
if (!alreadyInstalled && !plugin.marketplacePath && plugin.remotePluginId) {
|
|
2095
|
+
if (plugin.mustShowInstallationInterstitial === true) return { text: `${formatCodexDisplayText(requestedId)} requires a Codex installation confirmation that OpenClaw cannot display. Install it in Codex first, then rerun this command to authorize it here.` };
|
|
2096
|
+
if (plugin.mustShowInstallationInterstitial !== false) return { text: `${formatCodexDisplayText(requestedId)} cannot be installed because Codex did not provide its required installation-confirmation policy. Install it in Codex first, then rerun this command to authorize it here.` };
|
|
2097
|
+
}
|
|
2098
|
+
try {
|
|
2099
|
+
const configured = resolveInstalledPluginKey((await io.readConfig()).plugins ?? {}, plugin);
|
|
2100
|
+
if (configured.status === "ambiguous" || configured.status === "mismatched") return { text: describeConfiguredPluginIdentityConflict(requestedId, configured.status) };
|
|
2101
|
+
} catch (error) {
|
|
2102
|
+
return { text: `Could not verify existing Codex plugin authorization: ${formatCodexDisplayText(errorMessage(error))}` };
|
|
2103
|
+
}
|
|
2104
|
+
let result;
|
|
2105
|
+
if (!alreadyInstalled) {
|
|
2106
|
+
const requestParams = plugin.marketplacePath ? {
|
|
2107
|
+
marketplacePath: plugin.marketplacePath,
|
|
2108
|
+
pluginName: plugin.pluginName
|
|
2109
|
+
} : plugin.remotePluginId ? {
|
|
2110
|
+
remoteMarketplaceName: plugin.marketplaceName,
|
|
2111
|
+
pluginName: plugin.remotePluginId
|
|
2112
|
+
} : void 0;
|
|
2113
|
+
if (!requestParams) return { text: `${formatCodexDisplayText(requestedId)} cannot be installed because its marketplace did not provide a trusted local path or remote plugin identifier.` };
|
|
2114
|
+
try {
|
|
2115
|
+
result = await runtime.install(requestParams);
|
|
2116
|
+
} catch (error) {
|
|
2117
|
+
return { text: `Could not install ${formatCodexDisplayText(requestedId)}: ${formatCodexDisplayText(errorMessage(error))}` };
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
const selectedPlugin = plugin;
|
|
2121
|
+
try {
|
|
2122
|
+
await io.mutate((block) => {
|
|
2123
|
+
block.plugins ??= {};
|
|
2124
|
+
const configured = resolveInstalledPluginKey(block.plugins, selectedPlugin);
|
|
2125
|
+
if (configured.status === "ambiguous" || configured.status === "mismatched") throw new Error(describeConfiguredPluginIdentityConflict(selectedPlugin.id, configured.status));
|
|
2126
|
+
const curated = isOpenAiCuratedMarketplaceName(selectedPlugin.marketplaceName);
|
|
2127
|
+
const canonicalId = curated ? `${selectedPlugin.pluginName}@${CODEX_PLUGINS_MARKETPLACE_NAME}` : selectedPlugin.id;
|
|
2128
|
+
const configKey = configured.status === "matched" ? configured.configKey : canonicalId;
|
|
2129
|
+
const existing = block.plugins[configKey];
|
|
2130
|
+
block.enabled = true;
|
|
2131
|
+
const updated = {
|
|
2132
|
+
...existing,
|
|
2133
|
+
enabled: true,
|
|
2134
|
+
marketplaceName: existing?.marketplaceName ?? (curated ? "openai-curated" : selectedPlugin.marketplaceName),
|
|
2135
|
+
pluginName: existing?.pluginName ?? (curated ? selectedPlugin.pluginName : persistedPluginName(selectedPlugin))
|
|
2136
|
+
};
|
|
2137
|
+
block.plugins[configKey] = updated;
|
|
2138
|
+
});
|
|
2139
|
+
} catch (error) {
|
|
2140
|
+
return { text: `${formatCodexDisplayText(requestedId)} was installed in Codex but could not be authorized in OpenClaw and will not be exposed: ${formatCodexDisplayText(errorMessage(error))}` };
|
|
2141
|
+
}
|
|
2142
|
+
let refreshWarning = "";
|
|
2143
|
+
if (runtime.refresh) try {
|
|
2144
|
+
refreshWarning = (await runtime.refresh(workspaceDir)).diagnostics.map((diagnostic) => ` ${formatCodexDisplayText(diagnostic.message)}`).join("");
|
|
2145
|
+
} catch (error) {
|
|
2146
|
+
refreshWarning = ` Runtime refresh requires a new conversation: ${formatCodexDisplayText(errorMessage(error))}`;
|
|
2147
|
+
}
|
|
2148
|
+
const appsNeedingAuth = result?.appsNeedingAuth ?? [];
|
|
2149
|
+
if (appsNeedingAuth.length > 0) {
|
|
2150
|
+
const apps = appsNeedingAuth.map((app) => formatCodexDisplayText(app.name)).slice(0, 5).join(", ");
|
|
2151
|
+
return { text: `${formatCodexDisplayText(requestedId)} was installed and authorized, but ${apps} still require connector authentication. Complete sign-in before using those apps.${refreshWarning} ${POLICY_REFRESH_HINT}` };
|
|
2152
|
+
}
|
|
2153
|
+
const status = alreadyInstalled ? "was already installed in Codex and is now authorized" : "was installed and authorized";
|
|
2154
|
+
return { text: `${formatCodexDisplayText(requestedId)} ${status}.${refreshWarning} ${POLICY_REFRESH_HINT}` };
|
|
2155
|
+
}
|
|
2156
|
+
/** Merge historical curated wire aliases only when they identify the same install source. */
|
|
2157
|
+
function resolveCuratedMarketplaceAliases(plugins, requestedMarketplaceName) {
|
|
2158
|
+
if (!isOpenAiCuratedMarketplaceName(requestedMarketplaceName)) return;
|
|
2159
|
+
const sourceIdentities = new Set(plugins.map((plugin) => plugin.marketplacePath ? `local:${plugin.marketplacePath}` : plugin.remotePluginId ? `remote:${plugin.remotePluginId}` : void 0));
|
|
2160
|
+
if (sourceIdentities.size !== 1 || sourceIdentities.has(void 0)) return;
|
|
2161
|
+
const selected = plugins.find((plugin) => plugin.marketplaceName === requestedMarketplaceName) ?? plugins[0];
|
|
2162
|
+
if (!selected) return;
|
|
2163
|
+
return {
|
|
2164
|
+
...selected,
|
|
2165
|
+
installed: plugins.some((plugin) => plugin.installed),
|
|
2166
|
+
enabled: plugins.some((plugin) => plugin.installed && plugin.enabled),
|
|
2167
|
+
available: plugins.every((plugin) => plugin.available),
|
|
2168
|
+
...selected.remotePluginId ? { mustShowInstallationInterstitial: plugins.some((plugin) => plugin.mustShowInstallationInterstitial === true) ? true : plugins.every((plugin) => plugin.mustShowInstallationInterstitial === false) ? false : null } : {},
|
|
2169
|
+
...plugins.some((plugin) => plugin.installPolicy === "NOT_AVAILABLE") ? { installPolicy: "NOT_AVAILABLE" } : {}
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
2172
|
+
function persistedPluginName(plugin) {
|
|
2173
|
+
return !plugin.marketplacePath && plugin.summaryId.endsWith(`@${plugin.marketplaceName}`) ? plugin.summaryId : plugin.pluginName;
|
|
2174
|
+
}
|
|
2175
|
+
function resolveConfiguredPluginKey(plugins, target) {
|
|
2176
|
+
const requested = parseCodexPluginMarketplaceId(target);
|
|
2177
|
+
const direct = plugins[target];
|
|
2178
|
+
if (!requested) {
|
|
2179
|
+
if (!direct) return { status: "missing" };
|
|
2180
|
+
const qualifiedName = direct.pluginName ? parseCodexPluginMarketplaceId(direct.pluginName) : void 0;
|
|
2181
|
+
if (qualifiedName && direct.marketplaceName && !marketplaceNamesRepresentSameCatalog(qualifiedName.marketplaceName, direct.marketplaceName)) return { status: "mismatched" };
|
|
2182
|
+
const identity = resolveConfiguredPluginIdentity(direct);
|
|
2183
|
+
if (!identity) return {
|
|
2184
|
+
status: "matched",
|
|
2185
|
+
configKey: target
|
|
2186
|
+
};
|
|
2187
|
+
const marketplaceName = isOpenAiCuratedMarketplaceName(identity.marketplaceName) ? CODEX_PLUGINS_MARKETPLACE_NAME : identity.marketplaceName;
|
|
2188
|
+
const canonicalId = `${identity.pluginName}@${marketplaceName}`;
|
|
2189
|
+
const canonical = plugins[canonicalId];
|
|
2190
|
+
if (canonical && !matchesConfiguredPluginIdentity(canonical, identity, canonicalId)) return { status: "mismatched" };
|
|
2191
|
+
return Object.values(plugins).filter((entry) => matchesConfiguredPluginIdentity(entry, identity, canonicalId)).length > 1 ? { status: "ambiguous" } : {
|
|
2192
|
+
status: "matched",
|
|
2193
|
+
configKey: target
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
if (direct && !matchesConfiguredPluginIdentity(direct, requested, target)) return { status: "mismatched" };
|
|
2197
|
+
const matching = Object.entries(plugins).filter(([, entry]) => matchesConfiguredPluginIdentity(entry, requested, target));
|
|
2198
|
+
if (matching.length > 1) return { status: "ambiguous" };
|
|
2199
|
+
const configKey = matching[0]?.[0];
|
|
2200
|
+
return configKey ? {
|
|
2201
|
+
status: "matched",
|
|
2202
|
+
configKey
|
|
2203
|
+
} : { status: "missing" };
|
|
2204
|
+
}
|
|
2205
|
+
function resolveInstalledPluginKey(plugins, plugin) {
|
|
2206
|
+
const discovered = resolveConfiguredPluginKey(plugins, plugin.id);
|
|
2207
|
+
if (discovered.status === "ambiguous" || discovered.status === "mismatched") return discovered;
|
|
2208
|
+
if (!isOpenAiCuratedMarketplaceName(plugin.marketplaceName)) return discovered;
|
|
2209
|
+
const canonical = resolveConfiguredPluginKey(plugins, `${plugin.pluginName}@${CODEX_PLUGINS_MARKETPLACE_NAME}`);
|
|
2210
|
+
if (canonical.status === "ambiguous" || canonical.status === "mismatched") return canonical;
|
|
2211
|
+
if (discovered.status === "matched" && canonical.status === "matched" && discovered.configKey !== canonical.configKey) return { status: "ambiguous" };
|
|
2212
|
+
return canonical.status === "matched" ? canonical : discovered;
|
|
2213
|
+
}
|
|
2214
|
+
function resolveConfiguredPluginIdentity(entry) {
|
|
2215
|
+
if (!entry.pluginName || !entry.marketplaceName) return;
|
|
2216
|
+
const qualified = parseCodexPluginMarketplaceId(entry.pluginName);
|
|
2217
|
+
if (qualified) return marketplaceNamesRepresentSameCatalog(qualified.marketplaceName, entry.marketplaceName) ? {
|
|
2218
|
+
pluginName: qualified.pluginName,
|
|
2219
|
+
marketplaceName: entry.marketplaceName
|
|
2220
|
+
} : void 0;
|
|
2221
|
+
return parseCodexPluginMarketplaceId(`${entry.pluginName}@${entry.marketplaceName}`);
|
|
2222
|
+
}
|
|
2223
|
+
function matchesConfiguredPluginIdentity(entry, requested, target) {
|
|
2224
|
+
const configuredName = entry.pluginName ? parseCodexPluginMarketplaceId(entry.pluginName) : void 0;
|
|
2225
|
+
return typeof entry.marketplaceName === "string" && marketplaceNamesRepresentSameCatalog(entry.marketplaceName, requested.marketplaceName) && (entry.pluginName === requested.pluginName || entry.pluginName === target || configuredName?.pluginName === requested.pluginName && marketplaceNamesRepresentSameCatalog(configuredName.marketplaceName, requested.marketplaceName));
|
|
2226
|
+
}
|
|
2227
|
+
function marketplaceNamesRepresentSameCatalog(left, right) {
|
|
2228
|
+
return left === right || isOpenAiCuratedMarketplaceName(left) && isOpenAiCuratedMarketplaceName(right);
|
|
2229
|
+
}
|
|
2230
|
+
function describeConfiguredPluginIdentityConflict(target, status) {
|
|
2231
|
+
const identity = formatCodexDisplayText(target);
|
|
2232
|
+
return status === "ambiguous" ? `Multiple configured Codex plugins match '${identity}'; resolve duplicate plugin policies first.` : `Configured Codex plugin key '${identity}' points to a different plugin identity; resolve the configuration conflict first.`;
|
|
2233
|
+
}
|
|
2234
|
+
function formatAvailablePlugins(plugins, warnings) {
|
|
2235
|
+
if (plugins.length === 0) return ["No Codex plugins were discovered for the current workspace.", ...warnings.map((warning) => `Warning: ${formatCodexDisplayText(warning)}`)].join("\n");
|
|
2236
|
+
return [
|
|
2237
|
+
"Discoverable Codex plugins:",
|
|
2238
|
+
...plugins.slice(0, 30).map((plugin) => {
|
|
2239
|
+
const state = plugin.installed ? plugin.enabled ? "installed" : "installed, disabled" : plugin.available ? "available" : "unavailable";
|
|
2240
|
+
const description = plugin.description ? ` - ${formatCodexDisplayText(plugin.description)}` : "";
|
|
2241
|
+
return `- ${plugin.id} (${state})${description}`;
|
|
2242
|
+
}),
|
|
2243
|
+
...plugins.length > 30 ? ["- Additional plugins omitted."] : [],
|
|
2244
|
+
...warnings.map((warning) => `Warning: ${formatCodexDisplayText(warning)}`),
|
|
2245
|
+
"To authorize one plugin, an owner or operator.admin must send:",
|
|
2246
|
+
"/codex plugins install <plugin>@<marketplace>"
|
|
2247
|
+
].join("\n");
|
|
2248
|
+
}
|
|
2249
|
+
function errorMessage(error) {
|
|
2250
|
+
return error instanceof Error ? error.message : String(error);
|
|
2251
|
+
}
|
|
1945
2252
|
function formatPluginList(plugins, options = {}) {
|
|
1946
2253
|
const globalEnabled = options.globalEnabled === true;
|
|
1947
2254
|
const keys = Object.keys(plugins).toSorted();
|
|
@@ -1981,7 +2288,76 @@ async function handleCodexSubcommand(ctx, options) {
|
|
|
1981
2288
|
if (sandboxBlock) return { text: sandboxBlock };
|
|
1982
2289
|
if (normalized === "plugins") {
|
|
1983
2290
|
if (!deps.codexPluginsManagementIo) return { text: "Codex sub-plugin management is not wired up (codexPluginsManagementIo dep is undefined). Edit ~/.openclaw/openclaw.json or use `openclaw config patch` until the runtime exposes the IO." };
|
|
1984
|
-
|
|
2291
|
+
let appServerScope;
|
|
2292
|
+
const getAppServerScope = () => appServerScope ??= resolveCommandAppServerScope(deps, ctx, options.pluginConfig);
|
|
2293
|
+
return await handleCodexPluginsSubcommand(ctx, rest, deps.codexPluginsManagementIo, {
|
|
2294
|
+
workspaceDir: async () => {
|
|
2295
|
+
const data = readCodexConversationBindingData(await ctx.getCurrentConversationBinding());
|
|
2296
|
+
return (data?.kind === "codex-app-server-session" ? data.workspaceDir : void 0)?.trim() || deps.resolveCodexDefaultWorkspaceDir(options.pluginConfig);
|
|
2297
|
+
},
|
|
2298
|
+
list: async (requestParams) => {
|
|
2299
|
+
const scope = await getAppServerScope();
|
|
2300
|
+
return await deps.codexControlRequest(options.pluginConfig, CODEX_CONTROL_METHODS.listPlugins, requestParams, {
|
|
2301
|
+
...scope,
|
|
2302
|
+
config: ctx.config
|
|
2303
|
+
});
|
|
2304
|
+
},
|
|
2305
|
+
install: async (requestParams) => {
|
|
2306
|
+
const scope = await getAppServerScope();
|
|
2307
|
+
return await deps.codexControlRequest(options.pluginConfig, CODEX_CONTROL_METHODS.installPlugin, requestParams, {
|
|
2308
|
+
...scope,
|
|
2309
|
+
config: ctx.config
|
|
2310
|
+
});
|
|
2311
|
+
},
|
|
2312
|
+
refresh: async (workspaceDir) => {
|
|
2313
|
+
const scope = await getAppServerScope();
|
|
2314
|
+
const configuredAppServer = resolveCodexAppServerRuntimeOptions({ pluginConfig: options.pluginConfig });
|
|
2315
|
+
const appServer = scope.startOptions ? {
|
|
2316
|
+
...configuredAppServer,
|
|
2317
|
+
start: scope.startOptions
|
|
2318
|
+
} : configuredAppServer;
|
|
2319
|
+
const authProfileId = scope.authProfileId ?? void 0;
|
|
2320
|
+
const accountId = await resolveCodexAppServerAuthAccountCacheKey({
|
|
2321
|
+
authProfileId,
|
|
2322
|
+
agentDir: scope.agentDir,
|
|
2323
|
+
config: ctx.config
|
|
2324
|
+
});
|
|
2325
|
+
const client = await getLeasedSharedCodexAppServerClient({
|
|
2326
|
+
startOptions: appServer.start,
|
|
2327
|
+
pluginConfig: options.pluginConfig,
|
|
2328
|
+
authProfileId: scope.authProfileId,
|
|
2329
|
+
agentDir: scope.agentDir,
|
|
2330
|
+
config: ctx.config
|
|
2331
|
+
});
|
|
2332
|
+
try {
|
|
2333
|
+
const appCacheKey = buildCodexPluginAppCacheKey({
|
|
2334
|
+
appServer,
|
|
2335
|
+
agentDir: scope.agentDir,
|
|
2336
|
+
authProfileId,
|
|
2337
|
+
accountId,
|
|
2338
|
+
envApiKeyFingerprint: authProfileId ? void 0 : resolveCodexAppServerFallbackApiKeyCacheKey({ startOptions: appServer.start }),
|
|
2339
|
+
appServerVersion: client.getServerVersion(),
|
|
2340
|
+
runtimeIdentity: client.getRuntimeIdentity()
|
|
2341
|
+
});
|
|
2342
|
+
defaultCodexPluginMetadataCache.invalidate(appCacheKey);
|
|
2343
|
+
return await refreshCodexPluginRuntimeState({
|
|
2344
|
+
configCwd: workspaceDir,
|
|
2345
|
+
appCache: defaultCodexAppInventoryCache,
|
|
2346
|
+
appCacheKey,
|
|
2347
|
+
metadataCache: defaultCodexPluginMetadataCache,
|
|
2348
|
+
request: async (method, requestParams) => {
|
|
2349
|
+
const requestMethod = resolvePluginRuntimeRefreshMethod(method);
|
|
2350
|
+
return await deps.codexControlRequest(options.pluginConfig, requestMethod, requestParams, {
|
|
2351
|
+
...scope,
|
|
2352
|
+
config: ctx.config
|
|
2353
|
+
});
|
|
2354
|
+
}
|
|
2355
|
+
});
|
|
2356
|
+
} finally {
|
|
2357
|
+
releaseLeasedSharedCodexAppServerClient(client);
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
});
|
|
1985
2361
|
}
|
|
1986
2362
|
if (normalized === "status") {
|
|
1987
2363
|
if (rest.length > 0) return { text: "Usage: /codex status" };
|
|
@@ -2062,5 +2438,18 @@ async function handleCodexSubcommand(ctx, options) {
|
|
|
2062
2438
|
}
|
|
2063
2439
|
return { text: `Unknown Codex command: ${formatCodexDisplayText(subcommand)}\n\n${buildHelp()}` };
|
|
2064
2440
|
}
|
|
2441
|
+
function resolvePluginRuntimeRefreshMethod(method) {
|
|
2442
|
+
const recognized = [
|
|
2443
|
+
CODEX_CONTROL_METHODS.listPlugins,
|
|
2444
|
+
CODEX_CONTROL_METHODS.listSkills,
|
|
2445
|
+
CODEX_CONTROL_METHODS.listHooks,
|
|
2446
|
+
CODEX_CONTROL_METHODS.reloadMcpServers,
|
|
2447
|
+
CODEX_CONTROL_METHODS.installedApps,
|
|
2448
|
+
CODEX_CONTROL_METHODS.listApps,
|
|
2449
|
+
CODEX_CONTROL_METHODS.readApps
|
|
2450
|
+
].find((candidate) => candidate === method);
|
|
2451
|
+
if (!recognized) throw new Error(`Unexpected Codex plugin refresh method: ${method}`);
|
|
2452
|
+
return recognized;
|
|
2453
|
+
}
|
|
2065
2454
|
//#endregion
|
|
2066
2455
|
export { handleCodexSubcommand };
|