@openclaw/codex 2026.7.1-beta.1 → 2026.7.1-beta.4
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-xi0ZmBGO.js → app-server-policy-BUk0GLMy.js} +5 -5
- package/dist/{attempt-notifications-l9LElGOU.js → attempt-notifications-BGsEIIDI.js} +5 -40
- package/dist/{command-handlers-vZy8xX5X.js → command-handlers-UVniG5Hl.js} +267 -232
- package/dist/command-rpc-BjgEPh19.js +784 -0
- package/dist/compact-CZzbcygx.js +603 -0
- package/dist/{computer-use-FPGG-mtX.js → computer-use-DDeySrnb.js} +3 -3
- package/dist/{config-C_whBx3M.js → config-CYEDnLJ2.js} +36 -15
- package/dist/conversation-binding-data-BHRpYbX3.js +121 -0
- package/dist/doctor-contract-api.js +439 -22
- package/dist/harness.js +29 -10
- package/dist/index.js +1119 -28
- package/dist/{media-understanding-provider-C5yYzx_6.js → media-understanding-provider-DgqRvU2_.js} +13 -8
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-Ccy1_7nN.js → models-DDmO1zwd.js} +2 -2
- package/dist/notification-correlation-Bo7KB3ks.js +35 -0
- package/dist/{plugin-app-cache-key-CDioyur-.js → plugin-app-cache-key-o-AHbdaf.js} +8 -4
- package/dist/{protocol-dh-ETiNd.js → protocol-2POPqAY4.js} +2 -1
- package/dist/{protocol-validators-CB4mtyrs.js → protocol-validators-dZQ-UTOa.js} +117 -62
- package/dist/{provider-capabilities-CE5k8cmO.js → provider-capabilities-gTCwjfmh.js} +2920 -114
- package/dist/{provider-DosqwRb6.js → provider-cc_62eQE.js} +19 -10
- package/dist/provider.js +2 -2
- package/dist/{request-BQuSQF29.js → request-BcJyl8KL.js} +2 -2
- package/dist/{run-attempt-O3Zs32KI.js → run-attempt-8SPLiDlT.js} +1016 -2877
- package/dist/{sandbox-guard-pTY2COoC.js → sandbox-guard-DA2TQfZW.js} +3 -3
- package/dist/session-binding-Dc03iwRF.js +595 -0
- package/dist/session-binding-meta-B7aEMU7g.js +6 -0
- package/dist/{shared-client-rR6TDJA3.js → shared-client-4ICy3U6d.js} +892 -103
- package/dist/{side-question-DDVgFmpv.js → side-question-DkjXvYv7.js} +132 -140
- package/dist/{thread-lifecycle-DeSMZSXr.js → thread-lifecycle-BskXnNP-.js} +609 -444
- package/dist/{web-search-provider.runtime-Q89rQhsL.js → web-search-provider.runtime-B5F42P7x.js} +3 -3
- package/npm-shrinkwrap.json +34 -34
- package/openclaw.plugin.json +20 -0
- package/package.json +9 -6
- package/dist/client-IcTBKEqA.js +0 -732
- package/dist/client-factory-CwjdQQ4j.js +0 -18
- package/dist/compact-BzB9AqN1.js +0 -278
- package/dist/conversation-binding-CEE2UyYb.js +0 -1570
- package/dist/rate-limit-cache-C7qmZ0Jh.js +0 -26
- package/dist/session-binding-Cm0Gg7Z1.js +0 -339
|
@@ -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-cc_62eQE.js";
|
|
6
|
+
import { y as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-4ICy3U6d.js";
|
|
7
|
+
import { a as reclaimCurrentCodexSessionGeneration, i as normalizeCodexAppServerBindingModelProvider, n as bindingStoreKey, o as sessionBindingIdentity } from "./session-binding-Dc03iwRF.js";
|
|
8
|
+
import { r as CODEX_CONTROL_METHODS } from "./plugin-app-cache-key-o-AHbdaf.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-BUk0GLMy.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-BjgEPh19.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-DDeySrnb.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) {
|
|
@@ -475,81 +476,16 @@ function formatPluginList(plugins, options = {}) {
|
|
|
475
476
|
].join("\n");
|
|
476
477
|
}
|
|
477
478
|
//#endregion
|
|
478
|
-
//#region extensions/codex/src/command-rpc.ts
|
|
479
|
-
function requestOptions(pluginConfig, limit, config) {
|
|
480
|
-
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig });
|
|
481
|
-
return {
|
|
482
|
-
limit,
|
|
483
|
-
timeoutMs: runtime.requestTimeoutMs,
|
|
484
|
-
startOptions: runtime.start,
|
|
485
|
-
config
|
|
486
|
-
};
|
|
487
|
-
}
|
|
488
|
-
async function codexControlRequest(pluginConfig, method, requestParams, options = {}) {
|
|
489
|
-
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig });
|
|
490
|
-
return await requestCodexAppServerJson({
|
|
491
|
-
method,
|
|
492
|
-
requestParams,
|
|
493
|
-
timeoutMs: runtime.requestTimeoutMs,
|
|
494
|
-
startOptions: runtime.start,
|
|
495
|
-
config: options.config,
|
|
496
|
-
sessionKey: options.sessionKey,
|
|
497
|
-
sessionId: options.sessionId,
|
|
498
|
-
authProfileId: options.authProfileId,
|
|
499
|
-
agentDir: options.agentDir,
|
|
500
|
-
isolated: options.isolated
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
async function safeCodexControlRequest(pluginConfig, method, requestParams, options = {}) {
|
|
504
|
-
return await safeValue(async () => await codexControlRequest(pluginConfig, method, requestParams, options));
|
|
505
|
-
}
|
|
506
|
-
async function safeCodexModelList(pluginConfig, limit, config) {
|
|
507
|
-
return await safeValue(async () => await listCodexAppServerModels(requestOptions(pluginConfig, limit, config)));
|
|
508
|
-
}
|
|
509
|
-
async function readCodexStatusProbes(pluginConfig, config) {
|
|
510
|
-
const [models, account, limits, mcps, skills] = await Promise.all([
|
|
511
|
-
safeCodexModelList(pluginConfig, 20, config),
|
|
512
|
-
safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.account, { refreshToken: false }, { config }),
|
|
513
|
-
safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.rateLimits, void 0, { config }),
|
|
514
|
-
safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listMcpServers, { limit: 100 }, { config }),
|
|
515
|
-
safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listSkills, {}, { config })
|
|
516
|
-
]);
|
|
517
|
-
return {
|
|
518
|
-
models,
|
|
519
|
-
account,
|
|
520
|
-
limits,
|
|
521
|
-
mcps,
|
|
522
|
-
skills
|
|
523
|
-
};
|
|
524
|
-
}
|
|
525
|
-
async function safeValue(read) {
|
|
526
|
-
try {
|
|
527
|
-
return {
|
|
528
|
-
ok: true,
|
|
529
|
-
value: await read()
|
|
530
|
-
};
|
|
531
|
-
} catch (error) {
|
|
532
|
-
return {
|
|
533
|
-
ok: false,
|
|
534
|
-
error: describeControlFailure(error)
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
//#endregion
|
|
539
479
|
//#region extensions/codex/src/command-handlers.ts
|
|
540
480
|
const defaultCodexCommandDeps = {
|
|
541
481
|
codexControlRequest,
|
|
542
482
|
listCodexAppServerModels: listAllCodexAppServerModels,
|
|
543
483
|
readCodexStatusProbes,
|
|
544
|
-
readCodexAppServerBinding,
|
|
545
484
|
requestOptions,
|
|
546
485
|
safeCodexControlRequest,
|
|
547
|
-
writeCodexAppServerBinding,
|
|
548
|
-
clearCodexAppServerBinding,
|
|
549
486
|
readCodexComputerUseStatus,
|
|
550
487
|
installCodexComputerUse,
|
|
551
488
|
resolveCodexDefaultWorkspaceDir,
|
|
552
|
-
startCodexConversationThread,
|
|
553
489
|
readCodexConversationActiveTurn,
|
|
554
490
|
setCodexConversationFastMode,
|
|
555
491
|
setCodexConversationModel,
|
|
@@ -574,7 +510,7 @@ const CODEX_DIAGNOSTICS_CONFIRMATION_MAX_REQUESTS_PER_SCOPE = 100;
|
|
|
574
510
|
const CODEX_DIAGNOSTICS_CONFIRMATION_MAX_SCOPES = 100;
|
|
575
511
|
const CODEX_DIAGNOSTICS_SCOPE_FIELD_MAX_CHARS = 128;
|
|
576
512
|
const CODEX_RESUME_SAFE_THREAD_ID_PATTERN = /^[A-Za-z0-9._:-]+$/;
|
|
577
|
-
const CODEX_NATIVE_EXECUTION_SUBCOMMANDS = new Set([
|
|
513
|
+
const CODEX_NATIVE_EXECUTION_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
578
514
|
"bind",
|
|
579
515
|
"resume",
|
|
580
516
|
"steer",
|
|
@@ -584,7 +520,7 @@ const CODEX_NATIVE_EXECUTION_SUBCOMMANDS = new Set([
|
|
|
584
520
|
"compact",
|
|
585
521
|
"review"
|
|
586
522
|
]);
|
|
587
|
-
const CODEX_NATIVE_CONTROL_SUBCOMMANDS = new Set([
|
|
523
|
+
const CODEX_NATIVE_CONTROL_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
588
524
|
...CODEX_NATIVE_EXECUTION_SUBCOMMANDS,
|
|
589
525
|
"detach",
|
|
590
526
|
"unbind",
|
|
@@ -742,7 +678,7 @@ async function handleCodexSubcommand(ctx, options) {
|
|
|
742
678
|
if (rest.length > 0) return { text: "Usage: /codex models" };
|
|
743
679
|
return { text: formatModels(await deps.listCodexAppServerModels(deps.requestOptions(options.pluginConfig, 100, ctx.config))) };
|
|
744
680
|
}
|
|
745
|
-
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(" ")) };
|
|
746
682
|
if (normalized === "sessions") return { text: await buildCodexCliSessions(deps, rest) };
|
|
747
683
|
if (normalized === "resume") return { text: await resumeThread(deps, ctx, options.pluginConfig, rest) };
|
|
748
684
|
if (normalized === "bind") return await bindConversation(deps, ctx, options.pluginConfig, rest);
|
|
@@ -762,14 +698,14 @@ async function handleCodexSubcommand(ctx, options) {
|
|
|
762
698
|
if (normalized === "model") return { text: await setConversationModel(deps, ctx, options.pluginConfig, rest) };
|
|
763
699
|
if (normalized === "fast") {
|
|
764
700
|
if (isMenuVerb(rest)) return buildCodexFastMenuReply();
|
|
765
|
-
return { text: await setConversationFastMode(deps, ctx,
|
|
701
|
+
return { text: await setConversationFastMode(deps, ctx, rest) };
|
|
766
702
|
}
|
|
767
703
|
if (normalized === "permissions") {
|
|
768
704
|
if (isMenuVerb(rest)) return buildCodexPermissionsMenuReply();
|
|
769
|
-
return { text: await setConversationPermissions(deps, ctx,
|
|
705
|
+
return { text: await setConversationPermissions(deps, ctx, rest) };
|
|
770
706
|
}
|
|
771
|
-
if (normalized === "compact") return { text: await startThreadAction(deps, ctx, options.pluginConfig,
|
|
772
|
-
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) };
|
|
773
709
|
if (normalized === "diagnostics") return await handleCodexDiagnosticsFeedback(deps, ctx, options.pluginConfig, rest.join(" "), "/codex diagnostics");
|
|
774
710
|
if (normalized === "computer-use" || normalized === "computeruse") {
|
|
775
711
|
if (isMenuVerb(rest)) return buildCodexComputerUseMenuReply();
|
|
@@ -777,16 +713,28 @@ async function handleCodexSubcommand(ctx, options) {
|
|
|
777
713
|
}
|
|
778
714
|
if (normalized === "mcp") {
|
|
779
715
|
if (rest.length > 0) return { text: "Usage: /codex mcp" };
|
|
780
|
-
|
|
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") };
|
|
781
721
|
}
|
|
782
722
|
if (normalized === "skills") {
|
|
783
723
|
if (rest.length > 0) return { text: "Usage: /codex skills" };
|
|
784
|
-
|
|
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
|
+
})) };
|
|
785
729
|
}
|
|
786
730
|
if (normalized === "account") {
|
|
787
731
|
if (rest.length > 0) return { text: "Usage: /codex account" };
|
|
788
|
-
const
|
|
789
|
-
|
|
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)]);
|
|
790
738
|
return { text: formatAccount(account, limits, await readCodexAccountAuthOverview({
|
|
791
739
|
ctx,
|
|
792
740
|
pluginConfig: options.pluginConfig,
|
|
@@ -808,6 +756,7 @@ function resolveCodexNativeCommandSandboxBlock(ctx, subcommand, args) {
|
|
|
808
756
|
});
|
|
809
757
|
return resolveCodexNativeExecutionBlock({
|
|
810
758
|
config: ctx.config,
|
|
759
|
+
agentId: ctx.agentId,
|
|
811
760
|
sessionKey: ctx.sessionKey,
|
|
812
761
|
sessionId: ctx.sessionId,
|
|
813
762
|
surface: `/${["codex", subcommand].join(" ")}`
|
|
@@ -856,53 +805,54 @@ async function handleComputerUseCommand(deps, ctx, pluginConfig, args) {
|
|
|
856
805
|
async function bindConversation(deps, ctx, pluginConfig, args) {
|
|
857
806
|
const parsed = parseBindArgs(args);
|
|
858
807
|
if (parsed.help) return { text: "Usage: /codex bind [thread-id] [--cwd <path>] [--model <model>] [--provider <provider>]" };
|
|
859
|
-
if (!ctx.sessionFile) return { text: "Cannot bind Codex because this command did not include an OpenClaw session file." };
|
|
860
808
|
const scope = resolveCodexConversationControlScope(ctx);
|
|
861
809
|
const workspaceDir = parsed.cwd ?? deps.resolveCodexDefaultWorkspaceDir(pluginConfig);
|
|
862
|
-
const
|
|
863
|
-
|
|
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,
|
|
864
817
|
config: ctx.config
|
|
865
|
-
})
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
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,
|
|
870
830
|
workspaceDir,
|
|
831
|
+
agentId: scope.agentId,
|
|
871
832
|
agentDir: scope.agentDir,
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
request = await ctx.requestConversationBinding({
|
|
888
|
-
summary,
|
|
889
|
-
detachHint: "/codex detach",
|
|
890
|
-
data
|
|
891
|
-
});
|
|
892
|
-
} catch (error) {
|
|
893
|
-
await deps.clearCodexAppServerBinding(ctx.sessionFile);
|
|
894
|
-
throw error;
|
|
895
|
-
}
|
|
896
|
-
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
|
+
});
|
|
897
848
|
if (request.status === "pending") return request.reply;
|
|
898
|
-
|
|
899
|
-
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.` };
|
|
900
851
|
}
|
|
901
852
|
async function detachConversation(deps, ctx) {
|
|
902
853
|
const data = readCodexConversationBindingData(await ctx.getCurrentConversationBinding());
|
|
903
854
|
const detached = await ctx.detachConversationBinding();
|
|
904
|
-
if (data?.kind === "codex-app-server-session") await deps.
|
|
905
|
-
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" });
|
|
906
856
|
return detached.removed ? "Detached this conversation from Codex." : "No Codex conversation binding was attached.";
|
|
907
857
|
}
|
|
908
858
|
async function describeConversationBinding(deps, ctx) {
|
|
@@ -917,11 +867,9 @@ async function describeConversationBinding(deps, ctx) {
|
|
|
917
867
|
`- Workspace: ${formatCodexDisplayText(data.cwd ?? "unknown")}`,
|
|
918
868
|
"- Active run: not tracked"
|
|
919
869
|
].join("\n");
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
});
|
|
924
|
-
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);
|
|
925
873
|
return [
|
|
926
874
|
"Codex conversation binding:",
|
|
927
875
|
`- Thread: ${formatCodexDisplayText(threadBinding?.threadId ?? "unknown")}`,
|
|
@@ -930,13 +878,17 @@ async function describeConversationBinding(deps, ctx) {
|
|
|
930
878
|
`- Fast: ${isCodexFastServiceTier(threadBinding?.serviceTier) ? "on" : "off"}`,
|
|
931
879
|
`- Permissions: ${threadBinding ? formatPermissionsMode(threadBinding) : "default"}`,
|
|
932
880
|
`- Active run: ${formatCodexDisplayText(active ? active.turnId : "none")}`,
|
|
933
|
-
`-
|
|
881
|
+
`- Binding: ${formatCodexDisplayText(data.bindingId)}`
|
|
934
882
|
].join("\n");
|
|
935
883
|
}
|
|
936
|
-
async function buildThreads(deps, pluginConfig, filter) {
|
|
884
|
+
async function buildThreads(deps, ctx, pluginConfig, filter) {
|
|
885
|
+
const scope = await resolveCommandAppServerScope(deps, ctx);
|
|
937
886
|
return formatThreads(await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listThreads, {
|
|
938
887
|
limit: 10,
|
|
939
888
|
...filter.trim() ? { searchTerm: filter.trim() } : {}
|
|
889
|
+
}, {
|
|
890
|
+
config: ctx.config,
|
|
891
|
+
...scope
|
|
940
892
|
}));
|
|
941
893
|
}
|
|
942
894
|
async function buildCodexCliSessions(deps, args) {
|
|
@@ -954,20 +906,58 @@ async function resumeThread(deps, ctx, pluginConfig, args) {
|
|
|
954
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>";
|
|
955
907
|
if (parsed.host) return await bindCodexCliNodeSession(deps, ctx, parsed);
|
|
956
908
|
if (!normalizedThreadId || args.length !== 1) return "Usage: /codex resume <thread-id>";
|
|
957
|
-
if (!ctx.
|
|
958
|
-
const
|
|
959
|
-
|
|
960
|
-
|
|
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
|
|
961
916
|
});
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
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)}.`;
|
|
969
960
|
});
|
|
970
|
-
return `Attached this OpenClaw session to Codex thread ${formatCodexDisplayText(effectiveThreadId)}.`;
|
|
971
961
|
}
|
|
972
962
|
async function bindCodexCliNodeSession(deps, ctx, parsed) {
|
|
973
963
|
if (!parsed.threadId || !parsed.host || parsed.bindHere !== true) return "Usage: /codex resume <session-id> --host <node> --bind here";
|
|
@@ -978,9 +968,11 @@ async function bindCodexCliNodeSession(deps, ctx, parsed) {
|
|
|
978
968
|
if (!resolved.session) return `No Codex CLI session ${formatCodexDisplayText(parsed.threadId)} was found on ${formatCodexDisplayText(parsed.host)}.`;
|
|
979
969
|
const nodeId = resolved.node.nodeId;
|
|
980
970
|
if (!nodeId) return "Cannot bind Codex CLI session because the selected node did not include a node id.";
|
|
971
|
+
const scope = resolveCodexConversationControlScope(ctx);
|
|
981
972
|
const data = createCodexCliNodeConversationBindingData({
|
|
982
973
|
nodeId,
|
|
983
974
|
sessionId: parsed.threadId,
|
|
975
|
+
agentId: scope.agentId,
|
|
984
976
|
cwd: resolved.session?.cwd
|
|
985
977
|
});
|
|
986
978
|
const summary = `Codex CLI session ${formatCodexDisplayText(parsed.threadId)} on ${formatCodexDisplayText(nodeId)}`;
|
|
@@ -995,9 +987,10 @@ async function bindCodexCliNodeSession(deps, ctx, parsed) {
|
|
|
995
987
|
}
|
|
996
988
|
async function stopConversationTurn(deps, ctx, pluginConfig) {
|
|
997
989
|
const target = await resolveControlTarget(ctx);
|
|
998
|
-
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.";
|
|
999
991
|
return (await deps.stopCodexConversationTurn({
|
|
1000
|
-
|
|
992
|
+
identity: target.identity,
|
|
993
|
+
bindingStore: deps.bindingStore,
|
|
1001
994
|
pluginConfig,
|
|
1002
995
|
agentDir: target.agentDir,
|
|
1003
996
|
config: ctx.config
|
|
@@ -1005,11 +998,12 @@ async function stopConversationTurn(deps, ctx, pluginConfig) {
|
|
|
1005
998
|
}
|
|
1006
999
|
async function steerConversationTurn(deps, ctx, pluginConfig, message) {
|
|
1007
1000
|
const target = await resolveControlTarget(ctx);
|
|
1008
|
-
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.";
|
|
1009
1002
|
return (await deps.steerCodexConversationTurn({
|
|
1010
|
-
|
|
1011
|
-
|
|
1003
|
+
identity: target.identity,
|
|
1004
|
+
bindingStore: deps.bindingStore,
|
|
1012
1005
|
message,
|
|
1006
|
+
pluginConfig,
|
|
1013
1007
|
agentDir: target.agentDir,
|
|
1014
1008
|
config: ctx.config
|
|
1015
1009
|
})).message;
|
|
@@ -1017,77 +1011,100 @@ async function steerConversationTurn(deps, ctx, pluginConfig, message) {
|
|
|
1017
1011
|
async function setConversationModel(deps, ctx, pluginConfig, args) {
|
|
1018
1012
|
if (args.length > 1) return "Usage: /codex model <model>";
|
|
1019
1013
|
const target = await resolveControlTarget(ctx);
|
|
1020
|
-
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.";
|
|
1021
1015
|
const [model = ""] = args;
|
|
1022
1016
|
const normalized = model.trim();
|
|
1023
1017
|
if (!normalized) {
|
|
1024
|
-
const binding = await deps.
|
|
1025
|
-
agentDir: target.agentDir,
|
|
1026
|
-
config: ctx.config
|
|
1027
|
-
});
|
|
1018
|
+
const binding = await deps.bindingStore.read(target.identity);
|
|
1028
1019
|
return binding?.model ? `Codex model: ${formatCodexDisplayText(binding.model)}` : "Usage: /codex model <model>";
|
|
1029
1020
|
}
|
|
1030
1021
|
return await deps.setCodexConversationModel({
|
|
1031
|
-
|
|
1022
|
+
identity: target.identity,
|
|
1023
|
+
bindingStore: deps.bindingStore,
|
|
1032
1024
|
pluginConfig,
|
|
1033
1025
|
model: normalized,
|
|
1034
1026
|
agentDir: target.agentDir,
|
|
1035
1027
|
config: ctx.config
|
|
1036
1028
|
});
|
|
1037
1029
|
}
|
|
1038
|
-
async function setConversationFastMode(deps, ctx,
|
|
1030
|
+
async function setConversationFastMode(deps, ctx, args) {
|
|
1039
1031
|
if (args.length > 1) return "Usage: /codex fast [on|off|status]";
|
|
1040
1032
|
const target = await resolveControlTarget(ctx);
|
|
1041
|
-
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.";
|
|
1042
1034
|
const value = args[0];
|
|
1043
1035
|
const parsed = parseCodexFastModeArg(value);
|
|
1044
1036
|
if (value && parsed == null && value.trim().toLowerCase() !== "status") return "Usage: /codex fast [on|off|status]";
|
|
1045
1037
|
return await deps.setCodexConversationFastMode({
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
enabled: parsed
|
|
1049
|
-
agentDir: target.agentDir,
|
|
1050
|
-
config: ctx.config
|
|
1038
|
+
identity: target.identity,
|
|
1039
|
+
bindingStore: deps.bindingStore,
|
|
1040
|
+
enabled: parsed
|
|
1051
1041
|
});
|
|
1052
1042
|
}
|
|
1053
|
-
async function setConversationPermissions(deps, ctx,
|
|
1043
|
+
async function setConversationPermissions(deps, ctx, args) {
|
|
1054
1044
|
if (args.length > 1) return "Usage: /codex permissions [default|yolo|status]";
|
|
1045
|
+
const target = await resolveControlTarget(ctx);
|
|
1046
|
+
if (!target) return "Cannot set Codex permissions because this command did not include a stable binding identity.";
|
|
1055
1047
|
const value = args[0];
|
|
1056
1048
|
const parsed = parseCodexPermissionsModeArg(value);
|
|
1057
1049
|
if (value && !parsed && value.trim().toLowerCase() !== "status") return "Usage: /codex permissions [default|yolo|status]";
|
|
1058
|
-
const target = await resolveControlTarget(ctx);
|
|
1059
|
-
if (!target) return "Cannot set Codex permissions because this command did not include an OpenClaw session file.";
|
|
1060
1050
|
return await deps.setCodexConversationPermissions({
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
mode: parsed
|
|
1064
|
-
agentDir: target.agentDir,
|
|
1065
|
-
config: ctx.config
|
|
1051
|
+
identity: target.identity,
|
|
1052
|
+
bindingStore: deps.bindingStore,
|
|
1053
|
+
mode: parsed
|
|
1066
1054
|
});
|
|
1067
1055
|
}
|
|
1068
1056
|
async function resolveControlTarget(ctx) {
|
|
1069
1057
|
const data = readCodexConversationBindingData(await ctx.getCurrentConversationBinding());
|
|
1070
1058
|
const scope = resolveCodexConversationControlScope(ctx);
|
|
1071
1059
|
if (data?.kind === "codex-app-server-session") return {
|
|
1072
|
-
|
|
1073
|
-
|
|
1060
|
+
identity: conversationBindingIdentity(data.bindingId),
|
|
1061
|
+
agentId: data.agentId ?? scope.agentId,
|
|
1062
|
+
agentDir: data.agentDir ?? scope.agentDir,
|
|
1063
|
+
requestedAuthProfileId: data.start?.authProfileId
|
|
1074
1064
|
};
|
|
1075
|
-
return ctx.
|
|
1076
|
-
|
|
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,
|
|
1077
1073
|
agentDir: scope.agentDir
|
|
1078
1074
|
} : void 0;
|
|
1079
1075
|
}
|
|
1080
|
-
async function
|
|
1081
|
-
|
|
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
|
+
};
|
|
1082
1098
|
}
|
|
1083
1099
|
function resolveCodexConversationControlScope(ctx) {
|
|
1084
1100
|
const { sessionAgentId } = resolveSessionAgentIds({
|
|
1085
1101
|
sessionKey: ctx.sessionKey,
|
|
1102
|
+
agentId: ctx.agentId,
|
|
1086
1103
|
config: ctx.config
|
|
1087
1104
|
});
|
|
1088
1105
|
return {
|
|
1089
|
-
|
|
1090
|
-
sessionAgentId
|
|
1106
|
+
agentId: sessionAgentId,
|
|
1107
|
+
agentDir: resolveAgentDir(ctx.config, sessionAgentId)
|
|
1091
1108
|
};
|
|
1092
1109
|
}
|
|
1093
1110
|
async function handleCodexDiagnosticsFeedback(deps, ctx, pluginConfig, args, commandPrefix) {
|
|
@@ -1101,7 +1118,7 @@ async function handleCodexDiagnosticsFeedback(deps, ctx, pluginConfig, args, com
|
|
|
1101
1118
|
return await requestCodexDiagnosticsFeedbackApproval(deps, ctx, parsed.note, commandPrefix);
|
|
1102
1119
|
}
|
|
1103
1120
|
async function requestCodexDiagnosticsFeedbackApproval(deps, ctx, note, commandPrefix) {
|
|
1104
|
-
if (!await
|
|
1121
|
+
if (!await hasAnyCodexDiagnosticsIdentity(ctx)) return { text: "Cannot send Codex diagnostics because this command did not include a stable session identity." };
|
|
1105
1122
|
const targets = await resolveCodexDiagnosticsTargets(deps, ctx);
|
|
1106
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") };
|
|
1107
1124
|
const now = Date.now();
|
|
@@ -1157,7 +1174,7 @@ async function requestCodexDiagnosticsFeedbackApproval(deps, ctx, note, commandP
|
|
|
1157
1174
|
};
|
|
1158
1175
|
}
|
|
1159
1176
|
async function previewCodexDiagnosticsFeedbackApproval(deps, ctx, note) {
|
|
1160
|
-
if (!await
|
|
1177
|
+
if (!await hasAnyCodexDiagnosticsIdentity(ctx)) return "Cannot send Codex diagnostics because this command did not include a stable session identity.";
|
|
1161
1178
|
const targets = await resolveCodexDiagnosticsTargets(deps, ctx);
|
|
1162
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");
|
|
1163
1180
|
const cooldownMessage = readCodexDiagnosticsTargetsCooldownMessage(targets, ctx, Date.now(), { includeThreadId: false });
|
|
@@ -1181,10 +1198,10 @@ async function confirmCodexDiagnosticsFeedback(deps, ctx, pluginConfig, token) {
|
|
|
1181
1198
|
const scopeMismatch = readCodexDiagnosticsScopeMismatch(pending, ctx);
|
|
1182
1199
|
if (scopeMismatch) return scopeMismatch.confirmMessage;
|
|
1183
1200
|
deletePendingCodexDiagnosticsConfirmation(token);
|
|
1184
|
-
if (!pending.privateRouted && !await
|
|
1185
|
-
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);
|
|
1186
1203
|
if (!codexDiagnosticsTargetsMatch(pending.targets, currentTargets)) return "The Codex diagnostics sessions changed before confirmation. Run /diagnostics again for the current threads.";
|
|
1187
|
-
return await sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, pending.note ?? "", pending.
|
|
1204
|
+
return await sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, pending.note ?? "", currentTargets, { cooldownScope: pending.scopeKey });
|
|
1188
1205
|
}
|
|
1189
1206
|
function cancelCodexDiagnosticsFeedback(ctx, token) {
|
|
1190
1207
|
const pending = readPendingCodexDiagnosticsConfirmation(token, Date.now());
|
|
@@ -1202,15 +1219,15 @@ function cancelCodexDiagnosticsFeedback(ctx, token) {
|
|
|
1202
1219
|
].join("\n");
|
|
1203
1220
|
}
|
|
1204
1221
|
async function sendCodexDiagnosticsFeedbackForContext(deps, ctx, pluginConfig, note) {
|
|
1205
|
-
if (!await
|
|
1222
|
+
if (!await hasAnyCodexDiagnosticsIdentity(ctx)) return "Cannot send Codex diagnostics because this command did not include a stable session identity.";
|
|
1206
1223
|
const targets = await resolveCodexDiagnosticsTargets(deps, ctx);
|
|
1207
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");
|
|
1208
1225
|
return await sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, note, targets);
|
|
1209
1226
|
}
|
|
1210
|
-
async function sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, note, targets) {
|
|
1227
|
+
async function sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, note, targets, options = {}) {
|
|
1211
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");
|
|
1212
1229
|
const now = Date.now();
|
|
1213
|
-
const cooldownMessage = readCodexDiagnosticsTargetsCooldownMessage(targets, ctx, now);
|
|
1230
|
+
const cooldownMessage = readCodexDiagnosticsTargetsCooldownMessage(targets, ctx, now, { cooldownScope: options.cooldownScope });
|
|
1214
1231
|
if (cooldownMessage) return cooldownMessage;
|
|
1215
1232
|
const reason = normalizeDiagnosticsReason(note);
|
|
1216
1233
|
const sent = [];
|
|
@@ -1222,6 +1239,12 @@ async function sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, n
|
|
|
1222
1239
|
includeLogs: true,
|
|
1223
1240
|
tags: buildDiagnosticsTags(ctx),
|
|
1224
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 } : {}
|
|
1225
1248
|
});
|
|
1226
1249
|
if (!response.ok) {
|
|
1227
1250
|
failed.push({
|
|
@@ -1235,20 +1258,20 @@ async function sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, n
|
|
|
1235
1258
|
...target,
|
|
1236
1259
|
threadId: responseThreadId ?? target.threadId
|
|
1237
1260
|
});
|
|
1238
|
-
recordCodexDiagnosticsUpload(target.threadId, ctx, now);
|
|
1261
|
+
recordCodexDiagnosticsUpload(target.threadId, ctx, now, options.cooldownScope);
|
|
1239
1262
|
}
|
|
1240
1263
|
return formatCodexDiagnosticsUploadResult(sent, failed);
|
|
1241
1264
|
}
|
|
1242
|
-
async function
|
|
1243
|
-
if (await
|
|
1244
|
-
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));
|
|
1245
1268
|
}
|
|
1246
1269
|
async function resolveCodexDiagnosticsTargets(deps, ctx) {
|
|
1247
|
-
const
|
|
1270
|
+
const activeTarget = await resolveControlTarget(ctx);
|
|
1248
1271
|
const candidates = [];
|
|
1249
|
-
if (
|
|
1250
|
-
|
|
1251
|
-
|
|
1272
|
+
if (activeTarget) candidates.push({
|
|
1273
|
+
identity: activeTarget.identity,
|
|
1274
|
+
agentDir: activeTarget.agentDir,
|
|
1252
1275
|
sessionKey: ctx.sessionKey,
|
|
1253
1276
|
sessionId: ctx.sessionId,
|
|
1254
1277
|
channel: ctx.channel,
|
|
@@ -1258,10 +1281,17 @@ async function resolveCodexDiagnosticsTargets(deps, ctx) {
|
|
|
1258
1281
|
threadParentId: ctx.threadParentId
|
|
1259
1282
|
});
|
|
1260
1283
|
for (const session of ctx.diagnosticsSessions ?? []) {
|
|
1261
|
-
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
|
+
});
|
|
1262
1292
|
candidates.push({
|
|
1263
|
-
|
|
1264
|
-
|
|
1293
|
+
identity,
|
|
1294
|
+
agentDir: resolveAgentDir(ctx.config, identity.agentId),
|
|
1265
1295
|
sessionKey: session.sessionKey,
|
|
1266
1296
|
sessionId: session.sessionId,
|
|
1267
1297
|
channel: session.channel,
|
|
@@ -1271,38 +1301,50 @@ async function resolveCodexDiagnosticsTargets(deps, ctx) {
|
|
|
1271
1301
|
threadParentId: session.threadParentId
|
|
1272
1302
|
});
|
|
1273
1303
|
}
|
|
1274
|
-
const
|
|
1304
|
+
const seenBindingKeys = /* @__PURE__ */ new Set();
|
|
1275
1305
|
const seenThreadIds = /* @__PURE__ */ new Set();
|
|
1276
1306
|
const targets = [];
|
|
1277
1307
|
for (const candidate of candidates) {
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
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);
|
|
1281
1312
|
if (!binding?.threadId || seenThreadIds.has(binding.threadId)) continue;
|
|
1282
1313
|
seenThreadIds.add(binding.threadId);
|
|
1283
|
-
targets.push(
|
|
1284
|
-
...candidate,
|
|
1285
|
-
threadId: binding.threadId
|
|
1286
|
-
});
|
|
1314
|
+
targets.push(resolveCodexDiagnosticsTarget(candidate, binding, ctx.config));
|
|
1287
1315
|
}
|
|
1288
1316
|
return targets;
|
|
1289
1317
|
}
|
|
1290
|
-
async function resolvePendingCodexDiagnosticsTargets(deps, targets) {
|
|
1318
|
+
async function resolvePendingCodexDiagnosticsTargets(deps, targets, config) {
|
|
1291
1319
|
const resolved = [];
|
|
1292
1320
|
for (const target of targets) {
|
|
1293
|
-
const binding = await deps.
|
|
1321
|
+
const binding = await deps.bindingStore.read(target.identity);
|
|
1294
1322
|
if (!binding?.threadId) continue;
|
|
1295
|
-
resolved.push(
|
|
1296
|
-
...target,
|
|
1297
|
-
threadId: binding.threadId
|
|
1298
|
-
});
|
|
1323
|
+
resolved.push(resolveCodexDiagnosticsTarget(target, binding, config));
|
|
1299
1324
|
}
|
|
1300
1325
|
return resolved;
|
|
1301
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
|
+
}
|
|
1302
1339
|
function codexDiagnosticsTargetsMatch(expected, actual) {
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1305
|
-
|
|
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]);
|
|
1306
1348
|
}
|
|
1307
1349
|
function formatCodexDiagnosticsUploadResult(sent, failed) {
|
|
1308
1350
|
const lines = [];
|
|
@@ -1504,7 +1546,7 @@ function readCodexDiagnosticsTargetsCooldownMessage(targets, ctx, now, options =
|
|
|
1504
1546
|
return `Codex diagnostics were already sent for thread ${formatCodexThreadIdForDisplay(target.threadId)} recently. Try again in ${Math.ceil(cooldownMs / 1e3)}s.`;
|
|
1505
1547
|
}
|
|
1506
1548
|
}
|
|
1507
|
-
const scopeCooldownMs = readCodexDiagnosticsScopeCooldownMs(readCodexDiagnosticsCooldownScope(ctx), now);
|
|
1549
|
+
const scopeCooldownMs = readCodexDiagnosticsScopeCooldownMs(options.cooldownScope ?? readCodexDiagnosticsCooldownScope(ctx), now);
|
|
1508
1550
|
if (scopeCooldownMs > 0) return `Codex diagnostics were already sent for this account or channel recently. Try again in ${Math.ceil(scopeCooldownMs / 1e3)}s.`;
|
|
1509
1551
|
}
|
|
1510
1552
|
function readCodexDiagnosticsScopeCooldownMs(scope, now) {
|
|
@@ -1514,9 +1556,9 @@ function readCodexDiagnosticsScopeCooldownMs(scope, now) {
|
|
|
1514
1556
|
if (remainingMs === 0) lastCodexDiagnosticsUploadByScope.delete(scope);
|
|
1515
1557
|
return remainingMs;
|
|
1516
1558
|
}
|
|
1517
|
-
function recordCodexDiagnosticsUpload(threadId, ctx, now) {
|
|
1559
|
+
function recordCodexDiagnosticsUpload(threadId, ctx, now, cooldownScope) {
|
|
1518
1560
|
pruneCodexDiagnosticsCooldowns(now);
|
|
1519
|
-
recordBoundedCodexDiagnosticsCooldown(lastCodexDiagnosticsUploadByScope, readCodexDiagnosticsCooldownScope(ctx), CODEX_DIAGNOSTICS_COOLDOWN_MAX_SCOPES, now);
|
|
1561
|
+
recordBoundedCodexDiagnosticsCooldown(lastCodexDiagnosticsUploadByScope, cooldownScope ?? readCodexDiagnosticsCooldownScope(ctx), CODEX_DIAGNOSTICS_COOLDOWN_MAX_SCOPES, now);
|
|
1520
1562
|
recordBoundedCodexDiagnosticsCooldown(lastCodexDiagnosticsUploadByThread, threadId, CODEX_DIAGNOSTICS_COOLDOWN_MAX_THREADS, now);
|
|
1521
1563
|
}
|
|
1522
1564
|
function recordBoundedCodexDiagnosticsCooldown(map, key, maxSize, now) {
|
|
@@ -1564,24 +1606,17 @@ function normalizeCodexDiagnosticsScopeField(value) {
|
|
|
1564
1606
|
if (normalized.length <= CODEX_DIAGNOSTICS_SCOPE_FIELD_MAX_CHARS) return normalized;
|
|
1565
1607
|
return `sha256:${crypto.createHash("sha256").update(normalized).digest("hex")}`;
|
|
1566
1608
|
}
|
|
1567
|
-
async function startThreadAction(deps, ctx, pluginConfig,
|
|
1609
|
+
async function startThreadAction(deps, ctx, pluginConfig, kind, args) {
|
|
1610
|
+
const label = kind === "compact" ? "compaction" : "review";
|
|
1568
1611
|
if (args.length > 0) return `Usage: /codex ${label === "compaction" ? "compact" : label}`;
|
|
1569
1612
|
const target = await resolveControlTarget(ctx);
|
|
1570
|
-
if (!target) return `Cannot start Codex ${label} because this command did not include
|
|
1571
|
-
const binding = await deps.
|
|
1572
|
-
agentDir: target.agentDir,
|
|
1573
|
-
config: ctx.config
|
|
1574
|
-
});
|
|
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);
|
|
1575
1615
|
if (!binding?.threadId) return `No Codex thread is attached to this OpenClaw session yet.`;
|
|
1576
|
-
|
|
1616
|
+
await deps.codexControlRequest(pluginConfig, kind === "compact" ? CODEX_CONTROL_METHODS.compact : CODEX_CONTROL_METHODS.review, kind === "review" ? {
|
|
1577
1617
|
threadId: binding.threadId,
|
|
1578
1618
|
target: { type: "uncommittedChanges" }
|
|
1579
|
-
}, {
|
|
1580
|
-
agentDir: target.agentDir,
|
|
1581
|
-
authProfileId: binding.authProfileId,
|
|
1582
|
-
config: ctx.config
|
|
1583
|
-
});
|
|
1584
|
-
else await deps.codexControlRequest(pluginConfig, method, { threadId: binding.threadId }, {
|
|
1619
|
+
} : { threadId: binding.threadId }, {
|
|
1585
1620
|
agentDir: target.agentDir,
|
|
1586
1621
|
authProfileId: binding.authProfileId,
|
|
1587
1622
|
config: ctx.config
|