@openclaw/codex 2026.6.11 → 2026.7.1-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-server-policy-BPTiVNsW.js → app-server-policy-C968Kgin.js} +5 -5
- package/dist/{attempt-notifications-BYnndyFl.js → attempt-notifications-BGsEIIDI.js} +29 -40
- package/dist/{command-handlers-DEPTJYk0.js → command-handlers-Dv-pzAmg.js} +278 -237
- package/dist/command-rpc-eLM_mtC7.js +784 -0
- package/dist/compact-4mVBriKT.js +603 -0
- package/dist/{computer-use-ClrJwoFp.js → computer-use-Bmaz333N.js} +3 -3
- package/dist/{config-CszD0vP3.js → config-CYEDnLJ2.js} +44 -18
- package/dist/conversation-binding-data-BHRpYbX3.js +121 -0
- package/dist/doctor-contract-api.js +590 -22
- package/dist/harness.js +29 -10
- package/dist/index.js +1134 -35
- package/dist/{media-understanding-provider-ZUQHN7gT.js → media-understanding-provider-BHOu9-Gg.js} +13 -8
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-CAmfBWRl.js → models-DDmO1zwd.js} +2 -2
- package/dist/notification-correlation-Bo7KB3ks.js +35 -0
- package/dist/{plugin-app-cache-key-C9ILt7bl.js → plugin-app-cache-key-BrhVdeEf.js} +13 -5
- package/dist/{protocol-dh-ETiNd.js → protocol-2POPqAY4.js} +2 -1
- package/dist/{protocol-validators-B19q5BIX.js → protocol-validators-dZQ-UTOa.js} +205 -56
- package/dist/provider-capabilities-DzI8fgEu.js +6172 -0
- package/dist/provider-catalog.js +4 -2
- package/dist/{provider-B-OHpbD3.js → provider-zjPfx5Fs.js} +94 -23
- package/dist/provider.js +2 -2
- package/dist/{request-Dcd452Nk.js → request-BcJyl8KL.js} +2 -2
- package/dist/{run-attempt-CXxtFC9d.js → run-attempt-DNSNk_G8.js} +1250 -2992
- package/dist/{sandbox-guard-3tnjhjFb.js → sandbox-guard-DA2TQfZW.js} +5 -3
- package/dist/session-binding-C1ZXdP-x.js +627 -0
- package/dist/session-binding-meta-B7aEMU7g.js +6 -0
- package/dist/shared-client-4ICy3U6d.js +1853 -0
- package/dist/{side-question-C-DhgJOd.js → side-question-CMOpEhvY.js} +134 -142
- package/dist/{thread-lifecycle-DtT3-ehU.js → thread-lifecycle-qWE88Dn2.js} +803 -481
- package/dist/{web-search-provider.runtime-CkwxrXQj.js → web-search-provider.runtime-BSlriav6.js} +3 -3
- package/npm-shrinkwrap.json +34 -34
- package/openclaw.plugin.json +23 -3
- package/package.json +9 -6
- package/dist/client-CLrtWgrD.js +0 -729
- package/dist/client-factory-Bm6HsGob.js +0 -18
- package/dist/compact-aQJ0ZKhe.js +0 -278
- package/dist/conversation-binding-t0XMLz-j.js +0 -1557
- package/dist/provider-capabilities-D7hnTO76.js +0 -3201
- package/dist/rate-limit-cache-C7qmZ0Jh.js +0 -26
- package/dist/session-binding-HOuPt-E0.js +0 -338
- package/dist/shared-client-Dtx5i7Ez.js +0 -1013
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { a as
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { n as
|
|
12
|
-
import { n as
|
|
1
|
+
import { r as isJsonObject } from "./protocol-2POPqAY4.js";
|
|
2
|
+
import { o as isCodexFastServiceTier } from "./config-CYEDnLJ2.js";
|
|
3
|
+
import { n as assertCodexThreadResumeResponse } from "./protocol-validators-dZQ-UTOa.js";
|
|
4
|
+
import { t as listAllCodexAppServerModels } from "./models-DDmO1zwd.js";
|
|
5
|
+
import { p as summarizeCodexAccountUsage } from "./provider-zjPfx5Fs.js";
|
|
6
|
+
import { y as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-4ICy3U6d.js";
|
|
7
|
+
import { a as normalizeCodexAppServerBindingModelProvider, n as bindingStoreKey, o as reclaimCurrentCodexSessionGeneration, s as sessionBindingIdentity } from "./session-binding-C1ZXdP-x.js";
|
|
8
|
+
import { r as CODEX_CONTROL_METHODS } from "./plugin-app-cache-key-BrhVdeEf.js";
|
|
9
|
+
import { a as formatCodexDisplayText, c as formatList, d as formatThreads, f as readString$1, i as formatAccount, l as formatModels, o as formatCodexStatus, r as buildHelp, s as formatComputerUseStatus, u as formatSkills } from "./app-server-policy-C968Kgin.js";
|
|
10
|
+
import { n as resolveCodexNativeExecutionBlock, r as resolveCodexNativeSandboxBlock } from "./sandbox-guard-DA2TQfZW.js";
|
|
11
|
+
import { S as canMutateCodexHost, _ as setCodexConversationPermissions, d as formatPermissionsMode, f as parseCodexFastModeArg, g as setCodexConversationModel, h as setCodexConversationFastMode, i as safeCodexControlRequest, m as readCodexConversationActiveTurn, n as readCodexStatusProbes, p as parseCodexPermissionsModeArg, r as requestOptions, s as formatCodexCliSessions, t as codexControlRequest, v as steerCodexConversationTurn, x as CODEX_NATIVE_EXECUTION_AUTH_ERROR, y as stopCodexConversationTurn } from "./command-rpc-eLM_mtC7.js";
|
|
12
|
+
import { i as readCodexConversationBindingData, n as createCodexCliNodeConversationBindingData, o as resolveCodexDefaultWorkspaceDir, r as createCodexConversationBindingData } from "./conversation-binding-data-BHRpYbX3.js";
|
|
13
|
+
import { n as installCodexComputerUse, r as readCodexComputerUseStatus } from "./computer-use-Bmaz333N.js";
|
|
13
14
|
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
14
|
-
import { normalizeOptionalString, normalizeUniqueStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
15
15
|
import crypto from "node:crypto";
|
|
16
16
|
import { ensureAuthProfileStore, findNormalizedProviderValue, resolveAgentDir, resolveAuthProfileEligibility, resolveAuthProfileOrder, resolveDefaultAgentDir, resolveProfileUnusableUntilForDisplay, resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
|
|
17
|
+
import { parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
|
18
|
+
import { normalizeOptionalString, normalizeUniqueStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
17
19
|
//#region extensions/codex/src/command-account.ts
|
|
18
20
|
const OPENAI_PROVIDER_ID = "openai";
|
|
19
21
|
const OPENAI_CODEX_PROVIDER_ID = OPENAI_PROVIDER_ID;
|
|
@@ -163,7 +165,6 @@ async function readSubscriptionUsage(params) {
|
|
|
163
165
|
isolated: true
|
|
164
166
|
});
|
|
165
167
|
if (!limits.ok) return;
|
|
166
|
-
rememberCodexRateLimits(limits.value);
|
|
167
168
|
return summarizeCodexAccountUsage(limits.value, params.now);
|
|
168
169
|
}
|
|
169
170
|
function buildProfileRow(params) {
|
|
@@ -349,7 +350,7 @@ async function handleCodexPluginsSubcommand(ctx, rest, io) {
|
|
|
349
350
|
if (normalized === "enable" || normalized === "disable") {
|
|
350
351
|
if (args.length === 0) return buildPluginNamePickerReply(normalized, await io.readConfig());
|
|
351
352
|
if (!target || args.length > 1) return { text: `Usage: /codex plugins ${normalized} <name>` };
|
|
352
|
-
if (!
|
|
353
|
+
if (!canMutateCodexHost(ctx)) return { text: `Only an owner or operator.admin gateway client can run /codex plugins ${normalized}.` };
|
|
353
354
|
const wantEnabled = normalized === "enable";
|
|
354
355
|
if (!((await io.readConfig()).plugins ?? {})[target]) return { text: `Codex sub-plugin '${formatCodexDisplayText(target)}' is not configured. Run '/codex plugins list' to see configured plugins.` };
|
|
355
356
|
await io.mutate((block) => {
|
|
@@ -440,10 +441,6 @@ function buildPluginNamePickerReply(verb, current) {
|
|
|
440
441
|
presentation: buildCodexCommandPickerPresentation("Codex sub-plugins", `Pick a Codex sub-plugin to ${verb}:`, buttons)
|
|
441
442
|
};
|
|
442
443
|
}
|
|
443
|
-
function canMutateCodexPlugins(ctx) {
|
|
444
|
-
if (ctx.senderIsOwner === true) return true;
|
|
445
|
-
return ctx.gatewayClientScopes?.includes("operator.admin") === true;
|
|
446
|
-
}
|
|
447
444
|
function buildPluginsHelp() {
|
|
448
445
|
return [
|
|
449
446
|
"Codex sub-plugin management (writes only to ~/.openclaw/openclaw.json, never to ~/.codex/config.toml):",
|
|
@@ -479,81 +476,16 @@ function formatPluginList(plugins, options = {}) {
|
|
|
479
476
|
].join("\n");
|
|
480
477
|
}
|
|
481
478
|
//#endregion
|
|
482
|
-
//#region extensions/codex/src/command-rpc.ts
|
|
483
|
-
function requestOptions(pluginConfig, limit, config) {
|
|
484
|
-
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig });
|
|
485
|
-
return {
|
|
486
|
-
limit,
|
|
487
|
-
timeoutMs: runtime.requestTimeoutMs,
|
|
488
|
-
startOptions: runtime.start,
|
|
489
|
-
config
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
async function codexControlRequest(pluginConfig, method, requestParams, options = {}) {
|
|
493
|
-
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig });
|
|
494
|
-
return await requestCodexAppServerJson({
|
|
495
|
-
method,
|
|
496
|
-
requestParams,
|
|
497
|
-
timeoutMs: runtime.requestTimeoutMs,
|
|
498
|
-
startOptions: runtime.start,
|
|
499
|
-
config: options.config,
|
|
500
|
-
sessionKey: options.sessionKey,
|
|
501
|
-
sessionId: options.sessionId,
|
|
502
|
-
authProfileId: options.authProfileId,
|
|
503
|
-
agentDir: options.agentDir,
|
|
504
|
-
isolated: options.isolated
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
async function safeCodexControlRequest(pluginConfig, method, requestParams, options = {}) {
|
|
508
|
-
return await safeValue(async () => await codexControlRequest(pluginConfig, method, requestParams, options));
|
|
509
|
-
}
|
|
510
|
-
async function safeCodexModelList(pluginConfig, limit, config) {
|
|
511
|
-
return await safeValue(async () => await listCodexAppServerModels(requestOptions(pluginConfig, limit, config)));
|
|
512
|
-
}
|
|
513
|
-
async function readCodexStatusProbes(pluginConfig, config) {
|
|
514
|
-
const [models, account, limits, mcps, skills] = await Promise.all([
|
|
515
|
-
safeCodexModelList(pluginConfig, 20, config),
|
|
516
|
-
safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.account, { refreshToken: false }, { config }),
|
|
517
|
-
safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.rateLimits, void 0, { config }),
|
|
518
|
-
safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listMcpServers, { limit: 100 }, { config }),
|
|
519
|
-
safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listSkills, {}, { config })
|
|
520
|
-
]);
|
|
521
|
-
return {
|
|
522
|
-
models,
|
|
523
|
-
account,
|
|
524
|
-
limits,
|
|
525
|
-
mcps,
|
|
526
|
-
skills
|
|
527
|
-
};
|
|
528
|
-
}
|
|
529
|
-
async function safeValue(read) {
|
|
530
|
-
try {
|
|
531
|
-
return {
|
|
532
|
-
ok: true,
|
|
533
|
-
value: await read()
|
|
534
|
-
};
|
|
535
|
-
} catch (error) {
|
|
536
|
-
return {
|
|
537
|
-
ok: false,
|
|
538
|
-
error: describeControlFailure(error)
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
//#endregion
|
|
543
479
|
//#region extensions/codex/src/command-handlers.ts
|
|
544
480
|
const defaultCodexCommandDeps = {
|
|
545
481
|
codexControlRequest,
|
|
546
482
|
listCodexAppServerModels: listAllCodexAppServerModels,
|
|
547
483
|
readCodexStatusProbes,
|
|
548
|
-
readCodexAppServerBinding,
|
|
549
484
|
requestOptions,
|
|
550
485
|
safeCodexControlRequest,
|
|
551
|
-
writeCodexAppServerBinding,
|
|
552
|
-
clearCodexAppServerBinding,
|
|
553
486
|
readCodexComputerUseStatus,
|
|
554
487
|
installCodexComputerUse,
|
|
555
488
|
resolveCodexDefaultWorkspaceDir,
|
|
556
|
-
startCodexConversationThread,
|
|
557
489
|
readCodexConversationActiveTurn,
|
|
558
490
|
setCodexConversationFastMode,
|
|
559
491
|
setCodexConversationModel,
|
|
@@ -578,7 +510,7 @@ const CODEX_DIAGNOSTICS_CONFIRMATION_MAX_REQUESTS_PER_SCOPE = 100;
|
|
|
578
510
|
const CODEX_DIAGNOSTICS_CONFIRMATION_MAX_SCOPES = 100;
|
|
579
511
|
const CODEX_DIAGNOSTICS_SCOPE_FIELD_MAX_CHARS = 128;
|
|
580
512
|
const CODEX_RESUME_SAFE_THREAD_ID_PATTERN = /^[A-Za-z0-9._:-]+$/;
|
|
581
|
-
const CODEX_NATIVE_EXECUTION_SUBCOMMANDS = new Set([
|
|
513
|
+
const CODEX_NATIVE_EXECUTION_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
582
514
|
"bind",
|
|
583
515
|
"resume",
|
|
584
516
|
"steer",
|
|
@@ -588,6 +520,12 @@ const CODEX_NATIVE_EXECUTION_SUBCOMMANDS = new Set([
|
|
|
588
520
|
"compact",
|
|
589
521
|
"review"
|
|
590
522
|
]);
|
|
523
|
+
const CODEX_NATIVE_CONTROL_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
524
|
+
...CODEX_NATIVE_EXECUTION_SUBCOMMANDS,
|
|
525
|
+
"detach",
|
|
526
|
+
"unbind",
|
|
527
|
+
"stop"
|
|
528
|
+
]);
|
|
591
529
|
const lastCodexDiagnosticsUploadByThread = /* @__PURE__ */ new Map();
|
|
592
530
|
const lastCodexDiagnosticsUploadByScope = /* @__PURE__ */ new Map();
|
|
593
531
|
const pendingCodexDiagnosticsConfirmations = /* @__PURE__ */ new Map();
|
|
@@ -725,6 +663,7 @@ async function handleCodexSubcommand(ctx, options) {
|
|
|
725
663
|
const [subcommand = "status", ...rest] = args;
|
|
726
664
|
const normalized = subcommand.toLowerCase();
|
|
727
665
|
if (normalized === "help") return { text: buildHelp() };
|
|
666
|
+
if (CODEX_NATIVE_CONTROL_SUBCOMMANDS.has(normalized) && !returnsBeforeNativeCodexExecution(normalized, rest) && !canMutateCodexHost(ctx)) return { text: CODEX_NATIVE_EXECUTION_AUTH_ERROR };
|
|
728
667
|
const sandboxBlock = resolveCodexNativeCommandSandboxBlock(ctx, normalized, rest);
|
|
729
668
|
if (sandboxBlock) return { text: sandboxBlock };
|
|
730
669
|
if (normalized === "plugins") {
|
|
@@ -739,7 +678,7 @@ async function handleCodexSubcommand(ctx, options) {
|
|
|
739
678
|
if (rest.length > 0) return { text: "Usage: /codex models" };
|
|
740
679
|
return { text: formatModels(await deps.listCodexAppServerModels(deps.requestOptions(options.pluginConfig, 100, ctx.config))) };
|
|
741
680
|
}
|
|
742
|
-
if (normalized === "threads") return { text: await buildThreads(deps, options.pluginConfig, rest.join(" ")) };
|
|
681
|
+
if (normalized === "threads") return { text: await buildThreads(deps, ctx, options.pluginConfig, rest.join(" ")) };
|
|
743
682
|
if (normalized === "sessions") return { text: await buildCodexCliSessions(deps, rest) };
|
|
744
683
|
if (normalized === "resume") return { text: await resumeThread(deps, ctx, options.pluginConfig, rest) };
|
|
745
684
|
if (normalized === "bind") return await bindConversation(deps, ctx, options.pluginConfig, rest);
|
|
@@ -759,31 +698,43 @@ async function handleCodexSubcommand(ctx, options) {
|
|
|
759
698
|
if (normalized === "model") return { text: await setConversationModel(deps, ctx, options.pluginConfig, rest) };
|
|
760
699
|
if (normalized === "fast") {
|
|
761
700
|
if (isMenuVerb(rest)) return buildCodexFastMenuReply();
|
|
762
|
-
return { text: await setConversationFastMode(deps, ctx,
|
|
701
|
+
return { text: await setConversationFastMode(deps, ctx, rest) };
|
|
763
702
|
}
|
|
764
703
|
if (normalized === "permissions") {
|
|
765
704
|
if (isMenuVerb(rest)) return buildCodexPermissionsMenuReply();
|
|
766
|
-
return { text: await setConversationPermissions(deps, ctx,
|
|
705
|
+
return { text: await setConversationPermissions(deps, ctx, rest) };
|
|
767
706
|
}
|
|
768
|
-
if (normalized === "compact") return { text: await startThreadAction(deps, ctx, options.pluginConfig,
|
|
769
|
-
if (normalized === "review") return { text: await startThreadAction(deps, ctx, options.pluginConfig,
|
|
707
|
+
if (normalized === "compact") return { text: await startThreadAction(deps, ctx, options.pluginConfig, "compact", rest) };
|
|
708
|
+
if (normalized === "review") return { text: await startThreadAction(deps, ctx, options.pluginConfig, "review", rest) };
|
|
770
709
|
if (normalized === "diagnostics") return await handleCodexDiagnosticsFeedback(deps, ctx, options.pluginConfig, rest.join(" "), "/codex diagnostics");
|
|
771
710
|
if (normalized === "computer-use" || normalized === "computeruse") {
|
|
772
711
|
if (isMenuVerb(rest)) return buildCodexComputerUseMenuReply();
|
|
773
|
-
return { text: await handleComputerUseCommand(deps, options.pluginConfig, rest) };
|
|
712
|
+
return { text: await handleComputerUseCommand(deps, ctx, options.pluginConfig, rest) };
|
|
774
713
|
}
|
|
775
714
|
if (normalized === "mcp") {
|
|
776
715
|
if (rest.length > 0) return { text: "Usage: /codex mcp" };
|
|
777
|
-
|
|
716
|
+
const scope = await resolveCommandAppServerScope(deps, ctx);
|
|
717
|
+
return { text: formatList(await deps.codexControlRequest(options.pluginConfig, CODEX_CONTROL_METHODS.listMcpServers, { limit: 100 }, {
|
|
718
|
+
config: ctx.config,
|
|
719
|
+
...scope
|
|
720
|
+
}), "MCP servers") };
|
|
778
721
|
}
|
|
779
722
|
if (normalized === "skills") {
|
|
780
723
|
if (rest.length > 0) return { text: "Usage: /codex skills" };
|
|
781
|
-
|
|
724
|
+
const scope = await resolveCommandAppServerScope(deps, ctx);
|
|
725
|
+
return { text: formatSkills(await deps.codexControlRequest(options.pluginConfig, CODEX_CONTROL_METHODS.listSkills, {}, {
|
|
726
|
+
config: ctx.config,
|
|
727
|
+
...scope
|
|
728
|
+
})) };
|
|
782
729
|
}
|
|
783
730
|
if (normalized === "account") {
|
|
784
731
|
if (rest.length > 0) return { text: "Usage: /codex account" };
|
|
785
|
-
const
|
|
786
|
-
|
|
732
|
+
const scope = await resolveCommandAppServerScope(deps, ctx);
|
|
733
|
+
const requestScope = {
|
|
734
|
+
config: ctx.config,
|
|
735
|
+
...scope
|
|
736
|
+
};
|
|
737
|
+
const [account, limits] = await Promise.all([deps.safeCodexControlRequest(options.pluginConfig, CODEX_CONTROL_METHODS.account, { refreshToken: false }, requestScope), deps.safeCodexControlRequest(options.pluginConfig, CODEX_CONTROL_METHODS.rateLimits, void 0, requestScope)]);
|
|
787
738
|
return { text: formatAccount(account, limits, await readCodexAccountAuthOverview({
|
|
788
739
|
ctx,
|
|
789
740
|
pluginConfig: options.pluginConfig,
|
|
@@ -805,6 +756,7 @@ function resolveCodexNativeCommandSandboxBlock(ctx, subcommand, args) {
|
|
|
805
756
|
});
|
|
806
757
|
return resolveCodexNativeExecutionBlock({
|
|
807
758
|
config: ctx.config,
|
|
759
|
+
agentId: ctx.agentId,
|
|
808
760
|
sessionKey: ctx.sessionKey,
|
|
809
761
|
sessionId: ctx.sessionId,
|
|
810
762
|
surface: `/${["codex", subcommand].join(" ")}`
|
|
@@ -820,6 +772,8 @@ function returnsBeforeNativeCodexExecution(subcommand, args) {
|
|
|
820
772
|
case "permissions": return args.length === 0 || args.length > 1 || parseCodexPermissionsModeArg(args[0]) === void 0;
|
|
821
773
|
case "compact":
|
|
822
774
|
case "review":
|
|
775
|
+
case "detach":
|
|
776
|
+
case "unbind":
|
|
823
777
|
case "stop": return args.length > 0;
|
|
824
778
|
default: return false;
|
|
825
779
|
}
|
|
@@ -836,9 +790,10 @@ function returnsBeforeNativeCodexResume(args) {
|
|
|
836
790
|
if (parsed.host) return !normalizedThreadId || parsed.bindHere !== true;
|
|
837
791
|
return !normalizedThreadId || args.length !== 1;
|
|
838
792
|
}
|
|
839
|
-
async function handleComputerUseCommand(deps, pluginConfig, args) {
|
|
793
|
+
async function handleComputerUseCommand(deps, ctx, pluginConfig, args) {
|
|
840
794
|
const parsed = parseComputerUseArgs(args);
|
|
841
795
|
if (parsed.help) return ["Usage: /codex computer-use [status|install] [--source <marketplace-source>] [--marketplace-path <path>] [--marketplace <name>]", "Checks or installs the configured Codex Computer Use plugin through app-server."].join("\n");
|
|
796
|
+
if (parsed.action === "install" && !canMutateCodexHost(ctx)) return "Only an owner or operator.admin gateway client can configure Codex Computer Use.";
|
|
842
797
|
const params = {
|
|
843
798
|
pluginConfig,
|
|
844
799
|
forceEnable: parsed.action === "install" || parsed.hasOverrides,
|
|
@@ -850,53 +805,54 @@ async function handleComputerUseCommand(deps, pluginConfig, args) {
|
|
|
850
805
|
async function bindConversation(deps, ctx, pluginConfig, args) {
|
|
851
806
|
const parsed = parseBindArgs(args);
|
|
852
807
|
if (parsed.help) return { text: "Usage: /codex bind [thread-id] [--cwd <path>] [--model <model>] [--provider <provider>]" };
|
|
853
|
-
if (!ctx.sessionFile) return { text: "Cannot bind Codex because this command did not include an OpenClaw session file." };
|
|
854
808
|
const scope = resolveCodexConversationControlScope(ctx);
|
|
855
809
|
const workspaceDir = parsed.cwd ?? deps.resolveCodexDefaultWorkspaceDir(pluginConfig);
|
|
856
|
-
const
|
|
857
|
-
|
|
810
|
+
const currentConversation = await ctx.getCurrentConversationBinding();
|
|
811
|
+
const currentConversationData = readCodexConversationBindingData(currentConversation);
|
|
812
|
+
const bindingId = currentConversationData?.kind === "codex-app-server-session" ? currentConversationData.bindingId : currentConversation ? `conversation-${currentConversation.bindingId}` : void 0;
|
|
813
|
+
const sessionOwner = ctx.sessionId ? sessionBindingIdentity({
|
|
814
|
+
sessionId: ctx.sessionId,
|
|
815
|
+
sessionKey: ctx.sessionKey,
|
|
816
|
+
agentId: scope.agentId,
|
|
858
817
|
config: ctx.config
|
|
859
|
-
})
|
|
860
|
-
const
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
818
|
+
}) : void 0;
|
|
819
|
+
const currentOwner = currentConversationData?.kind === "codex-app-server-session" ? conversationBindingIdentity(currentConversationData.bindingId) : sessionOwner;
|
|
820
|
+
const existingBinding = currentOwner ? await deps.bindingStore.read(currentOwner) : void 0;
|
|
821
|
+
const sessionSource = sessionOwner && existingBinding ? {
|
|
822
|
+
agentId: sessionOwner.agentId,
|
|
823
|
+
sessionId: sessionOwner.sessionId,
|
|
824
|
+
threadId: existingBinding.threadId,
|
|
825
|
+
...sessionOwner.sessionKey ? { sessionKey: sessionOwner.sessionKey } : {}
|
|
826
|
+
} : void 0;
|
|
827
|
+
const authProfileId = existingBinding?.authProfileId;
|
|
828
|
+
const data = createCodexConversationBindingData({
|
|
829
|
+
bindingId,
|
|
864
830
|
workspaceDir,
|
|
831
|
+
agentId: scope.agentId,
|
|
865
832
|
agentDir: scope.agentDir,
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
request = await ctx.requestConversationBinding({
|
|
882
|
-
summary,
|
|
883
|
-
detachHint: "/codex detach",
|
|
884
|
-
data
|
|
885
|
-
});
|
|
886
|
-
} catch (error) {
|
|
887
|
-
await deps.clearCodexAppServerBinding(ctx.sessionFile);
|
|
888
|
-
throw error;
|
|
889
|
-
}
|
|
890
|
-
if (request.status === "bound") return { text: `Bound this conversation to Codex thread ${formatCodexDisplayText(threadId)} in ${formatCodexDisplayText(workspaceDir)}.` };
|
|
833
|
+
source: currentConversationData?.kind === "codex-app-server-session" ? currentConversationData.source : sessionSource,
|
|
834
|
+
start: {
|
|
835
|
+
id: crypto.randomUUID(),
|
|
836
|
+
threadId: parsed.threadId,
|
|
837
|
+
model: parsed.model,
|
|
838
|
+
modelProvider: parsed.provider,
|
|
839
|
+
authProfileId
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
const threadLabel = parsed.threadId ?? "a new thread";
|
|
843
|
+
const request = await ctx.requestConversationBinding({
|
|
844
|
+
summary: `Codex app-server thread ${formatCodexDisplayText(threadLabel)} in ${formatCodexDisplayText(workspaceDir)}`,
|
|
845
|
+
detachHint: "/codex detach",
|
|
846
|
+
data
|
|
847
|
+
});
|
|
891
848
|
if (request.status === "pending") return request.reply;
|
|
892
|
-
|
|
893
|
-
return { text: formatCodexDisplayText(
|
|
849
|
+
if (request.status === "error") return { text: formatCodexDisplayText(request.message) };
|
|
850
|
+
return { text: `Bound this conversation to ${formatCodexDisplayText(threadLabel)} in ${formatCodexDisplayText(workspaceDir)}. The next message will initialize it.` };
|
|
894
851
|
}
|
|
895
852
|
async function detachConversation(deps, ctx) {
|
|
896
853
|
const data = readCodexConversationBindingData(await ctx.getCurrentConversationBinding());
|
|
897
854
|
const detached = await ctx.detachConversationBinding();
|
|
898
|
-
if (data?.kind === "codex-app-server-session") await deps.
|
|
899
|
-
else if (ctx.sessionFile) await deps.clearCodexAppServerBinding(ctx.sessionFile);
|
|
855
|
+
if (data?.kind === "codex-app-server-session") await deps.bindingStore.mutate(conversationBindingIdentity(data.bindingId), { kind: "clear" });
|
|
900
856
|
return detached.removed ? "Detached this conversation from Codex." : "No Codex conversation binding was attached.";
|
|
901
857
|
}
|
|
902
858
|
async function describeConversationBinding(deps, ctx) {
|
|
@@ -911,11 +867,9 @@ async function describeConversationBinding(deps, ctx) {
|
|
|
911
867
|
`- Workspace: ${formatCodexDisplayText(data.cwd ?? "unknown")}`,
|
|
912
868
|
"- Active run: not tracked"
|
|
913
869
|
].join("\n");
|
|
914
|
-
const
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
});
|
|
918
|
-
const active = deps.readCodexConversationActiveTurn(data.sessionFile);
|
|
870
|
+
const identity = conversationBindingIdentity(data.bindingId);
|
|
871
|
+
const threadBinding = await deps.bindingStore.read(identity);
|
|
872
|
+
const active = deps.readCodexConversationActiveTurn(identity);
|
|
919
873
|
return [
|
|
920
874
|
"Codex conversation binding:",
|
|
921
875
|
`- Thread: ${formatCodexDisplayText(threadBinding?.threadId ?? "unknown")}`,
|
|
@@ -924,13 +878,17 @@ async function describeConversationBinding(deps, ctx) {
|
|
|
924
878
|
`- Fast: ${isCodexFastServiceTier(threadBinding?.serviceTier) ? "on" : "off"}`,
|
|
925
879
|
`- Permissions: ${threadBinding ? formatPermissionsMode(threadBinding) : "default"}`,
|
|
926
880
|
`- Active run: ${formatCodexDisplayText(active ? active.turnId : "none")}`,
|
|
927
|
-
`-
|
|
881
|
+
`- Binding: ${formatCodexDisplayText(data.bindingId)}`
|
|
928
882
|
].join("\n");
|
|
929
883
|
}
|
|
930
|
-
async function buildThreads(deps, pluginConfig, filter) {
|
|
884
|
+
async function buildThreads(deps, ctx, pluginConfig, filter) {
|
|
885
|
+
const scope = await resolveCommandAppServerScope(deps, ctx);
|
|
931
886
|
return formatThreads(await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listThreads, {
|
|
932
887
|
limit: 10,
|
|
933
888
|
...filter.trim() ? { searchTerm: filter.trim() } : {}
|
|
889
|
+
}, {
|
|
890
|
+
config: ctx.config,
|
|
891
|
+
...scope
|
|
934
892
|
}));
|
|
935
893
|
}
|
|
936
894
|
async function buildCodexCliSessions(deps, args) {
|
|
@@ -948,20 +906,58 @@ async function resumeThread(deps, ctx, pluginConfig, args) {
|
|
|
948
906
|
if (parsed.help) return args.includes("--help") || args.includes("-h") || parsed.host ? "Usage: /codex resume <thread-id>\nUsage: /codex resume <session-id> --host <node> --bind here" : "Usage: /codex resume <thread-id>";
|
|
949
907
|
if (parsed.host) return await bindCodexCliNodeSession(deps, ctx, parsed);
|
|
950
908
|
if (!normalizedThreadId || args.length !== 1) return "Usage: /codex resume <thread-id>";
|
|
951
|
-
if (!ctx.
|
|
952
|
-
const
|
|
953
|
-
|
|
954
|
-
|
|
909
|
+
if (!ctx.sessionId) return "Cannot attach a Codex thread because this command did not include an OpenClaw session id.";
|
|
910
|
+
const scope = resolveCodexConversationControlScope(ctx);
|
|
911
|
+
const identity = sessionBindingIdentity({
|
|
912
|
+
sessionId: ctx.sessionId,
|
|
913
|
+
sessionKey: ctx.sessionKey,
|
|
914
|
+
agentId: scope.agentId,
|
|
915
|
+
config: ctx.config
|
|
955
916
|
});
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
917
|
+
return await deps.bindingStore.withLease(identity, async () => {
|
|
918
|
+
if (!await reclaimCurrentCodexSessionGeneration({
|
|
919
|
+
bindingStore: deps.bindingStore,
|
|
920
|
+
identity,
|
|
921
|
+
config: ctx.config
|
|
922
|
+
})) throw new Error(`Codex session generation is no longer current: ${identity.sessionId}`);
|
|
923
|
+
const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
|
|
924
|
+
authProfileId: (await deps.bindingStore.read(identity))?.authProfileId,
|
|
925
|
+
agentDir: scope.agentDir,
|
|
926
|
+
config: ctx.config
|
|
927
|
+
});
|
|
928
|
+
const response = assertCodexThreadResumeResponse(await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.resumeThread, {
|
|
929
|
+
threadId: normalizedThreadId,
|
|
930
|
+
excludeTurns: true
|
|
931
|
+
}, {
|
|
932
|
+
config: ctx.config,
|
|
933
|
+
agentDir: scope.agentDir,
|
|
934
|
+
authProfileId,
|
|
935
|
+
sessionKey: ctx.sessionKey,
|
|
936
|
+
sessionId: ctx.sessionId
|
|
937
|
+
}));
|
|
938
|
+
const effectiveThreadId = response.thread.id;
|
|
939
|
+
if (effectiveThreadId !== normalizedThreadId) throw new Error(`Codex thread/resume returned ${effectiveThreadId} for ${normalizedThreadId}`);
|
|
940
|
+
const resumedCwd = response.thread.cwd;
|
|
941
|
+
if (typeof resumedCwd !== "string") throw new Error(`Codex thread/resume returned no cwd for ${normalizedThreadId}`);
|
|
942
|
+
const modelProvider = normalizeCodexAppServerBindingModelProvider({
|
|
943
|
+
authProfileId,
|
|
944
|
+
modelProvider: response.modelProvider ?? void 0,
|
|
945
|
+
agentDir: scope.agentDir,
|
|
946
|
+
config: ctx.config
|
|
947
|
+
});
|
|
948
|
+
if (!await deps.bindingStore.mutate(identity, {
|
|
949
|
+
kind: "set",
|
|
950
|
+
binding: {
|
|
951
|
+
threadId: effectiveThreadId,
|
|
952
|
+
cwd: resumedCwd,
|
|
953
|
+
authProfileId,
|
|
954
|
+
model: response.model,
|
|
955
|
+
modelProvider,
|
|
956
|
+
historyCoveredThrough: (/* @__PURE__ */ new Date()).toISOString()
|
|
957
|
+
}
|
|
958
|
+
})) throw new Error("Codex thread binding changed while attaching the resumed thread.");
|
|
959
|
+
return `Attached this OpenClaw session to Codex thread ${formatCodexDisplayText(effectiveThreadId)}.`;
|
|
963
960
|
});
|
|
964
|
-
return `Attached this OpenClaw session to Codex thread ${formatCodexDisplayText(effectiveThreadId)}.`;
|
|
965
961
|
}
|
|
966
962
|
async function bindCodexCliNodeSession(deps, ctx, parsed) {
|
|
967
963
|
if (!parsed.threadId || !parsed.host || parsed.bindHere !== true) return "Usage: /codex resume <session-id> --host <node> --bind here";
|
|
@@ -972,9 +968,11 @@ async function bindCodexCliNodeSession(deps, ctx, parsed) {
|
|
|
972
968
|
if (!resolved.session) return `No Codex CLI session ${formatCodexDisplayText(parsed.threadId)} was found on ${formatCodexDisplayText(parsed.host)}.`;
|
|
973
969
|
const nodeId = resolved.node.nodeId;
|
|
974
970
|
if (!nodeId) return "Cannot bind Codex CLI session because the selected node did not include a node id.";
|
|
971
|
+
const scope = resolveCodexConversationControlScope(ctx);
|
|
975
972
|
const data = createCodexCliNodeConversationBindingData({
|
|
976
973
|
nodeId,
|
|
977
974
|
sessionId: parsed.threadId,
|
|
975
|
+
agentId: scope.agentId,
|
|
978
976
|
cwd: resolved.session?.cwd
|
|
979
977
|
});
|
|
980
978
|
const summary = `Codex CLI session ${formatCodexDisplayText(parsed.threadId)} on ${formatCodexDisplayText(nodeId)}`;
|
|
@@ -989,9 +987,10 @@ async function bindCodexCliNodeSession(deps, ctx, parsed) {
|
|
|
989
987
|
}
|
|
990
988
|
async function stopConversationTurn(deps, ctx, pluginConfig) {
|
|
991
989
|
const target = await resolveControlTarget(ctx);
|
|
992
|
-
if (!target) return "Cannot stop Codex because this command did not include
|
|
990
|
+
if (!target) return "Cannot stop Codex because this command did not include a stable binding identity.";
|
|
993
991
|
return (await deps.stopCodexConversationTurn({
|
|
994
|
-
|
|
992
|
+
identity: target.identity,
|
|
993
|
+
bindingStore: deps.bindingStore,
|
|
995
994
|
pluginConfig,
|
|
996
995
|
agentDir: target.agentDir,
|
|
997
996
|
config: ctx.config
|
|
@@ -999,11 +998,12 @@ async function stopConversationTurn(deps, ctx, pluginConfig) {
|
|
|
999
998
|
}
|
|
1000
999
|
async function steerConversationTurn(deps, ctx, pluginConfig, message) {
|
|
1001
1000
|
const target = await resolveControlTarget(ctx);
|
|
1002
|
-
if (!target) return "Cannot steer Codex because this command did not include
|
|
1001
|
+
if (!target) return "Cannot steer Codex because this command did not include a stable binding identity.";
|
|
1003
1002
|
return (await deps.steerCodexConversationTurn({
|
|
1004
|
-
|
|
1005
|
-
|
|
1003
|
+
identity: target.identity,
|
|
1004
|
+
bindingStore: deps.bindingStore,
|
|
1006
1005
|
message,
|
|
1006
|
+
pluginConfig,
|
|
1007
1007
|
agentDir: target.agentDir,
|
|
1008
1008
|
config: ctx.config
|
|
1009
1009
|
})).message;
|
|
@@ -1011,77 +1011,100 @@ async function steerConversationTurn(deps, ctx, pluginConfig, message) {
|
|
|
1011
1011
|
async function setConversationModel(deps, ctx, pluginConfig, args) {
|
|
1012
1012
|
if (args.length > 1) return "Usage: /codex model <model>";
|
|
1013
1013
|
const target = await resolveControlTarget(ctx);
|
|
1014
|
-
if (!target) return "Cannot set Codex model because this command did not include
|
|
1014
|
+
if (!target) return "Cannot set Codex model because this command did not include a stable binding identity.";
|
|
1015
1015
|
const [model = ""] = args;
|
|
1016
1016
|
const normalized = model.trim();
|
|
1017
1017
|
if (!normalized) {
|
|
1018
|
-
const binding = await deps.
|
|
1019
|
-
agentDir: target.agentDir,
|
|
1020
|
-
config: ctx.config
|
|
1021
|
-
});
|
|
1018
|
+
const binding = await deps.bindingStore.read(target.identity);
|
|
1022
1019
|
return binding?.model ? `Codex model: ${formatCodexDisplayText(binding.model)}` : "Usage: /codex model <model>";
|
|
1023
1020
|
}
|
|
1024
1021
|
return await deps.setCodexConversationModel({
|
|
1025
|
-
|
|
1022
|
+
identity: target.identity,
|
|
1023
|
+
bindingStore: deps.bindingStore,
|
|
1026
1024
|
pluginConfig,
|
|
1027
1025
|
model: normalized,
|
|
1028
1026
|
agentDir: target.agentDir,
|
|
1029
1027
|
config: ctx.config
|
|
1030
1028
|
});
|
|
1031
1029
|
}
|
|
1032
|
-
async function setConversationFastMode(deps, ctx,
|
|
1030
|
+
async function setConversationFastMode(deps, ctx, args) {
|
|
1033
1031
|
if (args.length > 1) return "Usage: /codex fast [on|off|status]";
|
|
1034
1032
|
const target = await resolveControlTarget(ctx);
|
|
1035
|
-
if (!target) return "Cannot set Codex fast mode because this command did not include
|
|
1033
|
+
if (!target) return "Cannot set Codex fast mode because this command did not include a stable binding identity.";
|
|
1036
1034
|
const value = args[0];
|
|
1037
1035
|
const parsed = parseCodexFastModeArg(value);
|
|
1038
1036
|
if (value && parsed == null && value.trim().toLowerCase() !== "status") return "Usage: /codex fast [on|off|status]";
|
|
1039
1037
|
return await deps.setCodexConversationFastMode({
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
enabled: parsed
|
|
1043
|
-
agentDir: target.agentDir,
|
|
1044
|
-
config: ctx.config
|
|
1038
|
+
identity: target.identity,
|
|
1039
|
+
bindingStore: deps.bindingStore,
|
|
1040
|
+
enabled: parsed
|
|
1045
1041
|
});
|
|
1046
1042
|
}
|
|
1047
|
-
async function setConversationPermissions(deps, ctx,
|
|
1043
|
+
async function setConversationPermissions(deps, ctx, args) {
|
|
1048
1044
|
if (args.length > 1) return "Usage: /codex permissions [default|yolo|status]";
|
|
1049
1045
|
const target = await resolveControlTarget(ctx);
|
|
1050
|
-
if (!target) return "Cannot set Codex permissions because this command did not include
|
|
1046
|
+
if (!target) return "Cannot set Codex permissions because this command did not include a stable binding identity.";
|
|
1051
1047
|
const value = args[0];
|
|
1052
1048
|
const parsed = parseCodexPermissionsModeArg(value);
|
|
1053
1049
|
if (value && !parsed && value.trim().toLowerCase() !== "status") return "Usage: /codex permissions [default|yolo|status]";
|
|
1054
1050
|
return await deps.setCodexConversationPermissions({
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
mode: parsed
|
|
1058
|
-
agentDir: target.agentDir,
|
|
1059
|
-
config: ctx.config
|
|
1051
|
+
identity: target.identity,
|
|
1052
|
+
bindingStore: deps.bindingStore,
|
|
1053
|
+
mode: parsed
|
|
1060
1054
|
});
|
|
1061
1055
|
}
|
|
1062
1056
|
async function resolveControlTarget(ctx) {
|
|
1063
1057
|
const data = readCodexConversationBindingData(await ctx.getCurrentConversationBinding());
|
|
1064
1058
|
const scope = resolveCodexConversationControlScope(ctx);
|
|
1065
1059
|
if (data?.kind === "codex-app-server-session") return {
|
|
1066
|
-
|
|
1067
|
-
|
|
1060
|
+
identity: conversationBindingIdentity(data.bindingId),
|
|
1061
|
+
agentId: data.agentId ?? scope.agentId,
|
|
1062
|
+
agentDir: data.agentDir ?? scope.agentDir,
|
|
1063
|
+
requestedAuthProfileId: data.start?.authProfileId
|
|
1068
1064
|
};
|
|
1069
|
-
return ctx.
|
|
1070
|
-
|
|
1065
|
+
return ctx.sessionId ? {
|
|
1066
|
+
identity: sessionBindingIdentity({
|
|
1067
|
+
sessionId: ctx.sessionId,
|
|
1068
|
+
sessionKey: ctx.sessionKey,
|
|
1069
|
+
agentId: scope.agentId,
|
|
1070
|
+
config: ctx.config
|
|
1071
|
+
}),
|
|
1072
|
+
agentId: scope.agentId,
|
|
1071
1073
|
agentDir: scope.agentDir
|
|
1072
1074
|
} : void 0;
|
|
1073
1075
|
}
|
|
1074
|
-
async function
|
|
1075
|
-
|
|
1076
|
+
async function resolveCommandAppServerScope(deps, ctx) {
|
|
1077
|
+
const target = await resolveControlTarget(ctx);
|
|
1078
|
+
const fallback = resolveCodexConversationControlScope(ctx);
|
|
1079
|
+
const agentDir = target?.agentDir ?? fallback.agentDir;
|
|
1080
|
+
const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
|
|
1081
|
+
authProfileId: (target ? await deps.bindingStore.read(target.identity) : void 0)?.authProfileId ?? target?.requestedAuthProfileId,
|
|
1082
|
+
agentDir,
|
|
1083
|
+
config: ctx.config
|
|
1084
|
+
});
|
|
1085
|
+
return {
|
|
1086
|
+
agentId: target?.agentId ?? fallback.agentId,
|
|
1087
|
+
agentDir,
|
|
1088
|
+
...authProfileId ? { authProfileId } : {},
|
|
1089
|
+
...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
|
|
1090
|
+
...ctx.sessionId ? { sessionId: ctx.sessionId } : {}
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
function conversationBindingIdentity(bindingId) {
|
|
1094
|
+
return {
|
|
1095
|
+
kind: "conversation",
|
|
1096
|
+
bindingId
|
|
1097
|
+
};
|
|
1076
1098
|
}
|
|
1077
1099
|
function resolveCodexConversationControlScope(ctx) {
|
|
1078
1100
|
const { sessionAgentId } = resolveSessionAgentIds({
|
|
1079
1101
|
sessionKey: ctx.sessionKey,
|
|
1102
|
+
agentId: ctx.agentId,
|
|
1080
1103
|
config: ctx.config
|
|
1081
1104
|
});
|
|
1082
1105
|
return {
|
|
1083
|
-
|
|
1084
|
-
sessionAgentId
|
|
1106
|
+
agentId: sessionAgentId,
|
|
1107
|
+
agentDir: resolveAgentDir(ctx.config, sessionAgentId)
|
|
1085
1108
|
};
|
|
1086
1109
|
}
|
|
1087
1110
|
async function handleCodexDiagnosticsFeedback(deps, ctx, pluginConfig, args, commandPrefix) {
|
|
@@ -1095,7 +1118,7 @@ async function handleCodexDiagnosticsFeedback(deps, ctx, pluginConfig, args, com
|
|
|
1095
1118
|
return await requestCodexDiagnosticsFeedbackApproval(deps, ctx, parsed.note, commandPrefix);
|
|
1096
1119
|
}
|
|
1097
1120
|
async function requestCodexDiagnosticsFeedbackApproval(deps, ctx, note, commandPrefix) {
|
|
1098
|
-
if (!await
|
|
1121
|
+
if (!await hasAnyCodexDiagnosticsIdentity(ctx)) return { text: "Cannot send Codex diagnostics because this command did not include a stable session identity." };
|
|
1099
1122
|
const targets = await resolveCodexDiagnosticsTargets(deps, ctx);
|
|
1100
1123
|
if (targets.length === 0) return { text: ["No Codex thread is attached to this OpenClaw session yet.", "Use /codex threads to find a thread, then /codex resume <thread-id> before sending diagnostics."].join("\n") };
|
|
1101
1124
|
const now = Date.now();
|
|
@@ -1151,7 +1174,7 @@ async function requestCodexDiagnosticsFeedbackApproval(deps, ctx, note, commandP
|
|
|
1151
1174
|
};
|
|
1152
1175
|
}
|
|
1153
1176
|
async function previewCodexDiagnosticsFeedbackApproval(deps, ctx, note) {
|
|
1154
|
-
if (!await
|
|
1177
|
+
if (!await hasAnyCodexDiagnosticsIdentity(ctx)) return "Cannot send Codex diagnostics because this command did not include a stable session identity.";
|
|
1155
1178
|
const targets = await resolveCodexDiagnosticsTargets(deps, ctx);
|
|
1156
1179
|
if (targets.length === 0) return ["No Codex thread is attached to this OpenClaw session yet.", "Use /codex threads to find a thread, then /codex resume <thread-id> before sending diagnostics."].join("\n");
|
|
1157
1180
|
const cooldownMessage = readCodexDiagnosticsTargetsCooldownMessage(targets, ctx, Date.now(), { includeThreadId: false });
|
|
@@ -1175,10 +1198,10 @@ async function confirmCodexDiagnosticsFeedback(deps, ctx, pluginConfig, token) {
|
|
|
1175
1198
|
const scopeMismatch = readCodexDiagnosticsScopeMismatch(pending, ctx);
|
|
1176
1199
|
if (scopeMismatch) return scopeMismatch.confirmMessage;
|
|
1177
1200
|
deletePendingCodexDiagnosticsConfirmation(token);
|
|
1178
|
-
if (!pending.privateRouted && !await
|
|
1179
|
-
const currentTargets = pending.privateRouted ? await resolvePendingCodexDiagnosticsTargets(deps, pending.targets) : await resolveCodexDiagnosticsTargets(deps, ctx);
|
|
1201
|
+
if (!pending.privateRouted && !await hasAnyCodexDiagnosticsIdentity(ctx)) return "Cannot send Codex diagnostics because this command did not include a stable session identity.";
|
|
1202
|
+
const currentTargets = pending.privateRouted ? await resolvePendingCodexDiagnosticsTargets(deps, pending.targets, ctx.config) : await resolveCodexDiagnosticsTargets(deps, ctx);
|
|
1180
1203
|
if (!codexDiagnosticsTargetsMatch(pending.targets, currentTargets)) return "The Codex diagnostics sessions changed before confirmation. Run /diagnostics again for the current threads.";
|
|
1181
|
-
return await sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, pending.note ?? "", pending.
|
|
1204
|
+
return await sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, pending.note ?? "", currentTargets, { cooldownScope: pending.scopeKey });
|
|
1182
1205
|
}
|
|
1183
1206
|
function cancelCodexDiagnosticsFeedback(ctx, token) {
|
|
1184
1207
|
const pending = readPendingCodexDiagnosticsConfirmation(token, Date.now());
|
|
@@ -1196,15 +1219,15 @@ function cancelCodexDiagnosticsFeedback(ctx, token) {
|
|
|
1196
1219
|
].join("\n");
|
|
1197
1220
|
}
|
|
1198
1221
|
async function sendCodexDiagnosticsFeedbackForContext(deps, ctx, pluginConfig, note) {
|
|
1199
|
-
if (!await
|
|
1222
|
+
if (!await hasAnyCodexDiagnosticsIdentity(ctx)) return "Cannot send Codex diagnostics because this command did not include a stable session identity.";
|
|
1200
1223
|
const targets = await resolveCodexDiagnosticsTargets(deps, ctx);
|
|
1201
1224
|
if (targets.length === 0) return ["No Codex thread is attached to this OpenClaw session yet.", "Use /codex threads to find a thread, then /codex resume <thread-id> before sending diagnostics."].join("\n");
|
|
1202
1225
|
return await sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, note, targets);
|
|
1203
1226
|
}
|
|
1204
|
-
async function sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, note, targets) {
|
|
1227
|
+
async function sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, note, targets, options = {}) {
|
|
1205
1228
|
if (targets.length === 0) return ["No Codex thread is attached to this OpenClaw session yet.", "Use /codex threads to find a thread, then /codex resume <thread-id> before sending diagnostics."].join("\n");
|
|
1206
1229
|
const now = Date.now();
|
|
1207
|
-
const cooldownMessage = readCodexDiagnosticsTargetsCooldownMessage(targets, ctx, now);
|
|
1230
|
+
const cooldownMessage = readCodexDiagnosticsTargetsCooldownMessage(targets, ctx, now, { cooldownScope: options.cooldownScope });
|
|
1208
1231
|
if (cooldownMessage) return cooldownMessage;
|
|
1209
1232
|
const reason = normalizeDiagnosticsReason(note);
|
|
1210
1233
|
const sent = [];
|
|
@@ -1216,6 +1239,12 @@ async function sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, n
|
|
|
1216
1239
|
includeLogs: true,
|
|
1217
1240
|
tags: buildDiagnosticsTags(ctx),
|
|
1218
1241
|
...reason ? { reason } : {}
|
|
1242
|
+
}, {
|
|
1243
|
+
config: ctx.config,
|
|
1244
|
+
agentDir: target.agentDir,
|
|
1245
|
+
...target.authProfileId ? { authProfileId: target.authProfileId } : {},
|
|
1246
|
+
...target.sessionId ? { sessionId: target.sessionId } : {},
|
|
1247
|
+
...target.sessionKey ? { sessionKey: target.sessionKey } : {}
|
|
1219
1248
|
});
|
|
1220
1249
|
if (!response.ok) {
|
|
1221
1250
|
failed.push({
|
|
@@ -1229,20 +1258,20 @@ async function sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, n
|
|
|
1229
1258
|
...target,
|
|
1230
1259
|
threadId: responseThreadId ?? target.threadId
|
|
1231
1260
|
});
|
|
1232
|
-
recordCodexDiagnosticsUpload(target.threadId, ctx, now);
|
|
1261
|
+
recordCodexDiagnosticsUpload(target.threadId, ctx, now, options.cooldownScope);
|
|
1233
1262
|
}
|
|
1234
1263
|
return formatCodexDiagnosticsUploadResult(sent, failed);
|
|
1235
1264
|
}
|
|
1236
|
-
async function
|
|
1237
|
-
if (await
|
|
1238
|
-
return (ctx.diagnosticsSessions ?? []).some((session) => Boolean(session.
|
|
1265
|
+
async function hasAnyCodexDiagnosticsIdentity(ctx) {
|
|
1266
|
+
if (await resolveControlTarget(ctx)) return true;
|
|
1267
|
+
return (ctx.diagnosticsSessions ?? []).some((session) => Boolean(session.sessionId));
|
|
1239
1268
|
}
|
|
1240
1269
|
async function resolveCodexDiagnosticsTargets(deps, ctx) {
|
|
1241
|
-
const
|
|
1270
|
+
const activeTarget = await resolveControlTarget(ctx);
|
|
1242
1271
|
const candidates = [];
|
|
1243
|
-
if (
|
|
1244
|
-
|
|
1245
|
-
|
|
1272
|
+
if (activeTarget) candidates.push({
|
|
1273
|
+
identity: activeTarget.identity,
|
|
1274
|
+
agentDir: activeTarget.agentDir,
|
|
1246
1275
|
sessionKey: ctx.sessionKey,
|
|
1247
1276
|
sessionId: ctx.sessionId,
|
|
1248
1277
|
channel: ctx.channel,
|
|
@@ -1252,10 +1281,17 @@ async function resolveCodexDiagnosticsTargets(deps, ctx) {
|
|
|
1252
1281
|
threadParentId: ctx.threadParentId
|
|
1253
1282
|
});
|
|
1254
1283
|
for (const session of ctx.diagnosticsSessions ?? []) {
|
|
1255
|
-
if (!session.
|
|
1284
|
+
if (!session.sessionId) continue;
|
|
1285
|
+
const inventoryAgentId = session.sessionKey ? parseAgentSessionKey(session.sessionKey)?.agentId : void 0;
|
|
1286
|
+
const identity = sessionBindingIdentity({
|
|
1287
|
+
sessionId: session.sessionId,
|
|
1288
|
+
sessionKey: session.sessionKey,
|
|
1289
|
+
agentId: inventoryAgentId ?? ctx.agentId,
|
|
1290
|
+
config: ctx.config
|
|
1291
|
+
});
|
|
1256
1292
|
candidates.push({
|
|
1257
|
-
|
|
1258
|
-
|
|
1293
|
+
identity,
|
|
1294
|
+
agentDir: resolveAgentDir(ctx.config, identity.agentId),
|
|
1259
1295
|
sessionKey: session.sessionKey,
|
|
1260
1296
|
sessionId: session.sessionId,
|
|
1261
1297
|
channel: session.channel,
|
|
@@ -1265,38 +1301,50 @@ async function resolveCodexDiagnosticsTargets(deps, ctx) {
|
|
|
1265
1301
|
threadParentId: session.threadParentId
|
|
1266
1302
|
});
|
|
1267
1303
|
}
|
|
1268
|
-
const
|
|
1304
|
+
const seenBindingKeys = /* @__PURE__ */ new Set();
|
|
1269
1305
|
const seenThreadIds = /* @__PURE__ */ new Set();
|
|
1270
1306
|
const targets = [];
|
|
1271
1307
|
for (const candidate of candidates) {
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1308
|
+
const key = bindingStoreKey(candidate.identity);
|
|
1309
|
+
if (seenBindingKeys.has(key)) continue;
|
|
1310
|
+
seenBindingKeys.add(key);
|
|
1311
|
+
const binding = await deps.bindingStore.read(candidate.identity);
|
|
1275
1312
|
if (!binding?.threadId || seenThreadIds.has(binding.threadId)) continue;
|
|
1276
1313
|
seenThreadIds.add(binding.threadId);
|
|
1277
|
-
targets.push(
|
|
1278
|
-
...candidate,
|
|
1279
|
-
threadId: binding.threadId
|
|
1280
|
-
});
|
|
1314
|
+
targets.push(resolveCodexDiagnosticsTarget(candidate, binding, ctx.config));
|
|
1281
1315
|
}
|
|
1282
1316
|
return targets;
|
|
1283
1317
|
}
|
|
1284
|
-
async function resolvePendingCodexDiagnosticsTargets(deps, targets) {
|
|
1318
|
+
async function resolvePendingCodexDiagnosticsTargets(deps, targets, config) {
|
|
1285
1319
|
const resolved = [];
|
|
1286
1320
|
for (const target of targets) {
|
|
1287
|
-
const binding = await deps.
|
|
1321
|
+
const binding = await deps.bindingStore.read(target.identity);
|
|
1288
1322
|
if (!binding?.threadId) continue;
|
|
1289
|
-
resolved.push(
|
|
1290
|
-
...target,
|
|
1291
|
-
threadId: binding.threadId
|
|
1292
|
-
});
|
|
1323
|
+
resolved.push(resolveCodexDiagnosticsTarget(target, binding, config));
|
|
1293
1324
|
}
|
|
1294
1325
|
return resolved;
|
|
1295
1326
|
}
|
|
1327
|
+
function resolveCodexDiagnosticsTarget(target, binding, config) {
|
|
1328
|
+
const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
|
|
1329
|
+
authProfileId: binding.authProfileId,
|
|
1330
|
+
agentDir: target.agentDir,
|
|
1331
|
+
config
|
|
1332
|
+
});
|
|
1333
|
+
return {
|
|
1334
|
+
...target,
|
|
1335
|
+
threadId: binding.threadId,
|
|
1336
|
+
authProfileId
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1296
1339
|
function codexDiagnosticsTargetsMatch(expected, actual) {
|
|
1297
|
-
const
|
|
1298
|
-
|
|
1299
|
-
|
|
1340
|
+
const fingerprint = (target) => JSON.stringify([
|
|
1341
|
+
bindingStoreKey(target.identity),
|
|
1342
|
+
target.threadId,
|
|
1343
|
+
target.authProfileId ?? null
|
|
1344
|
+
]);
|
|
1345
|
+
const expectedTargets = expected.map(fingerprint).toSorted();
|
|
1346
|
+
const actualTargets = actual.map(fingerprint).toSorted();
|
|
1347
|
+
return expectedTargets.length === actualTargets.length && expectedTargets.every((target, index) => target === actualTargets[index]);
|
|
1300
1348
|
}
|
|
1301
1349
|
function formatCodexDiagnosticsUploadResult(sent, failed) {
|
|
1302
1350
|
const lines = [];
|
|
@@ -1498,7 +1546,7 @@ function readCodexDiagnosticsTargetsCooldownMessage(targets, ctx, now, options =
|
|
|
1498
1546
|
return `Codex diagnostics were already sent for thread ${formatCodexThreadIdForDisplay(target.threadId)} recently. Try again in ${Math.ceil(cooldownMs / 1e3)}s.`;
|
|
1499
1547
|
}
|
|
1500
1548
|
}
|
|
1501
|
-
const scopeCooldownMs = readCodexDiagnosticsScopeCooldownMs(readCodexDiagnosticsCooldownScope(ctx), now);
|
|
1549
|
+
const scopeCooldownMs = readCodexDiagnosticsScopeCooldownMs(options.cooldownScope ?? readCodexDiagnosticsCooldownScope(ctx), now);
|
|
1502
1550
|
if (scopeCooldownMs > 0) return `Codex diagnostics were already sent for this account or channel recently. Try again in ${Math.ceil(scopeCooldownMs / 1e3)}s.`;
|
|
1503
1551
|
}
|
|
1504
1552
|
function readCodexDiagnosticsScopeCooldownMs(scope, now) {
|
|
@@ -1508,9 +1556,9 @@ function readCodexDiagnosticsScopeCooldownMs(scope, now) {
|
|
|
1508
1556
|
if (remainingMs === 0) lastCodexDiagnosticsUploadByScope.delete(scope);
|
|
1509
1557
|
return remainingMs;
|
|
1510
1558
|
}
|
|
1511
|
-
function recordCodexDiagnosticsUpload(threadId, ctx, now) {
|
|
1559
|
+
function recordCodexDiagnosticsUpload(threadId, ctx, now, cooldownScope) {
|
|
1512
1560
|
pruneCodexDiagnosticsCooldowns(now);
|
|
1513
|
-
recordBoundedCodexDiagnosticsCooldown(lastCodexDiagnosticsUploadByScope, readCodexDiagnosticsCooldownScope(ctx), CODEX_DIAGNOSTICS_COOLDOWN_MAX_SCOPES, now);
|
|
1561
|
+
recordBoundedCodexDiagnosticsCooldown(lastCodexDiagnosticsUploadByScope, cooldownScope ?? readCodexDiagnosticsCooldownScope(ctx), CODEX_DIAGNOSTICS_COOLDOWN_MAX_SCOPES, now);
|
|
1514
1562
|
recordBoundedCodexDiagnosticsCooldown(lastCodexDiagnosticsUploadByThread, threadId, CODEX_DIAGNOSTICS_COOLDOWN_MAX_THREADS, now);
|
|
1515
1563
|
}
|
|
1516
1564
|
function recordBoundedCodexDiagnosticsCooldown(map, key, maxSize, now) {
|
|
@@ -1558,24 +1606,17 @@ function normalizeCodexDiagnosticsScopeField(value) {
|
|
|
1558
1606
|
if (normalized.length <= CODEX_DIAGNOSTICS_SCOPE_FIELD_MAX_CHARS) return normalized;
|
|
1559
1607
|
return `sha256:${crypto.createHash("sha256").update(normalized).digest("hex")}`;
|
|
1560
1608
|
}
|
|
1561
|
-
async function startThreadAction(deps, ctx, pluginConfig,
|
|
1609
|
+
async function startThreadAction(deps, ctx, pluginConfig, kind, args) {
|
|
1610
|
+
const label = kind === "compact" ? "compaction" : "review";
|
|
1562
1611
|
if (args.length > 0) return `Usage: /codex ${label === "compaction" ? "compact" : label}`;
|
|
1563
1612
|
const target = await resolveControlTarget(ctx);
|
|
1564
|
-
if (!target) return `Cannot start Codex ${label} because this command did not include
|
|
1565
|
-
const binding = await deps.
|
|
1566
|
-
agentDir: target.agentDir,
|
|
1567
|
-
config: ctx.config
|
|
1568
|
-
});
|
|
1613
|
+
if (!target) return `Cannot start Codex ${label} because this command did not include a stable binding identity.`;
|
|
1614
|
+
const binding = await deps.bindingStore.read(target.identity);
|
|
1569
1615
|
if (!binding?.threadId) return `No Codex thread is attached to this OpenClaw session yet.`;
|
|
1570
|
-
|
|
1616
|
+
await deps.codexControlRequest(pluginConfig, kind === "compact" ? CODEX_CONTROL_METHODS.compact : CODEX_CONTROL_METHODS.review, kind === "review" ? {
|
|
1571
1617
|
threadId: binding.threadId,
|
|
1572
1618
|
target: { type: "uncommittedChanges" }
|
|
1573
|
-
}, {
|
|
1574
|
-
agentDir: target.agentDir,
|
|
1575
|
-
authProfileId: binding.authProfileId,
|
|
1576
|
-
config: ctx.config
|
|
1577
|
-
});
|
|
1578
|
-
else await deps.codexControlRequest(pluginConfig, method, { threadId: binding.threadId }, {
|
|
1619
|
+
} : { threadId: binding.threadId }, {
|
|
1579
1620
|
agentDir: target.agentDir,
|
|
1580
1621
|
authProfileId: binding.authProfileId,
|
|
1581
1622
|
config: ctx.config
|