@openclaw/codex 2026.9.4 → 2026.9.5
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/assets/activity.svg +30 -0
- package/assets/icon.png +0 -0
- package/dist/.setup/account-usage-runtime-C2MOIvfq.mjs +75 -0
- package/dist/{apply-CvT8tW5i.js → .setup/apply-CJ0ZfmBG.mjs} +11 -11
- package/dist/.setup/async-questions-DdhOCwGi.mjs +27 -0
- package/dist/{attempt-client-cleanup-DKHzbwt5.js → .setup/attempt-client-cleanup-BpYKdTA0.mjs} +29 -14
- package/dist/{attempt-context-D44yeDII.js → .setup/attempt-context-VXVwTCfS.mjs} +5 -5
- package/dist/.setup/attempt-terminal-gZrxQ35N.mjs +5 -0
- package/dist/{auth-binding-CWlKVccJ.js → .setup/auth-binding-DhLWa_7K.mjs} +1 -1
- package/dist/{auth-cache-key-7orQae_j.js → .setup/auth-cache-key-QirmgiCo.mjs} +3 -3
- package/dist/{auth-profile-CwzO4_RG.js → .setup/auth-profile-WqZtZfXN.mjs} +1 -1
- package/dist/.setup/binding-connection-Bm38niE8.mjs +50 -0
- package/dist/.setup/binding-connection.runtime-CnRMC0ta.mjs +48 -0
- package/dist/{bounded-turn-C_Hneydj.js → .setup/bounded-turn-BtlZcivC.mjs} +16 -11
- package/dist/{build-state-BCmg9-5p.js → .setup/build-state-Xx9ybZqa.mjs} +1 -1
- package/dist/{capabilities-8vx68WLH.js → .setup/capabilities-_vXcZGSx.mjs} +1 -1
- package/dist/{client-B57KWPQx.js → .setup/client-vXs2929Q.mjs} +332 -163
- package/dist/{command-dispatch-BpduNGVf.js → .setup/command-dispatch-naHIZwwF.mjs} +3 -3
- package/dist/{command-formatters-lxlkNZgU.js → .setup/command-formatters-CGEkdawF.mjs} +2 -2
- package/dist/{command-handlers-tiL9iWj7.js → .setup/command-handlers-BtAJEW1X.mjs} +59 -42
- package/dist/{command-rpc-CR5EOEZX.js → .setup/command-rpc-DHFpn4le.mjs} +22 -13
- package/dist/{compact-B_H3AOd7.js → .setup/compact-C3i3qlzj.mjs} +28 -43
- package/dist/{config-oIORaQ5T.js → .setup/config-CedDWjM-.mjs} +5 -4
- package/dist/{config-options-CXMq1T-C.js → .setup/config-options-BsanJ8QG.mjs} +6 -418
- package/dist/{protocol-5bh1G-H7.js → .setup/config-parsing-CcB9iPoq.mjs} +9 -51
- package/dist/{config-runtime-D0zGNIHu.js → .setup/config-runtime-CeAhl_h4.mjs} +2 -2
- package/dist/.setup/config-security-BRQ2mgW9.mjs +465 -0
- package/dist/{conversation-binding-DsB4-O7U.js → .setup/conversation-binding-DhzrpTM6.mjs} +23 -21
- package/dist/{conversation-binding-data-BiodpIzq.js → .setup/conversation-binding-data-CrcyZk6O.mjs} +1 -1
- package/dist/{conversation-control-CtUu1b3m.js → .setup/conversation-control-DN53ns41.mjs} +17 -13
- package/dist/.setup/cyber-failover-DYwbmf1o.mjs +123 -0
- package/dist/.setup/cyber-failover-notice-e9G3J9-e.mjs +17 -0
- package/dist/{dynamic-tools-CmN5RAip.js → .setup/dynamic-tools-D16lOp67.mjs} +101 -55
- package/dist/{effective-mcp-catalog-Bd73bqA8.js → .setup/effective-mcp-catalog-K6kLCN8X.mjs} +3 -3
- package/dist/{ephemeral-turn-D78_VmzO.js → .setup/ephemeral-turn-R1gA8Sn6.mjs} +4 -5
- package/dist/{event-projector-assistant-message-BmBM4qBV.js → .setup/event-projector-assistant-message-DNUR1s0A.mjs} +6 -3
- package/dist/{event-projector-items-5VhsECCO.js → .setup/event-projector-items-DyTQ_XqL.mjs} +1 -1
- package/dist/{attempt-notifications-C9XufG6l.js → .setup/event-projector-usage-C7Ntzs-f.mjs} +102 -9
- package/dist/{harness-C7rRa7bm.js → .setup/harness-rxaaoN-o.mjs} +74 -20
- package/dist/{inference-context-Dr_mzvk4.js → .setup/inference-context-fulhZuz8.mjs} +1 -1
- package/dist/{inference-proxy-HGXKUIre.js → .setup/inference-proxy-CC15HuSi.mjs} +4 -4
- package/dist/{isolated-completion-DWVb5U2J.js → .setup/isolated-completion-B6srt48J.mjs} +7 -7
- package/dist/{launch-args-RXQwn8zV.js → .setup/launch-args-DnMak49W.mjs} +22 -1
- package/dist/{managed-binary-D22rNEcR.js → .setup/managed-binary-DP6Rib3P.mjs} +2 -2
- package/dist/{managed-thread-store-D1t5jh3G.js → .setup/managed-thread-store-BMqgThVL.mjs} +48 -14
- package/dist/{model-catalog-Dq2n8f_p.js → .setup/model-catalog-DkvUuBey.mjs} +35 -10
- package/dist/{models-PCKOim-h.js → .setup/models-BDFgm_G-.mjs} +3 -3
- package/dist/.setup/native-auth-BLZGqFjw.mjs +57 -0
- package/dist/{native-subagent-history-DjtPloaU.js → .setup/native-subagent-history-YXaL6dMd.mjs} +24 -29
- package/dist/.setup/native-subagent-monitor-B1hxzIOy.mjs +5031 -0
- package/dist/{native-subagent-task-ids-BzTdT0Uw.js → .setup/native-subagent-task-ids-DsuOonKv.mjs} +32 -8
- package/dist/{node-cli-sessions-DKaOmVUh.js → .setup/node-cli-sessions-Cl8b2ZF3.mjs} +1 -1
- package/dist/{node-exec-server.runtime-Cl2vapEi.js → .setup/node-exec-server.runtime-BaViBKNz.mjs} +3 -3
- package/dist/{plan-7uG-Fp36.js → .setup/plan-xD7WKYTM.mjs} +64 -48
- package/dist/{plugin-activation-BYQculil.js → .setup/plugin-activation-r8nq5Jzp.mjs} +3 -3
- package/dist/{plugin-app-cache-key-d0eKxy7b.js → .setup/plugin-app-cache-key-BOxKP7db.mjs} +29 -25
- package/dist/{plugin-approval-roundtrip-Bz3FT8W5.js → .setup/plugin-approval-roundtrip-CjfzafAZ.mjs} +1 -1
- package/dist/{plugin-inventory-oL4Na1ZG.js → .setup/plugin-inventory-DUle8DLz.mjs} +7 -6
- package/dist/{plugin-metadata-cache-ClPJpRio.js → .setup/plugin-metadata-cache-DsS5buNa.mjs} +29 -14
- package/dist/{provider-capabilities-BvCUL69A.js → .setup/provider-capabilities-B8J4Dy-O.mjs} +2 -2
- package/dist/{provider-MQ8PVkuE.js → .setup/provider-fFh_-VR3.mjs} +4 -4
- package/dist/{rate-limits-CBMmWBr9.js → .setup/rate-limits-DOEGYRNs.mjs} +90 -46
- package/dist/{request-B7hEkBGq.js → .setup/request-BioXJLYa.mjs} +113 -33
- package/dist/.setup/run-attempt-lifecycle-B7JbY4-F.mjs +71 -0
- package/dist/{run-attempt-CvsAAGa0.js → .setup/run-attempt-rlA07pUv.mjs} +580 -498
- package/dist/{runtime-artifact-BJ0rXBZ_.js → .setup/runtime-artifact-Dy38Z5iU.mjs} +2 -2
- package/dist/{sandbox-guard-BxgyoM8M.js → .setup/sandbox-guard-BBP-RbTf.mjs} +3 -3
- package/dist/{session-binding-CI8UuilT.js → .setup/session-binding-_0W_fHRr.mjs} +46 -64
- package/dist/{session-binding-record-Bcvslnhf.js → .setup/session-binding-record-TH3xA0zj.mjs} +181 -3
- package/dist/.setup/session-binding-sidecars-CKYEcfNn.mjs +543 -0
- package/dist/{session-catalog-adoption-tcZi1IfX.js → .setup/session-catalog-adoption-CR_whkEW.mjs} +6 -6
- package/dist/{session-catalog-archive-CwKg8Xz2.js → .setup/session-catalog-archive-CwZaPacf.mjs} +6 -6
- package/dist/.setup/session-catalog-events-jMcYfQyd.mjs +250 -0
- package/dist/.setup/session-catalog-fUdWDx-U.mjs +2464 -0
- package/dist/.setup/session-catalog-index-B141u15s.mjs +1070 -0
- package/dist/.setup/session-catalog-index-order-Cc5BzlQr.mjs +48 -0
- package/dist/.setup/session-catalog-index-state-BAuDztHN.mjs +180 -0
- package/dist/{thread-history-page-D0JIp48V.js → .setup/session-catalog-native-projection-ChV3cCtm.mjs} +206 -95
- package/dist/{session-catalog-node-adoption-B1tgDW-m.js → .setup/session-catalog-node-adoption-BVRsYOSh.mjs} +3 -3
- package/dist/{session-catalog-node-continue-Ci-DXyJP.js → .setup/session-catalog-node-continue-Dowv8osw.mjs} +7 -7
- package/dist/.setup/session-catalog-projection-C1Bj9Jl-.mjs +414 -0
- package/dist/{session-cli-DNN_jhGc.js → .setup/session-cli-DIj-IE-E.mjs} +4 -4
- package/dist/{session-history-Cz-tOOYj.js → .setup/session-history-C--SrLI8.mjs} +10 -5
- package/dist/{session-history-import-DwyijFI6.js → .setup/session-history-import-JHXT5Xfi.mjs} +4 -4
- package/dist/{session-history-read-cxPUxyVV.js → .setup/session-history-read-B3ZfSUbF.mjs} +2 -2
- package/dist/{session-history.worker-BeK1YOyT.js → .setup/session-history.worker-CpNj7H_P.mjs} +5 -6
- package/dist/{session-initialization-Ct0_Hzmg.js → .setup/session-initialization-DH1z6fXn.mjs} +2 -2
- package/dist/{session-permission-policy-BBmXA8Eg.js → .setup/session-permission-policy-DhurXY0l.mjs} +2 -2
- package/dist/{session-retirement-B2YfbAAT.js → .setup/session-retirement-CU87AOg9.mjs} +6 -6
- package/dist/{settled-turn-context-Du6gdUDd.js → .setup/settled-turn-context-fmwouT5I.mjs} +2 -2
- package/dist/{settled-turn-finalizer-Dn91stmc.js → .setup/settled-turn-finalizer-CiY2bLFK.mjs} +9 -9
- package/dist/{shared-client-CscigXXL.js → .setup/shared-client-BpTtuLQ5.mjs} +286 -145
- package/dist/{side-question-BGMJQf4U.js → .setup/side-question-DSCZyoBD.mjs} +64 -47
- package/dist/{source-CElXU0Oy.js → .setup/source-Bjp4UQij.mjs} +7 -6
- package/dist/{thread-archive-guard-DfpBr-Sz.js → .setup/thread-archive-guard-jmecf6OJ.mjs} +1 -1
- package/dist/.setup/thread-history-page-BTx-4xsg.mjs +83 -0
- package/dist/{thread-lifecycle-DXl1-I6b.js → .setup/thread-lifecycle-Cik28qWn.mjs} +256 -128
- package/dist/{thread-ownership-BsDRTsyp.js → .setup/thread-ownership-skomxnIt.mjs} +4 -4
- package/dist/{thread-policy-BuEQfE0g.js → .setup/thread-policy-Cjs4B8xf.mjs} +2 -2
- package/dist/{thread-resume-DsDOZpFg.js → .setup/thread-resume-BkNpj9Pz.mjs} +8 -5
- package/dist/.setup/timeout-DZF05XrR.mjs +63 -0
- package/dist/{transcript-history-projection-v17wXgdK.js → .setup/transcript-history-projection-DHNBDhCU.mjs} +8 -3
- package/dist/{transcript-mirror-C5CKgNy6.js → .setup/transcript-mirror-D4qp_leP.mjs} +15 -6
- package/dist/{transcript-mirror-attestation-DibE0krH.js → .setup/transcript-mirror-attestation-CzM-94NC.mjs} +4 -1
- package/dist/{transport-stdio-BGjHoYLr.js → .setup/transport-stdio-Cmx94Oiy.mjs} +149 -44
- package/dist/{upstream-session-fork-BZerMte_.js → .setup/upstream-session-fork-D3UKxgVD.mjs} +27 -26
- package/dist/{usage-tCk-mg_x.js → .setup/usage-BOgBEKso.mjs} +4 -4
- package/dist/.setup/usage-limit-error-C0ow7DOc.mjs +166 -0
- package/dist/{version-omm6IPI3.js → .setup/version-CoqzXPgK.mjs} +1 -1
- package/dist/{web-search-provider.runtime-B7bGbGWq.js → .setup/web-search-provider.runtime-CTGuJt6i.mjs} +3 -3
- package/dist/api.js +6 -6
- package/dist/cli-metadata.js +1 -1
- package/dist/doctor-contract-api.js +8 -528
- package/dist/harness.js +1 -1
- package/dist/index.js +123 -83
- package/dist/media-understanding-provider.js +3 -3
- package/dist/migration-provider-api.js +1 -1
- package/dist/native-tool-policy.js +12 -0
- package/dist/provider-discovery.js +18 -0
- package/dist/session-history-worker-runtime.js +23 -5
- package/dist/session-history.worker.js +2 -2
- package/dist/web-search-contract-api.js +1 -1
- package/openclaw.plugin.json +354 -69
- package/package.json +11 -8
- package/dist/binding-connection-D3udKbIE.js +0 -60
- package/dist/event-projector-usage-BfXmpOec.js +0 -102
- package/dist/native-subagent-history-owner-Pcp7JuVF.js +0 -34
- package/dist/native-subagent-monitor-fQ7eIXEf.js +0 -2978
- package/dist/rpc-error-ttN_QjEm.js +0 -30
- package/dist/session-catalog-ct-AH01I.js +0 -1477
- package/dist/session-catalog-home-id-CwhLdC03.js +0 -18
- package/dist/session-catalog-provenance-D-dIfRAH.js +0 -96
- package/dist/timeout-BVw8aaM1.js +0 -33
- package/dist/{auth-profile-selection-DFu9e167.js → .setup/auth-profile-selection-DFu9e167.mjs} +0 -0
- package/dist/{config-utils-DujwEnhg.js → .setup/config-utils-DujwEnhg.mjs} +0 -0
- package/dist/{context-compaction-activity-CMla5FPJ.js → .setup/context-compaction-activity-CMla5FPJ.mjs} +0 -0
- package/dist/{elicitation-input-DPoK8F7w.js → .setup/elicitation-input-DPoK8F7w.mjs} +0 -0
- package/dist/{history-rejection-B4nOXNXF.js → .setup/history-rejection-B4nOXNXF.mjs} +0 -0
- package/dist/{image-payload-sanitizer-tupAr-2o.js → .setup/image-payload-sanitizer-tupAr-2o.mjs} +0 -0
- package/dist/{incognito-session-uhrBF6wJ.js → .setup/incognito-session-uhrBF6wJ.mjs} +0 -0
- package/dist/{local-runtime-attribution-B_2mnqhb.js → .setup/local-runtime-attribution-B_2mnqhb.mjs} +0 -0
- package/dist/{native-execution-policy-umd0tgh4.js → .setup/native-execution-policy-YBMHSzC4.mjs} +1 -1
- /package/dist/{plugin-marketplace-discovery-P-Vndfqq.js → .setup/plugin-marketplace-discovery-P-Vndfqq.mjs} +0 -0
- /package/dist/{rolldown-runtime-8H4AJuhK.js → .setup/rolldown-runtime-8H4AJuhK.mjs} +0 -0
- /package/dist/{session-binding-meta-B7aEMU7g.js → .setup/session-binding-meta-B7aEMU7g.mjs} +0 -0
- /package/dist/{session-upstream-marker-D15C9NHp.js → .setup/session-upstream-marker-D15C9NHp.mjs} +0 -0
- /package/dist/{upstream-prompt-provenance-LphB8slG.js → .setup/upstream-prompt-provenance-LphB8slG.mjs} +0 -0
- /package/dist/{web-search-provider.shared-BrZmlqyR.js → .setup/web-search-provider.shared-BrZmlqyR.mjs} +0 -0
|
@@ -0,0 +1,2464 @@
|
|
|
1
|
+
import { p as sessionBindingIdentity } from "./session-binding-record-TH3xA0zj.mjs";
|
|
2
|
+
import { _ as resolveCodexAppServerUserHomeDir, f as assertCodexThreadForkParams, g as resolveCodexAppServerLocalHomeDir, h as resolveCodexAppServerHomeDir } from "./config-security-BRQ2mgW9.mjs";
|
|
3
|
+
import { d as readCodexPluginConfig } from "./config-parsing-CcB9iPoq.mjs";
|
|
4
|
+
import { A as parseTranscriptPage, B as unwrapNodeInvokePayload, E as isInteractiveThreadSource, I as requireOnlyKeys, M as readControlCursor, N as readGatewayParams, O as parseCatalogPage, P as readPageParams, S as catalogError, T as filterCatalogPageByTitle, _ as MAX_CURSOR_LENGTH, b as NODE_INVOKE_TIMEOUT_MS, d as CODEX_APP_SERVER_THREADS_CAPABILITY, f as CODEX_APP_SERVER_THREADS_LIST_COMMAND, g as CatalogParamsError, h as CODEX_LOCAL_SESSION_HOST_ID, j as readBoundedOptionalString, k as parseJsonParams, m as CODEX_CATALOG_TRANSCRIPT_READ_COMMAND, p as CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND, y as MAX_TRANSCRIPT_PAGE_BYTES } from "./session-catalog-native-projection-ChV3cCtm.mjs";
|
|
5
|
+
import { a as isCodexThreadReadMissingError, i as CodexAppServerRpcError, n as withTimeout } from "./timeout-DZF05XrR.mjs";
|
|
6
|
+
import { t as CODEX_CONTROL_METHODS } from "./capabilities-_vXcZGSx.mjs";
|
|
7
|
+
import { r as compareCodexCatalogRows } from "./session-catalog-index-order-Cc5BzlQr.mjs";
|
|
8
|
+
import { n as readLegacyCodexHistoryPage, t as readCodexThreadHistoryPage } from "./thread-history-page-BTx-4xsg.mjs";
|
|
9
|
+
import { i as projectCodexUserItemText } from "./transcript-history-projection-DHNBDhCU.mjs";
|
|
10
|
+
import { r as codexUpstreamContinueResult } from "./session-upstream-marker-D15C9NHp.mjs";
|
|
11
|
+
import { t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-BOxKP7db.mjs";
|
|
12
|
+
import { c as readCodexSessionMeta, o as codexCatalogHomeIdFromCanonicalPath, t as codexCatalogResidentHomeKey } from "./session-catalog-events-jMcYfQyd.mjs";
|
|
13
|
+
import { a as setCodexCatalogConnectionHomeResolver } from "./binding-connection-Bm38niE8.mjs";
|
|
14
|
+
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
15
|
+
import { randomUUID } from "node:crypto";
|
|
16
|
+
import { embeddedAgentLog, resolveDefaultAgentDir } from "openclaw/plugin-sdk/agent-harness-registration";
|
|
17
|
+
import { listAgentIds, resolveAgentDir, resolveDefaultAgentId, resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import fs from "node:fs/promises";
|
|
21
|
+
import { scheduler } from "node:timers/promises";
|
|
22
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
23
|
+
import { isMainThread, threadId } from "node:worker_threads";
|
|
24
|
+
import { areDiagnosticsEnabledForProcess, createSubsystemLogger } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
25
|
+
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
|
26
|
+
import { publishSessionCatalogHost, sessionCatalogPaging } from "openclaw/plugin-sdk/session-catalog-paging";
|
|
27
|
+
import { decodeNodePtyResumeParams, decodeNodePtyStartParams, resolveNodeHostExecutable, runNodePtyCommand } from "openclaw/plugin-sdk/node-host";
|
|
28
|
+
//#region extensions/codex/src/session-catalog-create.ts
|
|
29
|
+
const CODEX_AGENT_RUNTIME_ID = "codex";
|
|
30
|
+
const CODEX_CATALOG_DEFAULT_MODEL_REF = "openai/gpt-6-astra";
|
|
31
|
+
function resolveCodexCatalogCreateSession(modelConfig, config, requestedAgentId) {
|
|
32
|
+
if (!config) return;
|
|
33
|
+
const agentId = requestedAgentId ?? resolveDefaultAgentId(config);
|
|
34
|
+
const defaultModel = modelConfig.resolveDefaultModelForAgent({
|
|
35
|
+
cfg: config,
|
|
36
|
+
agentId
|
|
37
|
+
});
|
|
38
|
+
const modelRef = defaultModel.provider === "openai" ? `${defaultModel.provider}/${defaultModel.model}` : CODEX_CATALOG_DEFAULT_MODEL_REF;
|
|
39
|
+
const allowed = modelConfig.resolveAllowedModelRef({
|
|
40
|
+
cfg: config,
|
|
41
|
+
catalog: [],
|
|
42
|
+
raw: modelRef,
|
|
43
|
+
defaultProvider: defaultModel.provider,
|
|
44
|
+
defaultModel: defaultModel.model,
|
|
45
|
+
agentId
|
|
46
|
+
});
|
|
47
|
+
return "error" in allowed ? void 0 : {
|
|
48
|
+
model: allowed.key,
|
|
49
|
+
agentRuntime: CODEX_AGENT_RUNTIME_ID
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region extensions/codex/src/app-server/request-observation.ts
|
|
54
|
+
const CODEX_REQUEST_WAITER_OUTCOMES = [
|
|
55
|
+
"resolved",
|
|
56
|
+
"native-error",
|
|
57
|
+
"timed-out",
|
|
58
|
+
"aborted",
|
|
59
|
+
"authority-rejected",
|
|
60
|
+
"local-failed",
|
|
61
|
+
"client-closed"
|
|
62
|
+
];
|
|
63
|
+
const CODEX_REQUEST_WIRE_OUTCOMES = [
|
|
64
|
+
"retained-pending",
|
|
65
|
+
"native-ok",
|
|
66
|
+
"native-error",
|
|
67
|
+
"ingress-rejected",
|
|
68
|
+
"correlation-closed",
|
|
69
|
+
"not-written"
|
|
70
|
+
];
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region extensions/codex/src/session-catalog-diagnostics.ts
|
|
73
|
+
const log = createSubsystemLogger("gateway/session-catalog");
|
|
74
|
+
const listScope = new AsyncLocalStorage();
|
|
75
|
+
let epoch;
|
|
76
|
+
let sequence = 0;
|
|
77
|
+
let active = 0;
|
|
78
|
+
let windowStart = 0;
|
|
79
|
+
let emitted = 0;
|
|
80
|
+
let omitted = 0;
|
|
81
|
+
const WAITER_OUTCOMES = new Set(CODEX_REQUEST_WAITER_OUTCOMES);
|
|
82
|
+
const WIRE_OUTCOMES = new Set(CODEX_REQUEST_WIRE_OUTCOMES);
|
|
83
|
+
function controlWaiterTuple(controlCallOrdinal, summary) {
|
|
84
|
+
const ordinals = [
|
|
85
|
+
controlCallOrdinal,
|
|
86
|
+
summary.overloadAttemptOrdinal,
|
|
87
|
+
summary.rpcId,
|
|
88
|
+
summary.waiterOrdinal
|
|
89
|
+
];
|
|
90
|
+
const times = [
|
|
91
|
+
summary.attemptCreatedAtMs,
|
|
92
|
+
summary.waiterAttachedAtMs,
|
|
93
|
+
summary.waiterSettledAtMs
|
|
94
|
+
];
|
|
95
|
+
const optionalTimes = [summary.firstPossibleWriteAtMs, summary.wireObservedAtMs];
|
|
96
|
+
const validTime = (value) => Number.isFinite(value) && value >= 0 && Number.isSafeInteger(Math.round(value));
|
|
97
|
+
if (!/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(summary.clientInstanceId) || !ordinals.every((value) => Number.isSafeInteger(value) && value > 0) || !times.every(validTime) || !optionalTimes.every((value) => value === null || validTime(value)) || summary.disposition !== "new" && summary.disposition !== "joined" || !WAITER_OUTCOMES.has(summary.waiterOutcome) || !WIRE_OUTCOMES.has(summary.wireOutcomeAtWaiterSettlement)) return;
|
|
98
|
+
return [
|
|
99
|
+
controlCallOrdinal,
|
|
100
|
+
summary.overloadAttemptOrdinal,
|
|
101
|
+
summary.clientInstanceId,
|
|
102
|
+
summary.rpcId,
|
|
103
|
+
summary.waiterOrdinal,
|
|
104
|
+
summary.disposition,
|
|
105
|
+
Math.round(summary.attemptCreatedAtMs),
|
|
106
|
+
summary.firstPossibleWriteAtMs === null ? null : Math.round(summary.firstPossibleWriteAtMs),
|
|
107
|
+
Math.round(summary.waiterAttachedAtMs),
|
|
108
|
+
Math.round(summary.waiterSettledAtMs),
|
|
109
|
+
summary.waiterOutcome,
|
|
110
|
+
summary.wireOutcomeAtWaiterSettlement,
|
|
111
|
+
summary.wireObservedAtMs === null ? null : Math.round(summary.wireObservedAtMs)
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
function fitsMetadata(metadata) {
|
|
115
|
+
return Object.keys(metadata).length <= 28 && Buffer.byteLength(JSON.stringify(metadata)) <= 2048;
|
|
116
|
+
}
|
|
117
|
+
function withControlWaiters(metadata, fields) {
|
|
118
|
+
if (!("origin" in fields) || !fields.controlWaitersV1) return metadata;
|
|
119
|
+
const kept = fields.controlWaitersV1.slice();
|
|
120
|
+
let count = fields.controlWaitersOmitted ?? 0;
|
|
121
|
+
while (true) {
|
|
122
|
+
const complete = {
|
|
123
|
+
...metadata,
|
|
124
|
+
controlWaitersV1: JSON.stringify(kept),
|
|
125
|
+
controlWaitersOmitted: count
|
|
126
|
+
};
|
|
127
|
+
if (fitsMetadata(complete)) return complete;
|
|
128
|
+
if (kept.length === 0) {
|
|
129
|
+
const countOnly = {
|
|
130
|
+
...metadata,
|
|
131
|
+
controlWaitersOmitted: count
|
|
132
|
+
};
|
|
133
|
+
return fitsMetadata(countOnly) ? countOnly : metadata;
|
|
134
|
+
}
|
|
135
|
+
kept.splice(kept.length > 2 ? kept.length - 2 : 0, 1);
|
|
136
|
+
count = Math.min(Number.MAX_SAFE_INTEGER, count + 1);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const CONTROL_PHASE_FIELDS = {
|
|
140
|
+
"load-control": "controlLoadMs",
|
|
141
|
+
prepare: "controlPrepareMs",
|
|
142
|
+
"acquire-client": "controlAcquireClientMs",
|
|
143
|
+
"client-request": "controlClientRequestMs",
|
|
144
|
+
"release-client": "controlReleaseClientMs"
|
|
145
|
+
};
|
|
146
|
+
function enabled() {
|
|
147
|
+
return areDiagnosticsEnabledForProcess() && log.isEnabled("warn");
|
|
148
|
+
}
|
|
149
|
+
function start(kind, fields) {
|
|
150
|
+
if (!enabled()) return;
|
|
151
|
+
if (active >= 64) {
|
|
152
|
+
omitted = Math.min(Number.MAX_SAFE_INTEGER, omitted + 1);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
active++;
|
|
156
|
+
epoch ??= randomUUID();
|
|
157
|
+
const started = performance.now();
|
|
158
|
+
const observation = {
|
|
159
|
+
operationId: String(++sequence),
|
|
160
|
+
fields,
|
|
161
|
+
closed: false,
|
|
162
|
+
finish(outcome) {
|
|
163
|
+
if (observation.closed) return;
|
|
164
|
+
observation.closed = true;
|
|
165
|
+
active--;
|
|
166
|
+
const elapsedMs = performance.now() - started;
|
|
167
|
+
try {
|
|
168
|
+
if (elapsedMs < 1e3 || !enabled()) return;
|
|
169
|
+
if (performance.now() - windowStart >= 6e4) {
|
|
170
|
+
windowStart = performance.now();
|
|
171
|
+
emitted = 0;
|
|
172
|
+
}
|
|
173
|
+
if (emitted >= 60) {
|
|
174
|
+
omitted = Math.min(Number.MAX_SAFE_INTEGER, omitted + 1);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const metadata = {
|
|
178
|
+
diagnosticEpoch: epoch,
|
|
179
|
+
operationId: observation.operationId,
|
|
180
|
+
pid: process.pid,
|
|
181
|
+
threadId,
|
|
182
|
+
isMainThread,
|
|
183
|
+
elapsedMs: Math.round(elapsedMs),
|
|
184
|
+
outcome,
|
|
185
|
+
omittedObservations: omitted,
|
|
186
|
+
...Object.fromEntries(Object.entries(fields).filter(([key, value]) => value !== void 0 && key !== "controlWaitersV1" && key !== "controlWaitersOmitted").map(([key, value]) => [key, typeof value === "number" ? Math.round(value) : value]))
|
|
187
|
+
};
|
|
188
|
+
if (!fitsMetadata(metadata)) {
|
|
189
|
+
omitted = Math.min(Number.MAX_SAFE_INTEGER, omitted + 1);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
emitted++;
|
|
193
|
+
log.warn(`slow Codex catalog ${kind}`, withControlWaiters(metadata, fields));
|
|
194
|
+
omitted = 0;
|
|
195
|
+
} catch {}
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
return observation;
|
|
199
|
+
}
|
|
200
|
+
function currentCodexCatalogListDiagnostics() {
|
|
201
|
+
const observation = listScope.getStore();
|
|
202
|
+
return observation?.closed ? void 0 : observation;
|
|
203
|
+
}
|
|
204
|
+
/** One logical list scope survives admission pauses; finishing drops its captured context. */
|
|
205
|
+
function createCodexCatalogListScope() {
|
|
206
|
+
const observation = start("list phases", {
|
|
207
|
+
controlPageCalls: 0,
|
|
208
|
+
exclusionMarkCalls: 0,
|
|
209
|
+
adoptionCalls: 0
|
|
210
|
+
});
|
|
211
|
+
let captured = listScope.run(observation, () => AsyncLocalStorage.snapshot());
|
|
212
|
+
return {
|
|
213
|
+
run(run) {
|
|
214
|
+
if (!captured) throw new Error("Codex catalog diagnostic scope is closed");
|
|
215
|
+
return captured(run);
|
|
216
|
+
},
|
|
217
|
+
finish(outcome) {
|
|
218
|
+
const finishInScope = captured;
|
|
219
|
+
captured = void 0;
|
|
220
|
+
finishInScope?.(() => observation?.finish(outcome));
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function startCodexCatalogPageDiagnostics(origin) {
|
|
225
|
+
return start("page producer", {
|
|
226
|
+
origin,
|
|
227
|
+
listOperationId: currentCodexCatalogListDiagnostics()?.operationId,
|
|
228
|
+
controlRequestCalls: 0,
|
|
229
|
+
provenanceChecks: 0,
|
|
230
|
+
provenanceCacheHits: 0,
|
|
231
|
+
provenanceReadCalls: 0
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
function startCodexCatalogControlRequestDiagnostics(page) {
|
|
235
|
+
if (!page) return;
|
|
236
|
+
let state = "active";
|
|
237
|
+
const controlCallOrdinal = page.fields.controlRequestCalls;
|
|
238
|
+
let phase = "load-control";
|
|
239
|
+
let phaseStarted = performance.now();
|
|
240
|
+
const finishPhase = () => {
|
|
241
|
+
const now = performance.now();
|
|
242
|
+
const field = CONTROL_PHASE_FIELDS[phase];
|
|
243
|
+
page.fields[field] = (page.fields[field] ?? 0) + (now - phaseStarted);
|
|
244
|
+
phaseStarted = now;
|
|
245
|
+
};
|
|
246
|
+
const observation = {
|
|
247
|
+
attemptWaiterFinished(summary) {
|
|
248
|
+
if (state === "closed" || page.closed) return;
|
|
249
|
+
const kept = page.fields.controlWaitersV1 ??= [];
|
|
250
|
+
page.fields.controlWaitersOmitted ??= 0;
|
|
251
|
+
const tuple = controlWaiterTuple(controlCallOrdinal, summary);
|
|
252
|
+
if (!tuple) {
|
|
253
|
+
page.fields.controlWaitersOmitted = Math.min(Number.MAX_SAFE_INTEGER, page.fields.controlWaitersOmitted + 1);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (kept.length === 4) {
|
|
257
|
+
kept.splice(2, 1);
|
|
258
|
+
page.fields.controlWaitersOmitted = Math.min(Number.MAX_SAFE_INTEGER, page.fields.controlWaitersOmitted + 1);
|
|
259
|
+
}
|
|
260
|
+
kept.push(tuple);
|
|
261
|
+
},
|
|
262
|
+
phase(next) {
|
|
263
|
+
if (state === "active" && !page.closed) {
|
|
264
|
+
finishPhase();
|
|
265
|
+
phase = next;
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
failed(failure) {
|
|
269
|
+
if (state === "active" && !page.closed) {
|
|
270
|
+
finishPhase();
|
|
271
|
+
state = "failed";
|
|
272
|
+
page.fields.controlFailurePhase = failure.phase;
|
|
273
|
+
page.fields.controlFailureCategory = failure.category;
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
rejected() {
|
|
277
|
+
observation.failed({
|
|
278
|
+
phase,
|
|
279
|
+
category: "other"
|
|
280
|
+
});
|
|
281
|
+
},
|
|
282
|
+
close() {
|
|
283
|
+
if (state === "active" && !page.closed) finishPhase();
|
|
284
|
+
state = "closed";
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
return observation;
|
|
288
|
+
}
|
|
289
|
+
//#endregion
|
|
290
|
+
//#region extensions/codex/src/session-catalog-availability.ts
|
|
291
|
+
var CodexCatalogLoadingError = class extends Error {
|
|
292
|
+
constructor() {
|
|
293
|
+
super("Codex session catalog is still loading. Retry shortly.");
|
|
294
|
+
this.code = "APP_SERVER_UNAVAILABLE";
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
/** One generation promise wakes cold callers; native pages remain owned by the index. */
|
|
298
|
+
var CodexCatalogAvailability = class {
|
|
299
|
+
constructor() {
|
|
300
|
+
this.complete = false;
|
|
301
|
+
this.changed = createDeferred();
|
|
302
|
+
}
|
|
303
|
+
begin() {
|
|
304
|
+
this.failure = void 0;
|
|
305
|
+
}
|
|
306
|
+
publish(frontier, complete = false) {
|
|
307
|
+
if (this.complete) return;
|
|
308
|
+
if (frontier && (!this.frontier || compareCodexCatalogRows(frontier, this.frontier) > 0)) {
|
|
309
|
+
const { threadId, updatedAt, recencyAt, sourceOrder } = frontier;
|
|
310
|
+
this.frontier = {
|
|
311
|
+
threadId,
|
|
312
|
+
updatedAt,
|
|
313
|
+
recencyAt,
|
|
314
|
+
sourceOrder
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
this.complete = complete;
|
|
318
|
+
this.notify();
|
|
319
|
+
}
|
|
320
|
+
fail(error) {
|
|
321
|
+
this.failure = { error };
|
|
322
|
+
this.notify();
|
|
323
|
+
}
|
|
324
|
+
notify() {
|
|
325
|
+
const previous = this.changed;
|
|
326
|
+
this.changed = createDeferred();
|
|
327
|
+
previous.resolve();
|
|
328
|
+
}
|
|
329
|
+
async until(promise, deadline) {
|
|
330
|
+
const remaining = deadline - performance.now();
|
|
331
|
+
if (remaining <= 0) throw new CodexCatalogLoadingError();
|
|
332
|
+
return await withTimeout(promise, remaining, "Codex session catalog is still loading", () => new CodexCatalogLoadingError());
|
|
333
|
+
}
|
|
334
|
+
async next(deadline) {
|
|
335
|
+
if (this.failure) throw this.failure.error;
|
|
336
|
+
await this.until(this.changed.promise, deadline);
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
//#endregion
|
|
340
|
+
//#region extensions/codex/src/session-catalog-node-lookup.ts
|
|
341
|
+
async function lookupNodeCodexCatalogRecord(params) {
|
|
342
|
+
const deadline = performance.now() + NODE_INVOKE_TIMEOUT_MS;
|
|
343
|
+
const unverified = () => new CatalogParamsError("Codex session eligibility could not be verified");
|
|
344
|
+
const remaining = () => {
|
|
345
|
+
const timeoutMs = Math.ceil(deadline - performance.now());
|
|
346
|
+
if (timeoutMs <= 0) throw unverified();
|
|
347
|
+
return timeoutMs;
|
|
348
|
+
};
|
|
349
|
+
let cursor;
|
|
350
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
351
|
+
for (;;) {
|
|
352
|
+
const timeoutMs = remaining();
|
|
353
|
+
const raw = await withTimeout(params.runtime.nodes.invoke({
|
|
354
|
+
nodeId: params.nodeId,
|
|
355
|
+
command: CODEX_APP_SERVER_THREADS_LIST_COMMAND,
|
|
356
|
+
params: {
|
|
357
|
+
agentId: params.agentId,
|
|
358
|
+
limit: 100,
|
|
359
|
+
...cursor ? { cursor } : {}
|
|
360
|
+
},
|
|
361
|
+
timeoutMs,
|
|
362
|
+
scopes: ["operator.write"]
|
|
363
|
+
}), timeoutMs, "Codex session eligibility could not be verified", unverified);
|
|
364
|
+
remaining();
|
|
365
|
+
const page = parseCatalogPage(unwrapNodeInvokePayload(raw));
|
|
366
|
+
const record = page.sessions.find((candidate) => candidate.threadId === params.threadId);
|
|
367
|
+
if (record) return {
|
|
368
|
+
kind: "found",
|
|
369
|
+
record
|
|
370
|
+
};
|
|
371
|
+
const nextCursor = page.nextCursor?.trim();
|
|
372
|
+
if (!nextCursor) break;
|
|
373
|
+
if (seenCursors.has(nextCursor)) return { kind: "cursor-cycle" };
|
|
374
|
+
seenCursors.add(nextCursor);
|
|
375
|
+
if (seenCursors.size > 2e4) {
|
|
376
|
+
const oldest = seenCursors.values().next().value;
|
|
377
|
+
if (oldest !== void 0) seenCursors.delete(oldest);
|
|
378
|
+
}
|
|
379
|
+
cursor = nextCursor;
|
|
380
|
+
}
|
|
381
|
+
return { kind: "missing" };
|
|
382
|
+
}
|
|
383
|
+
//#endregion
|
|
384
|
+
//#region extensions/codex/src/session-catalog-terminal.ts
|
|
385
|
+
const CODEX_TERMINAL_RESUME_COMMAND = "codex.terminal.resume.v1";
|
|
386
|
+
const CODEX_TERMINAL_START_COMMAND = "codex.terminal.start.v1";
|
|
387
|
+
function createCodexTerminalStartNodeHostCommand() {
|
|
388
|
+
return {
|
|
389
|
+
command: CODEX_TERMINAL_START_COMMAND,
|
|
390
|
+
cap: CODEX_APP_SERVER_THREADS_CAPABILITY,
|
|
391
|
+
dangerous: false,
|
|
392
|
+
duplex: true,
|
|
393
|
+
isAvailable: ({ env }) => Boolean(resolveNodeHostExecutable("codex", {
|
|
394
|
+
env,
|
|
395
|
+
strategy: "direct"
|
|
396
|
+
})),
|
|
397
|
+
handle: async (paramsJSON, io) => {
|
|
398
|
+
if (!io) throw new Error("Codex terminal command requires duplex transport");
|
|
399
|
+
const params = decodeNodePtyStartParams(paramsJSON);
|
|
400
|
+
const resolution = resolveNodeHostExecutable("codex", { strategy: "direct" });
|
|
401
|
+
if (!resolution) throw new Error("Codex CLI is unavailable; install codex on this node and reconnect");
|
|
402
|
+
return JSON.stringify(await runNodePtyCommand({
|
|
403
|
+
file: resolution.executable,
|
|
404
|
+
args: params.initialMessage !== void 0 ? ["--", params.initialMessage] : [],
|
|
405
|
+
cwd: params.cwd,
|
|
406
|
+
requiredCwd: true,
|
|
407
|
+
cols: params.cols,
|
|
408
|
+
rows: params.rows
|
|
409
|
+
}, io));
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
function resolveCodexCatalogTerminalHome(sources) {
|
|
414
|
+
sources.source?.assertCurrent();
|
|
415
|
+
const runtimeConfig = sources.getRuntimeConfig();
|
|
416
|
+
if (!runtimeConfig) throw new Error("OpenClaw runtime config is unavailable");
|
|
417
|
+
const agentDir = sources.source?.agentDir ?? (sources.agentId ? resolveAgentDir(runtimeConfig, sources.agentId) : resolveDefaultAgentDir(runtimeConfig));
|
|
418
|
+
const startOptions = sources.source?.appServer.start ?? sources.resolveRuntimeOptions({ pluginConfig: sources.getPluginConfig() }).start;
|
|
419
|
+
return resolveCodexAppServerLocalHomeDir(startOptions, agentDir);
|
|
420
|
+
}
|
|
421
|
+
function resolveLocalCodexTerminalExecutable(env = process.env) {
|
|
422
|
+
return resolveLocalCodexTerminalResolution(env)?.executable;
|
|
423
|
+
}
|
|
424
|
+
function resolveLocalCodexTerminalResolution(env = process.env) {
|
|
425
|
+
return resolveNodeHostExecutable("codex", {
|
|
426
|
+
env,
|
|
427
|
+
pathEnv: env.PATH ?? env.Path ?? "",
|
|
428
|
+
strategy: "fallback"
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
function codexNodeTerminalCapability(node) {
|
|
432
|
+
const commands = node.invocableCommands ?? node.commands;
|
|
433
|
+
return {
|
|
434
|
+
canOpenTerminalCodex: node.connected === true && commands?.includes("codex.terminal.resume.v1") === true,
|
|
435
|
+
canStartTerminal: node.connected === true && node.invocableCommands?.includes("codex.terminal.start.v1") === true
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function createCodexTerminalNodeHostCommand(bindRequest) {
|
|
439
|
+
return {
|
|
440
|
+
command: CODEX_TERMINAL_RESUME_COMMAND,
|
|
441
|
+
cap: CODEX_APP_SERVER_THREADS_CAPABILITY,
|
|
442
|
+
dangerous: false,
|
|
443
|
+
duplex: true,
|
|
444
|
+
isAvailable: ({ env }) => Boolean(resolveNodeHostExecutable("codex", {
|
|
445
|
+
env,
|
|
446
|
+
pathEnv: env.PATH ?? env.Path ?? "",
|
|
447
|
+
strategy: "direct"
|
|
448
|
+
})),
|
|
449
|
+
handle: async (paramsJSON, io) => {
|
|
450
|
+
if (!io) throw new Error("Codex terminal command requires duplex transport");
|
|
451
|
+
const request = await bindRequest(paramsJSON);
|
|
452
|
+
const resume = decodeNodePtyResumeParams(request.paramsJSON, (value) => {
|
|
453
|
+
if (typeof value !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu.test(value)) throw new CatalogParamsError("threadId must be a UUID");
|
|
454
|
+
return value;
|
|
455
|
+
});
|
|
456
|
+
const record = await request.control.requireEligibleThread(resume.threadId);
|
|
457
|
+
const resolution = resolveNodeHostExecutable("codex", {
|
|
458
|
+
env: process.env,
|
|
459
|
+
pathEnv: process.env.PATH ?? process.env.Path ?? "",
|
|
460
|
+
strategy: "direct"
|
|
461
|
+
});
|
|
462
|
+
if (!resolution) throw new Error("Codex CLI is unavailable");
|
|
463
|
+
return JSON.stringify(await runNodePtyCommand({
|
|
464
|
+
file: resolution.executable,
|
|
465
|
+
args: ["resume", resume.threadId],
|
|
466
|
+
...record.cwd ? { cwd: record.cwd } : {},
|
|
467
|
+
env: { CODEX_HOME: request.codexHome },
|
|
468
|
+
cols: resume.cols,
|
|
469
|
+
rows: resume.rows
|
|
470
|
+
}, io));
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
async function openCodexCatalogTerminal(params) {
|
|
475
|
+
const title = `codex resume ${params.threadId.slice(0, 8)}…`;
|
|
476
|
+
if (params.hostId === "gateway:local" || params.hostId.startsWith(`gateway:local:`)) {
|
|
477
|
+
const record = await params.control.requireEligibleThread(params.threadId);
|
|
478
|
+
const resolution = resolveLocalCodexTerminalResolution();
|
|
479
|
+
if (!resolution) throw new CatalogParamsError("Codex CLI is unavailable");
|
|
480
|
+
return {
|
|
481
|
+
kind: "local",
|
|
482
|
+
argv: [
|
|
483
|
+
resolution.executable,
|
|
484
|
+
"resume",
|
|
485
|
+
params.threadId
|
|
486
|
+
],
|
|
487
|
+
...record.cwd ? { cwd: record.cwd } : {},
|
|
488
|
+
env: { CODEX_HOME: resolveCodexCatalogTerminalHome(params) },
|
|
489
|
+
...resolution.pathEnv ? { pathEnv: resolution.pathEnv } : {},
|
|
490
|
+
title
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
if (!params.hostId.startsWith("node:")) throw new CatalogParamsError("hostId is invalid");
|
|
494
|
+
const nodeId = params.hostId.slice(5);
|
|
495
|
+
if (!(await params.api.runtime.nodes.list()).nodes.find((candidate) => {
|
|
496
|
+
const commands = candidate.invocableCommands ?? candidate.commands;
|
|
497
|
+
return candidate.nodeId === nodeId && candidate.connected === true && commands?.includes("codex.appServer.threads.list.v1") === true && commands.includes("codex.terminal.resume.v1");
|
|
498
|
+
})) throw new CatalogParamsError("paired-node Codex terminal is unavailable");
|
|
499
|
+
const lookup = await lookupNodeCodexCatalogRecord({
|
|
500
|
+
agentId: params.agentId,
|
|
501
|
+
runtime: params.api.runtime,
|
|
502
|
+
nodeId,
|
|
503
|
+
threadId: params.threadId
|
|
504
|
+
});
|
|
505
|
+
if (lookup.kind !== "found" || !isInteractiveThreadSource(lookup.record.source)) throw new CatalogParamsError("Codex session is not a non-archived interactive Codex session");
|
|
506
|
+
const record = lookup.record;
|
|
507
|
+
return {
|
|
508
|
+
kind: "node",
|
|
509
|
+
nodeId,
|
|
510
|
+
command: CODEX_TERMINAL_RESUME_COMMAND,
|
|
511
|
+
uploadPathStyle: "native",
|
|
512
|
+
paramsJSON: JSON.stringify({
|
|
513
|
+
agentId: params.agentId,
|
|
514
|
+
threadId: params.threadId
|
|
515
|
+
}),
|
|
516
|
+
...record.cwd ? { cwd: record.cwd } : {},
|
|
517
|
+
title
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
async function startCodexCatalogTerminal(params) {
|
|
521
|
+
if (params.nodeId) return {
|
|
522
|
+
kind: "node",
|
|
523
|
+
nodeId: params.nodeId,
|
|
524
|
+
command: CODEX_TERMINAL_START_COMMAND,
|
|
525
|
+
uploadPathStyle: "native",
|
|
526
|
+
paramsJSON: JSON.stringify({
|
|
527
|
+
cwd: params.cwd,
|
|
528
|
+
initialMessage: params.initialMessage
|
|
529
|
+
}),
|
|
530
|
+
cwd: params.cwd,
|
|
531
|
+
title: "codex"
|
|
532
|
+
};
|
|
533
|
+
const resolution = resolveLocalCodexTerminalResolution();
|
|
534
|
+
if (!resolution) throw new CatalogParamsError("Codex CLI is unavailable; install Codex or add codex to PATH, then try again");
|
|
535
|
+
return {
|
|
536
|
+
kind: "local",
|
|
537
|
+
argv: [resolution.executable, ...params.initialMessage !== void 0 ? ["--", params.initialMessage] : []],
|
|
538
|
+
cwd: params.cwd,
|
|
539
|
+
env: { CODEX_HOME: resolveCodexCatalogTerminalHome(params) },
|
|
540
|
+
...resolution.pathEnv ? { pathEnv: resolution.pathEnv } : {},
|
|
541
|
+
title: "codex"
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
//#endregion
|
|
545
|
+
//#region extensions/codex/src/session-catalog-list-request.ts
|
|
546
|
+
const listRequest = new AsyncLocalStorage();
|
|
547
|
+
function currentCodexCatalogListRequest() {
|
|
548
|
+
return listRequest.getStore();
|
|
549
|
+
}
|
|
550
|
+
/** One foreground request owns its native read budget and source-health outcome. */
|
|
551
|
+
var CodexCatalogListRequest = class {
|
|
552
|
+
constructor() {
|
|
553
|
+
this.pages = 0;
|
|
554
|
+
this.closed = false;
|
|
555
|
+
this.attempts = /* @__PURE__ */ new Map();
|
|
556
|
+
}
|
|
557
|
+
get hasPages() {
|
|
558
|
+
return this.pages < 20;
|
|
559
|
+
}
|
|
560
|
+
deadline(timeoutMs) {
|
|
561
|
+
this.assertActive();
|
|
562
|
+
return this.deadlineAt ??= performance.now() + timeoutMs;
|
|
563
|
+
}
|
|
564
|
+
constrainDeadline(absolute) {
|
|
565
|
+
this.assertActive();
|
|
566
|
+
this.deadlineAt = Math.min(this.deadlineAt ?? absolute, absolute);
|
|
567
|
+
return this.deadlineAt;
|
|
568
|
+
}
|
|
569
|
+
remaining(timeoutMs) {
|
|
570
|
+
const remaining = Math.min(timeoutMs, this.deadline(timeoutMs) - performance.now());
|
|
571
|
+
if (remaining <= 0) throw new CodexCatalogLoadingError();
|
|
572
|
+
return remaining;
|
|
573
|
+
}
|
|
574
|
+
assertActive() {
|
|
575
|
+
if (this.closed) throw new DOMException("Codex session catalog request is closed", "AbortError");
|
|
576
|
+
if (this.deadlineAt !== void 0 && this.deadlineAt <= performance.now()) throw new CodexCatalogLoadingError();
|
|
577
|
+
}
|
|
578
|
+
async run(run) {
|
|
579
|
+
this.assertActive();
|
|
580
|
+
const pending = listRequest.run(this, run);
|
|
581
|
+
const remaining = this.deadlineAt === void 0 ? void 0 : this.deadlineAt - performance.now();
|
|
582
|
+
if (remaining !== void 0 && remaining <= 0) {
|
|
583
|
+
pending.catch(() => {});
|
|
584
|
+
throw new CodexCatalogLoadingError();
|
|
585
|
+
}
|
|
586
|
+
const result = remaining === void 0 ? await pending : await withTimeout(pending, remaining, "Codex session catalog is still loading", () => new CodexCatalogLoadingError());
|
|
587
|
+
this.assertActive();
|
|
588
|
+
return result;
|
|
589
|
+
}
|
|
590
|
+
async read(timeoutMs, read) {
|
|
591
|
+
const remaining = this.remaining(timeoutMs);
|
|
592
|
+
if (!this.hasPages) throw new CatalogParamsError("Codex catalog native page budget is exhausted");
|
|
593
|
+
this.pages++;
|
|
594
|
+
try {
|
|
595
|
+
const result = await withTimeout(read(remaining), remaining, "Codex session catalog is still loading", () => new CodexCatalogLoadingError());
|
|
596
|
+
this.assertActive();
|
|
597
|
+
return result;
|
|
598
|
+
} catch (error) {
|
|
599
|
+
if (!this.closed) this.failure ??= { error };
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
attempt(owner, key, start) {
|
|
604
|
+
this.assertActive();
|
|
605
|
+
let attempts = this.attempts.get(owner);
|
|
606
|
+
const existing = attempts?.get(key);
|
|
607
|
+
if (existing) return existing;
|
|
608
|
+
const attempt = start();
|
|
609
|
+
if (!attempts) {
|
|
610
|
+
attempts = /* @__PURE__ */ new Map();
|
|
611
|
+
this.attempts.set(owner, attempts);
|
|
612
|
+
}
|
|
613
|
+
attempts.set(key, attempt);
|
|
614
|
+
return attempt;
|
|
615
|
+
}
|
|
616
|
+
resolved() {
|
|
617
|
+
if (this.failure) {
|
|
618
|
+
this.rejected(this.failure.error);
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
this.settle((attempt) => attempt.resolved());
|
|
622
|
+
}
|
|
623
|
+
rejected(error) {
|
|
624
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
625
|
+
this.close();
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
const reason = this.failure?.error ?? error;
|
|
629
|
+
this.settle((attempt) => attempt.rejected(reason));
|
|
630
|
+
}
|
|
631
|
+
close() {
|
|
632
|
+
this.settle((attempt) => attempt.abandoned());
|
|
633
|
+
}
|
|
634
|
+
settle(settle) {
|
|
635
|
+
if (this.closed) return;
|
|
636
|
+
this.closed = true;
|
|
637
|
+
for (const attempts of this.attempts.values()) for (const attempt of attempts.values()) if (attempt.allowed) settle(attempt);
|
|
638
|
+
this.attempts.clear();
|
|
639
|
+
this.failure = void 0;
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
async function withCodexCatalogListRequest(run) {
|
|
643
|
+
const current = currentCodexCatalogListRequest();
|
|
644
|
+
if (current) {
|
|
645
|
+
current.assertActive();
|
|
646
|
+
return await run(current);
|
|
647
|
+
}
|
|
648
|
+
const scope = new CodexCatalogListRequest();
|
|
649
|
+
try {
|
|
650
|
+
const result = await scope.run(() => run(scope));
|
|
651
|
+
scope.resolved();
|
|
652
|
+
return result;
|
|
653
|
+
} catch (error) {
|
|
654
|
+
scope.rejected(error);
|
|
655
|
+
throw error;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
//#endregion
|
|
659
|
+
//#region extensions/codex/src/session-catalog-visible-page.ts
|
|
660
|
+
/** Fill exclusions from bounded resident pages. */
|
|
661
|
+
var CodexCatalogVisiblePage = class {
|
|
662
|
+
constructor(params) {
|
|
663
|
+
this.params = params;
|
|
664
|
+
this.sessions = [];
|
|
665
|
+
this.seenCursors = /* @__PURE__ */ new Set();
|
|
666
|
+
this.pages = 0;
|
|
667
|
+
this.complete = false;
|
|
668
|
+
this.request = new CodexCatalogListRequest();
|
|
669
|
+
this.cursor = params.cursor;
|
|
670
|
+
}
|
|
671
|
+
async next() {
|
|
672
|
+
try {
|
|
673
|
+
const step = await this.request.run(() => this.nextPage());
|
|
674
|
+
if (step.done) this.request.resolved();
|
|
675
|
+
return step;
|
|
676
|
+
} catch (error) {
|
|
677
|
+
if (this.params.signal?.aborted) this.request.close();
|
|
678
|
+
else this.request.rejected(error);
|
|
679
|
+
throw error;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
close() {
|
|
683
|
+
this.complete = true;
|
|
684
|
+
this.request.close();
|
|
685
|
+
}
|
|
686
|
+
async nextPage() {
|
|
687
|
+
if (this.complete) throw new Error("Codex catalog page is already complete");
|
|
688
|
+
const params = this.params;
|
|
689
|
+
params.signal?.throwIfAborted();
|
|
690
|
+
const diagnostics = currentCodexCatalogListDiagnostics();
|
|
691
|
+
const started = diagnostics ? performance.now() : 0;
|
|
692
|
+
if (diagnostics) diagnostics.fields.controlPageCalls++;
|
|
693
|
+
let rawPage;
|
|
694
|
+
try {
|
|
695
|
+
rawPage = await params.control.listPage({
|
|
696
|
+
limit: params.limit - this.sessions.length,
|
|
697
|
+
...this.cursor ? { cursor: this.cursor } : {},
|
|
698
|
+
...params.searchTerm ? { searchTerm: params.searchTerm } : {},
|
|
699
|
+
...params.cwd ? { cwd: params.cwd } : {}
|
|
700
|
+
});
|
|
701
|
+
} finally {
|
|
702
|
+
if (diagnostics && !diagnostics.closed) diagnostics.fields.controlWaitSumMs = (diagnostics.fields.controlWaitSumMs ?? 0) + performance.now() - started;
|
|
703
|
+
}
|
|
704
|
+
this.request.assertActive();
|
|
705
|
+
params.signal?.throwIfAborted();
|
|
706
|
+
const page = filterCatalogPageByTitle(parseCatalogPage(rawPage), params.searchTerm);
|
|
707
|
+
if (this.pages++ === 0) this.backwardsCursor = page.backwardsCursor;
|
|
708
|
+
let excludedFromPage = false;
|
|
709
|
+
for (const managed of rawPage.managedThreads ?? []) {
|
|
710
|
+
excludedFromPage = true;
|
|
711
|
+
params.signal?.throwIfAborted();
|
|
712
|
+
await params.onExcludedThread?.(managed);
|
|
713
|
+
}
|
|
714
|
+
for (const session of page.sessions) {
|
|
715
|
+
if (!params.excludedThreadIds?.has(session.threadId)) {
|
|
716
|
+
this.sessions.push(session);
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
excludedFromPage = true;
|
|
720
|
+
params.signal?.throwIfAborted();
|
|
721
|
+
await params.onExcludedThread?.({ threadId: session.threadId });
|
|
722
|
+
}
|
|
723
|
+
const nextCursor = page.nextCursor;
|
|
724
|
+
if (!nextCursor || this.sessions.length >= params.limit || !excludedFromPage) this.complete = true;
|
|
725
|
+
else {
|
|
726
|
+
if (this.seenCursors.has(nextCursor)) throw new Error("Codex session catalog returned a repeated exclusion cursor");
|
|
727
|
+
this.seenCursors.add(nextCursor);
|
|
728
|
+
this.cursor = nextCursor;
|
|
729
|
+
this.complete = this.pages >= 20 || !this.request.hasPages;
|
|
730
|
+
}
|
|
731
|
+
return this.complete ? {
|
|
732
|
+
done: true,
|
|
733
|
+
page: {
|
|
734
|
+
sessions: this.sessions.slice(0, params.limit),
|
|
735
|
+
...nextCursor ? { nextCursor } : {},
|
|
736
|
+
...this.backwardsCursor ? { backwardsCursor: this.backwardsCursor } : {}
|
|
737
|
+
}
|
|
738
|
+
} : { done: false };
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
/** Node and direct callers finish the same bounded outer algorithm inline. */
|
|
742
|
+
async function listVisiblePage(params) {
|
|
743
|
+
const operation = new CodexCatalogVisiblePage(params);
|
|
744
|
+
try {
|
|
745
|
+
for (;;) {
|
|
746
|
+
const step = await operation.next();
|
|
747
|
+
if (step.done) return step.page;
|
|
748
|
+
}
|
|
749
|
+
} finally {
|
|
750
|
+
operation.close();
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
//#endregion
|
|
754
|
+
//#region extensions/codex/src/session-catalog-list-operation.ts
|
|
755
|
+
function hostFailure(source, error) {
|
|
756
|
+
return {
|
|
757
|
+
hostId: source?.hostId ?? "gateway:local",
|
|
758
|
+
label: source?.label ?? "Local Codex",
|
|
759
|
+
kind: "gateway",
|
|
760
|
+
connected: false,
|
|
761
|
+
sessions: [],
|
|
762
|
+
error: error instanceof CodexCatalogLoadingError ? {
|
|
763
|
+
code: error.code,
|
|
764
|
+
message: error.message
|
|
765
|
+
} : catalogError("APP_SERVER_UNAVAILABLE", error)
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
async function projectLocalHost(params, agentId, source, page) {
|
|
769
|
+
params.signal?.throwIfAborted();
|
|
770
|
+
const { listAdoptedSessionEntries } = await import("./session-catalog-adoption-CR_whkEW.mjs");
|
|
771
|
+
const { sessionCatalogAdoptedSourceKey } = await import("openclaw/plugin-sdk/session-catalog");
|
|
772
|
+
params.signal?.throwIfAborted();
|
|
773
|
+
const diagnostics = currentCodexCatalogListDiagnostics();
|
|
774
|
+
const started = diagnostics ? performance.now() : 0;
|
|
775
|
+
if (diagnostics) diagnostics.fields.adoptionCalls++;
|
|
776
|
+
let adopted;
|
|
777
|
+
try {
|
|
778
|
+
adopted = await listAdoptedSessionEntries({
|
|
779
|
+
agentId,
|
|
780
|
+
bindingStore: params.bindingStore,
|
|
781
|
+
config: params.config,
|
|
782
|
+
runtime: params.runtime,
|
|
783
|
+
sessionEntries: params.sessionEntries
|
|
784
|
+
});
|
|
785
|
+
} finally {
|
|
786
|
+
if (diagnostics && !diagnostics.closed) diagnostics.fields.adoptionSumMs = (diagnostics.fields.adoptionSumMs ?? 0) + performance.now() - started;
|
|
787
|
+
}
|
|
788
|
+
const hostId = source?.hostId ?? "gateway:local";
|
|
789
|
+
const sourceHomeId = source?.sourceHomeId ?? "gateway:local";
|
|
790
|
+
return {
|
|
791
|
+
hostId,
|
|
792
|
+
label: source?.label ?? "Local Codex",
|
|
793
|
+
kind: "gateway",
|
|
794
|
+
connected: true,
|
|
795
|
+
...page,
|
|
796
|
+
sessions: page.sessions.map((session) => {
|
|
797
|
+
const entry = adopted.get(sessionCatalogAdoptedSourceKey(sourceHomeId, session.threadId)) ?? (hostId === "gateway:local" ? adopted.get(sessionCatalogAdoptedSourceKey("gateway:local", session.threadId)) : void 0);
|
|
798
|
+
const sourced = source ? {
|
|
799
|
+
...session,
|
|
800
|
+
sourceHomeId: source.sourceHomeId
|
|
801
|
+
} : session;
|
|
802
|
+
return entry ? {
|
|
803
|
+
...sourced,
|
|
804
|
+
sessionKey: entry.key
|
|
805
|
+
} : sourced;
|
|
806
|
+
})
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
function managedMarker(store, sourceHomeId, managed) {
|
|
810
|
+
return async ({ threadId, rolloutPath }) => {
|
|
811
|
+
if (managed?.has(threadId)) return;
|
|
812
|
+
const diagnostics = currentCodexCatalogListDiagnostics();
|
|
813
|
+
const started = diagnostics ? performance.now() : 0;
|
|
814
|
+
if (diagnostics) diagnostics.fields.exclusionMarkCalls++;
|
|
815
|
+
try {
|
|
816
|
+
await store.mark({
|
|
817
|
+
sourceHomeId,
|
|
818
|
+
threadId,
|
|
819
|
+
...rolloutPath ? { rolloutPath } : {}
|
|
820
|
+
});
|
|
821
|
+
} finally {
|
|
822
|
+
if (diagnostics && !diagnostics.closed) diagnostics.fields.exclusionMarkSumMs = (diagnostics.fields.exclusionMarkSumMs ?? 0) + performance.now() - started;
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
function createNodePublicationTracker(publications, waitUntil) {
|
|
827
|
+
const settled = () => {
|
|
828
|
+
publications.pending--;
|
|
829
|
+
};
|
|
830
|
+
return (completion) => {
|
|
831
|
+
publications.pending++;
|
|
832
|
+
completion.then(settled, settled);
|
|
833
|
+
waitUntil?.(completion);
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
/** Holds only one logical filled list; no native producer is suspended between next calls. */
|
|
837
|
+
var CodexCatalogListDriver = class {
|
|
838
|
+
constructor(params) {
|
|
839
|
+
this.locals = [];
|
|
840
|
+
this.nodeActive = false;
|
|
841
|
+
this.nodeDiscoveryFailed = false;
|
|
842
|
+
this.nodePublications = { pending: 0 };
|
|
843
|
+
this.nodesStarted = false;
|
|
844
|
+
this.localFailed = false;
|
|
845
|
+
this.active = 0;
|
|
846
|
+
this.running = false;
|
|
847
|
+
this.complete = false;
|
|
848
|
+
this.params = params;
|
|
849
|
+
}
|
|
850
|
+
request() {
|
|
851
|
+
if (!this.params) throw new Error("Codex catalog list operation is closed");
|
|
852
|
+
return this.params;
|
|
853
|
+
}
|
|
854
|
+
selection() {
|
|
855
|
+
if (!this.prepared) throw new Error("Codex catalog list operation is not initialized");
|
|
856
|
+
return this.prepared;
|
|
857
|
+
}
|
|
858
|
+
async initialize() {
|
|
859
|
+
const params = this.request();
|
|
860
|
+
const agentId = resolveSessionAgentIdsStrict({
|
|
861
|
+
config: params.config ?? {},
|
|
862
|
+
agentId: params.agentId
|
|
863
|
+
}).sessionAgentId;
|
|
864
|
+
const query = readGatewayParams(params.query);
|
|
865
|
+
const requestedHostIds = query.hostIds ? new Set(query.hostIds) : void 0;
|
|
866
|
+
const localSources = params.localHomes?.filter((source) => !requestedHostIds || requestedHostIds.has(source.hostId)) ?? (params.includeLocal !== false && (!requestedHostIds || requestedHostIds.has("gateway:local")) ? [void 0] : []);
|
|
867
|
+
const diagnostics = currentCodexCatalogListDiagnostics();
|
|
868
|
+
if (diagnostics) diagnostics.fields.localHostCount = localSources.length;
|
|
869
|
+
const store = params.bindingStore.managedThreads;
|
|
870
|
+
const started = diagnostics && store ? performance.now() : 0;
|
|
871
|
+
let managed;
|
|
872
|
+
try {
|
|
873
|
+
managed = await store?.snapshot();
|
|
874
|
+
} finally {
|
|
875
|
+
if (diagnostics && !diagnostics.closed && store) diagnostics.fields.managedSnapshotMs = performance.now() - started;
|
|
876
|
+
}
|
|
877
|
+
params.signal?.throwIfAborted();
|
|
878
|
+
const fallback = localSources.some((source) => source === void 0) ? (await params.control.homesForAgent(agentId))[0] : void 0;
|
|
879
|
+
params.signal?.throwIfAborted();
|
|
880
|
+
this.prepared = {
|
|
881
|
+
agentId,
|
|
882
|
+
query,
|
|
883
|
+
requestedHostIds
|
|
884
|
+
};
|
|
885
|
+
if (requestedHostIds && !query.hostIds?.some((host) => host.startsWith("node:"))) this.nodeHosts = [];
|
|
886
|
+
for (const source of localSources) {
|
|
887
|
+
const selected = source ?? fallback;
|
|
888
|
+
const excluded = selected ? managed?.get(selected.sourceHomeId) : void 0;
|
|
889
|
+
const host = {
|
|
890
|
+
source,
|
|
891
|
+
page: new CodexCatalogVisiblePage({
|
|
892
|
+
control: params.control.forRequest(agentId, selected),
|
|
893
|
+
cursor: query.cursors?.[source?.hostId ?? "gateway:local"],
|
|
894
|
+
limit: query.limitPerHost,
|
|
895
|
+
excludedThreadIds: excluded,
|
|
896
|
+
searchTerm: query.search,
|
|
897
|
+
signal: params.signal,
|
|
898
|
+
...selected && store ? { onExcludedThread: managedMarker(store, selected.sourceHomeId, excluded) } : {}
|
|
899
|
+
}),
|
|
900
|
+
completion: createDeferred()
|
|
901
|
+
};
|
|
902
|
+
this.locals.push(host);
|
|
903
|
+
publishSessionCatalogHost(params, host.completion.promise);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
canPause() {
|
|
907
|
+
return !this.localFailed && this.nodeHosts !== void 0 && !this.nodeDiscoveryFailed && !this.nodeActive && this.nodePublications.pending === 0;
|
|
908
|
+
}
|
|
909
|
+
async readHost(host) {
|
|
910
|
+
const params = this.request();
|
|
911
|
+
try {
|
|
912
|
+
const page = await host.page.next();
|
|
913
|
+
params.signal?.throwIfAborted();
|
|
914
|
+
if (page.done) host.value = await projectLocalHost(params, this.selection().agentId, host.source, page.page);
|
|
915
|
+
} catch (error) {
|
|
916
|
+
this.localFailed = true;
|
|
917
|
+
host.value = hostFailure(host.source, error);
|
|
918
|
+
}
|
|
919
|
+
if (host.value) host.completion.resolve(host.value);
|
|
920
|
+
}
|
|
921
|
+
startHost(host) {
|
|
922
|
+
if (host.value || host.active || this.failure) return;
|
|
923
|
+
this.active++;
|
|
924
|
+
host.active = this.readHost(host).then(() => {
|
|
925
|
+
host.active = void 0;
|
|
926
|
+
this.active--;
|
|
927
|
+
if (!host.value && (!this.canPause() || this.active > 0)) this.startHost(host);
|
|
928
|
+
this.checkpoint();
|
|
929
|
+
}, (error) => {
|
|
930
|
+
host.active = void 0;
|
|
931
|
+
this.active--;
|
|
932
|
+
this.failure ??= { error };
|
|
933
|
+
this.checkpoint();
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
async readNodes() {
|
|
937
|
+
const params = this.request();
|
|
938
|
+
const { agentId, query, requestedHostIds } = this.selection();
|
|
939
|
+
const diagnostics = currentCodexCatalogListDiagnostics();
|
|
940
|
+
const started = diagnostics ? performance.now() : 0;
|
|
941
|
+
if (diagnostics) diagnostics.fields.nodeRegistryCalls = 1;
|
|
942
|
+
let nodes;
|
|
943
|
+
try {
|
|
944
|
+
try {
|
|
945
|
+
nodes = (await (params.listNodes?.() ?? params.runtime.nodes.list())).nodes.filter((node) => node.gatewayLocal !== true && (node.commands?.includes("codex.appServer.threads.list.v1") || codexNodeTerminalCapability(node).canStartTerminal) && (!requestedHostIds || requestedHostIds.has(`node:${node.nodeId}`))).slice(0, 100 - this.locals.length);
|
|
946
|
+
} finally {
|
|
947
|
+
if (diagnostics && !diagnostics.closed) diagnostics.fields.nodeRegistryMs = performance.now() - started;
|
|
948
|
+
}
|
|
949
|
+
} catch (error) {
|
|
950
|
+
this.nodeDiscoveryFailed = true;
|
|
951
|
+
const host = {
|
|
952
|
+
hostId: "node:registry",
|
|
953
|
+
label: "Paired nodes",
|
|
954
|
+
kind: "node",
|
|
955
|
+
connected: false,
|
|
956
|
+
canStartTerminal: false,
|
|
957
|
+
sessions: [],
|
|
958
|
+
error: catalogError("NODE_LIST_FAILED", error)
|
|
959
|
+
};
|
|
960
|
+
params.onHost?.(host);
|
|
961
|
+
return [host];
|
|
962
|
+
}
|
|
963
|
+
params.signal?.throwIfAborted();
|
|
964
|
+
const { listNodeAdoptedSessionEntries } = await import("./session-catalog-node-adoption-BVRsYOSh.mjs");
|
|
965
|
+
const { compareNodeLabels, listPairedNode } = await import("./session-catalog-node-continue-Dowv8osw.mjs");
|
|
966
|
+
params.signal?.throwIfAborted();
|
|
967
|
+
const adopted = listNodeAdoptedSessionEntries({
|
|
968
|
+
agentId,
|
|
969
|
+
config: params.config,
|
|
970
|
+
runtime: params.runtime,
|
|
971
|
+
sessionEntries: params.sessionEntries
|
|
972
|
+
});
|
|
973
|
+
if (diagnostics && !diagnostics.closed) {
|
|
974
|
+
diagnostics.fields.pairedNodeCalls = 0;
|
|
975
|
+
diagnostics.fields.pairedNodeSettled = 0;
|
|
976
|
+
}
|
|
977
|
+
const trackPublication = createNodePublicationTracker(this.nodePublications, params.waitUntil);
|
|
978
|
+
const pendingHosts = nodes.toSorted(compareNodeLabels).map((node) => {
|
|
979
|
+
const nodeStarted = diagnostics ? performance.now() : 0;
|
|
980
|
+
if (diagnostics && !diagnostics.closed) diagnostics.fields.pairedNodeCalls = (diagnostics.fields.pairedNodeCalls ?? 0) + 1;
|
|
981
|
+
const host = listPairedNode({
|
|
982
|
+
agentId,
|
|
983
|
+
runtime: params.runtime,
|
|
984
|
+
node,
|
|
985
|
+
query,
|
|
986
|
+
adoptedSessions: adopted,
|
|
987
|
+
terminalCapabilities: codexNodeTerminalCapability(node),
|
|
988
|
+
waitUntil: trackPublication,
|
|
989
|
+
signal: params.signal,
|
|
990
|
+
...params.onHost ? { onHost: params.onHost } : {}
|
|
991
|
+
});
|
|
992
|
+
return diagnostics ? host.finally(() => {
|
|
993
|
+
if (!diagnostics.closed) {
|
|
994
|
+
diagnostics.fields.pairedNodeSettled = (diagnostics.fields.pairedNodeSettled ?? 0) + 1;
|
|
995
|
+
diagnostics.fields.nodeWaitSumMs = (diagnostics.fields.nodeWaitSumMs ?? 0) + performance.now() - nodeStarted;
|
|
996
|
+
}
|
|
997
|
+
}) : host;
|
|
998
|
+
});
|
|
999
|
+
try {
|
|
1000
|
+
return await Promise.all(pendingHosts);
|
|
1001
|
+
} catch (error) {
|
|
1002
|
+
await Promise.allSettled(pendingHosts);
|
|
1003
|
+
throw error;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
startNodes() {
|
|
1007
|
+
if (this.nodeHosts !== void 0 || this.nodesStarted) return;
|
|
1008
|
+
this.nodesStarted = true;
|
|
1009
|
+
this.nodeActive = true;
|
|
1010
|
+
this.readNodes().then((hosts) => {
|
|
1011
|
+
this.nodeHosts = hosts;
|
|
1012
|
+
this.nodeActive = false;
|
|
1013
|
+
this.checkpoint();
|
|
1014
|
+
}, (error) => {
|
|
1015
|
+
this.nodeActive = false;
|
|
1016
|
+
this.failure ??= { error };
|
|
1017
|
+
this.checkpoint();
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
checkpoint() {
|
|
1021
|
+
if (this.active > 0 || !this.step) return;
|
|
1022
|
+
if (this.failure) {
|
|
1023
|
+
if (!this.nodeActive) this.step.reject(this.failure.error);
|
|
1024
|
+
} else if (this.nodeHosts && this.locals.every((host) => host.value !== void 0)) {
|
|
1025
|
+
this.complete = true;
|
|
1026
|
+
this.step.resolve({
|
|
1027
|
+
done: true,
|
|
1028
|
+
hosts: [...this.locals.flatMap((host) => host.value ? [host.value] : []), ...this.nodeHosts]
|
|
1029
|
+
});
|
|
1030
|
+
} else if (this.canPause()) this.step.resolve({ done: false });
|
|
1031
|
+
}
|
|
1032
|
+
async next() {
|
|
1033
|
+
const params = this.request();
|
|
1034
|
+
if (this.running || this.complete) throw new Error("Codex catalog list operation cannot advance");
|
|
1035
|
+
params.signal?.throwIfAborted();
|
|
1036
|
+
this.running = true;
|
|
1037
|
+
try {
|
|
1038
|
+
if (!this.prepared) await this.initialize();
|
|
1039
|
+
this.step = createDeferred();
|
|
1040
|
+
for (const host of this.locals) this.startHost(host);
|
|
1041
|
+
this.startNodes();
|
|
1042
|
+
this.checkpoint();
|
|
1043
|
+
return await this.step.promise;
|
|
1044
|
+
} finally {
|
|
1045
|
+
this.running = false;
|
|
1046
|
+
this.step = void 0;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
close() {
|
|
1050
|
+
if (!this.params) return;
|
|
1051
|
+
if (this.running) throw new Error("Cannot close an active Codex catalog list step");
|
|
1052
|
+
const reason = this.failure?.error ?? this.params.signal?.reason ?? /* @__PURE__ */ new Error("Codex catalog list operation closed");
|
|
1053
|
+
this.params = void 0;
|
|
1054
|
+
for (const host of this.locals) {
|
|
1055
|
+
host.page.close();
|
|
1056
|
+
if (!host.value) host.completion.reject(reason);
|
|
1057
|
+
}
|
|
1058
|
+
this.locals = [];
|
|
1059
|
+
this.prepared = void 0;
|
|
1060
|
+
this.nodeHosts = void 0;
|
|
1061
|
+
this.failure = void 0;
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
function createCodexSessionCatalogListOperation(params) {
|
|
1065
|
+
const driver = new CodexCatalogListDriver(params);
|
|
1066
|
+
return {
|
|
1067
|
+
next: () => driver.next(),
|
|
1068
|
+
close: () => driver.close()
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
async function runCatalogListInline(operation) {
|
|
1072
|
+
try {
|
|
1073
|
+
for (;;) {
|
|
1074
|
+
const step = await operation.next();
|
|
1075
|
+
if (step.done) return step.hosts;
|
|
1076
|
+
}
|
|
1077
|
+
} finally {
|
|
1078
|
+
operation.close();
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
async function listCodexSessionCatalog(params) {
|
|
1082
|
+
return { hosts: await runCatalogListInline(createCodexSessionCatalogListOperation(params)) };
|
|
1083
|
+
}
|
|
1084
|
+
//#endregion
|
|
1085
|
+
//#region extensions/codex/src/session-catalog-transcript-item.ts
|
|
1086
|
+
const CODEX_MESSAGE_TYPES = /* @__PURE__ */ new Map([
|
|
1087
|
+
["userMessage", "userMessage"],
|
|
1088
|
+
["agentMessage", "agentMessage"],
|
|
1089
|
+
["reasoning", "reasoning"]
|
|
1090
|
+
]);
|
|
1091
|
+
const CODEX_TOOL_TYPES = /* @__PURE__ */ new Set([
|
|
1092
|
+
"commandExecution",
|
|
1093
|
+
"fileChange",
|
|
1094
|
+
"mcpToolCall",
|
|
1095
|
+
"dynamicToolCall",
|
|
1096
|
+
"collabAgentToolCall",
|
|
1097
|
+
"webSearch",
|
|
1098
|
+
"imageView",
|
|
1099
|
+
"imageGeneration"
|
|
1100
|
+
]);
|
|
1101
|
+
function toGenericTranscriptItem(item) {
|
|
1102
|
+
let type = CODEX_MESSAGE_TYPES.get(item.type);
|
|
1103
|
+
if (!type && CODEX_TOOL_TYPES.has(item.type)) type = item.result !== void 0 || Boolean(item.aggregatedOutput) ? "toolResult" : "toolCall";
|
|
1104
|
+
type ??= "other";
|
|
1105
|
+
const fallback = item.title ?? item.name ?? item.tool ?? item.command ?? item.query ?? void 0;
|
|
1106
|
+
const resultText = item.aggregatedOutput || (item.result === void 0 ? void 0 : JSON.stringify(item.result, null, 2));
|
|
1107
|
+
const changesText = Array.isArray(item.changes) ? item.changes.map((change) => `${change.kind}: ${change.path}`).join("\n") || void 0 : void 0;
|
|
1108
|
+
const text = item.type === "userMessage" ? projectCodexUserItemText(item) : item.text || resultText || changesText || fallback;
|
|
1109
|
+
return {
|
|
1110
|
+
id: item.id,
|
|
1111
|
+
type,
|
|
1112
|
+
...text ? { text } : {},
|
|
1113
|
+
raw: item
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
//#endregion
|
|
1117
|
+
//#region extensions/codex/src/session-catalog-transcript.ts
|
|
1118
|
+
const transcriptPageSchema = z.strictObject({
|
|
1119
|
+
items: z.array(z.strictObject({
|
|
1120
|
+
id: z.string(),
|
|
1121
|
+
type: z.enum([
|
|
1122
|
+
"userMessage",
|
|
1123
|
+
"agentMessage",
|
|
1124
|
+
"reasoning",
|
|
1125
|
+
"toolCall",
|
|
1126
|
+
"toolResult",
|
|
1127
|
+
"other"
|
|
1128
|
+
]),
|
|
1129
|
+
text: z.string().optional(),
|
|
1130
|
+
raw: z.record(z.string(), z.json()).optional(),
|
|
1131
|
+
truncated: z.boolean().optional()
|
|
1132
|
+
})),
|
|
1133
|
+
nextCursor: z.string().optional()
|
|
1134
|
+
});
|
|
1135
|
+
function parseCodexCatalogTranscriptPage(value) {
|
|
1136
|
+
return transcriptPageSchema.parse(value);
|
|
1137
|
+
}
|
|
1138
|
+
function projectTranscriptPage(items, limit) {
|
|
1139
|
+
const projected = items.map(toGenericTranscriptItem);
|
|
1140
|
+
const page = sessionCatalogPaging.boundTranscriptPage(projected.toReversed(), limit, 0).items;
|
|
1141
|
+
for (const [index, item] of page.entries()) if (item.text !== projected[index]?.text && projected[index]?.text) item.truncated = true;
|
|
1142
|
+
return page;
|
|
1143
|
+
}
|
|
1144
|
+
function pageFitsNodeTransport(page) {
|
|
1145
|
+
return Buffer.byteLength(JSON.stringify({ payloadJSON: JSON.stringify(page) }), "utf8") <= MAX_TRANSCRIPT_PAGE_BYTES;
|
|
1146
|
+
}
|
|
1147
|
+
/** The legacy API can anchor a turn, but cannot continue within that turn. */
|
|
1148
|
+
async function readLegacyCodexTranscriptPage(readTurns, request) {
|
|
1149
|
+
return readLegacyCodexHistoryPage(readTurns, request, {
|
|
1150
|
+
project: (entries, limit) => projectTranscriptPage(entries.map(({ item }) => item), limit),
|
|
1151
|
+
fits: pageFitsNodeTransport
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
/** Uses the native store's item cursor whenever that store supports item history. */
|
|
1155
|
+
async function readCodexCatalogTranscriptPage(control, request) {
|
|
1156
|
+
const thread = await control.requireEligibleThread(request.threadId);
|
|
1157
|
+
return readCodexThreadHistoryPage(control, thread, request, {
|
|
1158
|
+
project: (entries, limit) => projectTranscriptPage(entries.map(({ item }) => item), limit),
|
|
1159
|
+
fits: pageFitsNodeTransport
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
//#endregion
|
|
1163
|
+
//#region extensions/codex/src/session-catalog-listing.ts
|
|
1164
|
+
/** Builds the node-local read-only Codex app-server catalog command. */
|
|
1165
|
+
function createCodexSessionCatalogNodeHostCommands(controlFactory, bindingStore) {
|
|
1166
|
+
const bindRequest = async (paramsJSON) => {
|
|
1167
|
+
const parsed = parseJsonParams(paramsJSON);
|
|
1168
|
+
if (!isRecord(parsed)) throw new CatalogParamsError("Codex session catalog parameters must be an object");
|
|
1169
|
+
const agentId = readBoundedOptionalString(parsed, "agentId", 256);
|
|
1170
|
+
const request = { ...parsed };
|
|
1171
|
+
delete request.agentId;
|
|
1172
|
+
return {
|
|
1173
|
+
...await controlFactory.forNode(agentId),
|
|
1174
|
+
params: request,
|
|
1175
|
+
paramsJSON: JSON.stringify(request)
|
|
1176
|
+
};
|
|
1177
|
+
};
|
|
1178
|
+
const commands = [
|
|
1179
|
+
{
|
|
1180
|
+
command: CODEX_APP_SERVER_THREADS_LIST_COMMAND,
|
|
1181
|
+
cap: CODEX_APP_SERVER_THREADS_CAPABILITY,
|
|
1182
|
+
dangerous: false,
|
|
1183
|
+
handle: async (paramsJSON) => {
|
|
1184
|
+
const request = await bindRequest(paramsJSON);
|
|
1185
|
+
const pageParams = readPageParams(request.params);
|
|
1186
|
+
try {
|
|
1187
|
+
const managedThreads = await bindingStore?.managedThreads?.snapshot();
|
|
1188
|
+
const sourceHomeId = request.sourceHomeId;
|
|
1189
|
+
const managedThreadIds = sourceHomeId ? managedThreads?.get(sourceHomeId) : void 0;
|
|
1190
|
+
const page = await listVisiblePage({
|
|
1191
|
+
control: request.control,
|
|
1192
|
+
cursor: pageParams.cursor,
|
|
1193
|
+
cwd: pageParams.cwd,
|
|
1194
|
+
excludedThreadIds: managedThreadIds,
|
|
1195
|
+
limit: pageParams.limit,
|
|
1196
|
+
...sourceHomeId && bindingStore?.managedThreads ? { onExcludedThread: async ({ threadId, rolloutPath }) => {
|
|
1197
|
+
if (!managedThreadIds?.has(threadId)) await bindingStore.managedThreads?.mark({
|
|
1198
|
+
sourceHomeId,
|
|
1199
|
+
threadId,
|
|
1200
|
+
...rolloutPath ? { rolloutPath } : {}
|
|
1201
|
+
});
|
|
1202
|
+
} } : {},
|
|
1203
|
+
searchTerm: pageParams.searchTerm
|
|
1204
|
+
});
|
|
1205
|
+
return JSON.stringify(page);
|
|
1206
|
+
} catch {
|
|
1207
|
+
throw new Error("Codex app-server catalog is unavailable");
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
command: CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND,
|
|
1213
|
+
cap: CODEX_APP_SERVER_THREADS_CAPABILITY,
|
|
1214
|
+
dangerous: false,
|
|
1215
|
+
handle: async (paramsJSON) => {
|
|
1216
|
+
const request = await bindRequest(paramsJSON);
|
|
1217
|
+
const action = readNodeTranscriptParams(request.params);
|
|
1218
|
+
try {
|
|
1219
|
+
await request.control.requireEligibleThread(action.threadId);
|
|
1220
|
+
const page = parseTranscriptPage(await request.control.listTurnPage({
|
|
1221
|
+
threadId: action.threadId,
|
|
1222
|
+
limit: action.limit,
|
|
1223
|
+
sortDirection: "desc",
|
|
1224
|
+
itemsView: "full",
|
|
1225
|
+
...action.cursor ? { cursor: action.cursor } : {}
|
|
1226
|
+
}));
|
|
1227
|
+
return JSON.stringify(page);
|
|
1228
|
+
} catch (error) {
|
|
1229
|
+
if (error instanceof CatalogParamsError) throw error;
|
|
1230
|
+
throw new Error("Codex app-server transcript is unavailable", { cause: error });
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
},
|
|
1234
|
+
{
|
|
1235
|
+
command: CODEX_CATALOG_TRANSCRIPT_READ_COMMAND,
|
|
1236
|
+
cap: CODEX_APP_SERVER_THREADS_CAPABILITY,
|
|
1237
|
+
dangerous: false,
|
|
1238
|
+
handle: async (paramsJSON) => {
|
|
1239
|
+
const request = await bindRequest(paramsJSON);
|
|
1240
|
+
const action = readNodeTranscriptParams(request.params);
|
|
1241
|
+
try {
|
|
1242
|
+
return JSON.stringify(await readCodexCatalogTranscriptPage(request.control, action));
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
if (error instanceof CatalogParamsError) throw error;
|
|
1245
|
+
throw new Error("Codex app-server transcript is unavailable", { cause: error });
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
},
|
|
1249
|
+
createCodexTerminalNodeHostCommand(bindRequest),
|
|
1250
|
+
createCodexTerminalStartNodeHostCommand()
|
|
1251
|
+
];
|
|
1252
|
+
return process.env.OPENCLAW_NODE_EXEC_HOST?.trim().toLowerCase() === "app" ? commands.filter(({ command }) => command !== CODEX_CATALOG_TRANSCRIPT_READ_COMMAND) : commands;
|
|
1253
|
+
}
|
|
1254
|
+
function readNodeTranscriptParams(value) {
|
|
1255
|
+
if (!isRecord(value)) throw new CatalogParamsError("Codex session read parameters must be an object");
|
|
1256
|
+
requireOnlyKeys(value, /* @__PURE__ */ new Set([
|
|
1257
|
+
"threadId",
|
|
1258
|
+
"cursor",
|
|
1259
|
+
"limit"
|
|
1260
|
+
]));
|
|
1261
|
+
const threadId = readBoundedOptionalString(value, "threadId", 256);
|
|
1262
|
+
if (!threadId) throw new CatalogParamsError("threadId is required");
|
|
1263
|
+
const cursor = readBoundedOptionalString(value, "cursor", MAX_CURSOR_LENGTH);
|
|
1264
|
+
return {
|
|
1265
|
+
threadId,
|
|
1266
|
+
limit: readBoundedLimit(value.limit, "limit", 20, 50),
|
|
1267
|
+
...cursor ? { cursor } : {}
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
function readBoundedLimit(value, key, fallback, max) {
|
|
1271
|
+
if (value === void 0) return fallback;
|
|
1272
|
+
if (!Number.isInteger(value) || value < 1 || value > max) throw new CatalogParamsError(`${key} must be an integer from 1 to ${max}`);
|
|
1273
|
+
return value;
|
|
1274
|
+
}
|
|
1275
|
+
/** Reads the persisted transcript for a Gateway-local or paired-node Codex session. */
|
|
1276
|
+
async function readCodexSessionTranscript(params) {
|
|
1277
|
+
const cursor = readControlCursor(params.cursor, "transcript request");
|
|
1278
|
+
const limit = readBoundedLimit(params.limit, "limit", 20, 50);
|
|
1279
|
+
if (params.source || params.hostId === "gateway:local") {
|
|
1280
|
+
const page = await readCodexCatalogTranscriptPage(params.control, {
|
|
1281
|
+
threadId: params.threadId,
|
|
1282
|
+
limit,
|
|
1283
|
+
cursor
|
|
1284
|
+
});
|
|
1285
|
+
return {
|
|
1286
|
+
hostId: params.hostId,
|
|
1287
|
+
label: params.source?.label ?? "Local Codex",
|
|
1288
|
+
threadId: params.threadId,
|
|
1289
|
+
...page
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
const nodeId = params.hostId.slice(5);
|
|
1293
|
+
const node = (await params.runtime.nodes.list()).nodes.find((candidate) => candidate.nodeId === nodeId && candidate.connected === true && candidate.commands?.includes("codex.appServer.thread.turns.list.v1"));
|
|
1294
|
+
if (!node) throw new CatalogParamsError("paired-node Codex session host is offline or unavailable");
|
|
1295
|
+
const invoke = async (command, request) => unwrapNodeInvokePayload(await params.runtime.nodes.invoke({
|
|
1296
|
+
nodeId,
|
|
1297
|
+
command,
|
|
1298
|
+
params: {
|
|
1299
|
+
agentId: params.agentId,
|
|
1300
|
+
threadId: params.threadId,
|
|
1301
|
+
...request
|
|
1302
|
+
},
|
|
1303
|
+
timeoutMs: NODE_INVOKE_TIMEOUT_MS,
|
|
1304
|
+
scopes: ["operator.write"]
|
|
1305
|
+
}));
|
|
1306
|
+
const page = node.commands?.includes("codex.sessionCatalog.transcript.read.v1") ? parseCodexCatalogTranscriptPage(await invoke(CODEX_CATALOG_TRANSCRIPT_READ_COMMAND, {
|
|
1307
|
+
cursor,
|
|
1308
|
+
limit
|
|
1309
|
+
})) : await readLegacyCodexTranscriptPage(async ({ cursor: turnCursor, limit: turnLimit }) => parseTranscriptPage(await invoke(CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND, {
|
|
1310
|
+
cursor: turnCursor,
|
|
1311
|
+
limit: turnLimit
|
|
1312
|
+
})), {
|
|
1313
|
+
threadId: params.threadId,
|
|
1314
|
+
cursor,
|
|
1315
|
+
limit
|
|
1316
|
+
});
|
|
1317
|
+
const { nodeLabel } = await import("./session-catalog-node-continue-Dowv8osw.mjs");
|
|
1318
|
+
return {
|
|
1319
|
+
hostId: params.hostId,
|
|
1320
|
+
label: nodeLabel(node),
|
|
1321
|
+
threadId: params.threadId,
|
|
1322
|
+
...page
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
//#endregion
|
|
1326
|
+
//#region extensions/codex/src/session-upstream-activity.ts
|
|
1327
|
+
const CODEX_UPSTREAM_TURN_LIMIT = 100;
|
|
1328
|
+
function readMarker(probe) {
|
|
1329
|
+
if (!isRecord(probe.marker)) return;
|
|
1330
|
+
const turnId = probe.marker.turnId;
|
|
1331
|
+
if (turnId !== null && typeof turnId !== "string") return;
|
|
1332
|
+
const count = probe.marker.userMessageCount;
|
|
1333
|
+
if (count !== void 0 && (!Number.isSafeInteger(count) || count < 0)) return;
|
|
1334
|
+
return {
|
|
1335
|
+
turnId,
|
|
1336
|
+
...count === void 0 ? {} : { userMessageCount: count }
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
function upstreamConnectionFingerprint(probe) {
|
|
1340
|
+
return isRecord(probe.upstreamRef) && typeof probe.upstreamRef.connectionFingerprint === "string" ? probe.upstreamRef.connectionFingerprint : void 0;
|
|
1341
|
+
}
|
|
1342
|
+
function classifyCodexUpstreamTurns(params) {
|
|
1343
|
+
const marker = readMarker(params.probe);
|
|
1344
|
+
if (!marker) return;
|
|
1345
|
+
const newest = params.turns[0];
|
|
1346
|
+
if (!newest?.id) return;
|
|
1347
|
+
const markerIndex = marker.turnId === null ? -1 : params.turns.findIndex((turn) => turn.id === marker.turnId);
|
|
1348
|
+
const candidateTurns = markerIndex < 0 ? params.turns : params.turns.slice(0, markerIndex + 1);
|
|
1349
|
+
const newestUserMessageCount = countUserMessages(newest);
|
|
1350
|
+
if (!(marker.turnId !== newest.id || marker.userMessageCount === void 0 || newestUserMessageCount > marker.userMessageCount)) return;
|
|
1351
|
+
const ownTexts = new Set(params.probe.ownRecentUserTexts);
|
|
1352
|
+
let humanTurns = 0;
|
|
1353
|
+
let occurredAt;
|
|
1354
|
+
for (const turn of candidateTurns) {
|
|
1355
|
+
const userMessages = turn.items.filter((item) => item.type === "userMessage");
|
|
1356
|
+
const alreadySeen = turn.id === marker.turnId ? marker.userMessageCount ?? userMessages.length : 0;
|
|
1357
|
+
for (const item of userMessages.slice(alreadySeen)) {
|
|
1358
|
+
const texts = normalizeUserMessageTexts(item);
|
|
1359
|
+
if (ownTexts.has(texts.join(" ")) || texts.length > 1 && texts.every((text) => ownTexts.has(text))) continue;
|
|
1360
|
+
humanTurns += 1;
|
|
1361
|
+
if (occurredAt === void 0) {
|
|
1362
|
+
const timestampSeconds = turn.completedAt ?? turn.startedAt;
|
|
1363
|
+
occurredAt = typeof timestampSeconds === "number" && Number.isFinite(timestampSeconds) ? timestampSeconds * 1e3 : params.now ?? Date.now();
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
const activityId = `${newest.id}:${newestUserMessageCount}`;
|
|
1368
|
+
return {
|
|
1369
|
+
kind: "activity",
|
|
1370
|
+
sessionKey: params.probe.sessionKey,
|
|
1371
|
+
humanTurns,
|
|
1372
|
+
nextMarker: {
|
|
1373
|
+
turnId: newest.id,
|
|
1374
|
+
userMessageCount: newestUserMessageCount
|
|
1375
|
+
},
|
|
1376
|
+
...humanTurns > 0 ? {
|
|
1377
|
+
occurredAt: occurredAt ?? params.now ?? Date.now(),
|
|
1378
|
+
dedupeId: activityId
|
|
1379
|
+
} : {}
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
function countUserMessages(turn) {
|
|
1383
|
+
return turn.items.filter((item) => item.type === "userMessage").length;
|
|
1384
|
+
}
|
|
1385
|
+
function normalizeUserMessageTexts(item) {
|
|
1386
|
+
const typed = item;
|
|
1387
|
+
const contentTexts = typed.content?.filter((input) => input.type === "text").map((input) => input.text.trim().replace(/\s+/g, " ")).filter(Boolean);
|
|
1388
|
+
return contentTexts?.length ? contentTexts : [(typed.text ?? "").trim().replace(/\s+/g, " ")];
|
|
1389
|
+
}
|
|
1390
|
+
async function checkCodexUpstreamActivity(probes, control, resolveThreadId = (probe) => probe.threadId) {
|
|
1391
|
+
return await control.withPinnedConnection(async (pinned) => {
|
|
1392
|
+
const activities = [];
|
|
1393
|
+
for (const probe of probes) {
|
|
1394
|
+
const fingerprint = upstreamConnectionFingerprint(probe);
|
|
1395
|
+
if (probe.upstreamKind !== "codex-app-server" || !fingerprint || fingerprint !== pinned.connectionFingerprint) continue;
|
|
1396
|
+
try {
|
|
1397
|
+
const threadId = resolveThreadId(probe);
|
|
1398
|
+
const page = await pinned.listTurnPage({
|
|
1399
|
+
threadId,
|
|
1400
|
+
limit: CODEX_UPSTREAM_TURN_LIMIT,
|
|
1401
|
+
sortDirection: "desc",
|
|
1402
|
+
itemsView: "full"
|
|
1403
|
+
});
|
|
1404
|
+
const marker = readMarker(probe);
|
|
1405
|
+
if (page.data.length === 0 && marker) {
|
|
1406
|
+
try {
|
|
1407
|
+
await pinned.readThread(threadId, false);
|
|
1408
|
+
} catch (error) {
|
|
1409
|
+
if (isCodexThreadReadMissingError(error, threadId)) activities.push({
|
|
1410
|
+
kind: "missing",
|
|
1411
|
+
sessionKey: probe.sessionKey
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
continue;
|
|
1415
|
+
}
|
|
1416
|
+
const activity = classifyCodexUpstreamTurns({
|
|
1417
|
+
probe,
|
|
1418
|
+
turns: page.data
|
|
1419
|
+
});
|
|
1420
|
+
if (activity) activities.push(activity);
|
|
1421
|
+
} catch {}
|
|
1422
|
+
}
|
|
1423
|
+
return activities;
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
function createChecker(params) {
|
|
1427
|
+
const resolveThreadId = (probe) => {
|
|
1428
|
+
const config = params.getRuntimeConfig();
|
|
1429
|
+
const sessionId = params.api.runtime.agent.session.getSessionEntry({
|
|
1430
|
+
agentId: probe.agentId,
|
|
1431
|
+
sessionKey: probe.sessionKey,
|
|
1432
|
+
readConsistency: "latest"
|
|
1433
|
+
})?.sessionId?.trim();
|
|
1434
|
+
if (!sessionId) return probe.threadId;
|
|
1435
|
+
const binding = params.bindingStore.read(sessionBindingIdentity({
|
|
1436
|
+
sessionId,
|
|
1437
|
+
sessionKey: probe.sessionKey,
|
|
1438
|
+
config
|
|
1439
|
+
}));
|
|
1440
|
+
return binding?.connectionScope === "supervision" && binding.supervisionSourceThreadId === probe.threadId ? binding.threadId : probe.threadId;
|
|
1441
|
+
};
|
|
1442
|
+
return async (probes) => {
|
|
1443
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1444
|
+
for (const probe of probes) {
|
|
1445
|
+
const fingerprint = upstreamConnectionFingerprint(probe);
|
|
1446
|
+
if (!fingerprint) continue;
|
|
1447
|
+
const control = await params.control.forUpstream(probe.agentId, fingerprint);
|
|
1448
|
+
if (!control) continue;
|
|
1449
|
+
const key = `${probe.agentId}\0${fingerprint}`;
|
|
1450
|
+
const group = groups.get(key) ?? {
|
|
1451
|
+
control,
|
|
1452
|
+
probes: []
|
|
1453
|
+
};
|
|
1454
|
+
group.probes.push(probe);
|
|
1455
|
+
groups.set(key, group);
|
|
1456
|
+
}
|
|
1457
|
+
return (await Promise.all([...groups.values()].map((group) => checkCodexUpstreamActivity(group.probes, group.control, resolveThreadId)))).flat();
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
//#endregion
|
|
1461
|
+
//#region extensions/codex/src/session-catalog-eligibility.ts
|
|
1462
|
+
/** Exact identity and native membership are independent of resident retention. */
|
|
1463
|
+
async function requireEligibleCodexThread(params) {
|
|
1464
|
+
const { requests, threadId } = params;
|
|
1465
|
+
const deadline = params.now() + requests.requestTimeoutMs;
|
|
1466
|
+
const unverified = () => new CatalogParamsError("Codex session eligibility could not be verified. Refresh the catalog and verify the session in its native Codex home before retrying.");
|
|
1467
|
+
const remaining = () => {
|
|
1468
|
+
const timeoutMs = Math.ceil(deadline - params.now());
|
|
1469
|
+
if (timeoutMs <= 0) throw unverified();
|
|
1470
|
+
return timeoutMs;
|
|
1471
|
+
};
|
|
1472
|
+
const verify = async () => {
|
|
1473
|
+
if (params.sourceHomeId && await params.managedThreads?.has(params.sourceHomeId, threadId)) throw unverified();
|
|
1474
|
+
remaining();
|
|
1475
|
+
const index = await requests.index();
|
|
1476
|
+
const root = params.localSessionsRoot;
|
|
1477
|
+
if (root && index.get(threadId)?.archived) throw unverified();
|
|
1478
|
+
const thread = await requests.readThread(threadId, false, remaining());
|
|
1479
|
+
remaining();
|
|
1480
|
+
if (thread.id !== threadId || thread.ephemeral === true || !isInteractiveThreadSource(thread.source)) throw unverified();
|
|
1481
|
+
if (!root) {
|
|
1482
|
+
const { CODEX_CATALOG_NATIVE_PAGE_LIMIT } = await import("./session-catalog-native-projection-ChV3cCtm.mjs").then((n) => n.i);
|
|
1483
|
+
const { CODEX_CATALOG_MAX_ROWS } = await import("./session-catalog-native-projection-ChV3cCtm.mjs").then((n) => n.U);
|
|
1484
|
+
let cursor;
|
|
1485
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
1486
|
+
for (;;) {
|
|
1487
|
+
const page = await requests.listThreads({
|
|
1488
|
+
archived: false,
|
|
1489
|
+
limit: CODEX_CATALOG_NATIVE_PAGE_LIMIT,
|
|
1490
|
+
modelProviders: [],
|
|
1491
|
+
sortKey: "recency_at",
|
|
1492
|
+
sortDirection: "desc",
|
|
1493
|
+
...thread.cwd ? { cwd: thread.cwd } : {},
|
|
1494
|
+
...cursor ? { cursor } : {}
|
|
1495
|
+
}, remaining());
|
|
1496
|
+
remaining();
|
|
1497
|
+
const listed = page.data.find((entry) => entry.id === threadId);
|
|
1498
|
+
if (listed) {
|
|
1499
|
+
if (listed.ephemeral === true || !isInteractiveThreadSource(listed.source)) throw unverified();
|
|
1500
|
+
break;
|
|
1501
|
+
}
|
|
1502
|
+
const nextCursor = readControlCursor(page.nextCursor, "next response");
|
|
1503
|
+
if (!nextCursor || seenCursors.has(nextCursor)) throw unverified();
|
|
1504
|
+
seenCursors.add(nextCursor);
|
|
1505
|
+
if (seenCursors.size > CODEX_CATALOG_MAX_ROWS) {
|
|
1506
|
+
const oldest = seenCursors.values().next().value;
|
|
1507
|
+
if (oldest !== void 0) seenCursors.delete(oldest);
|
|
1508
|
+
}
|
|
1509
|
+
cursor = nextCursor;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
if (root) {
|
|
1513
|
+
if (!thread.path) throw unverified();
|
|
1514
|
+
const metadata = await readCodexSessionMeta(root, thread.path, threadId);
|
|
1515
|
+
remaining();
|
|
1516
|
+
if (!metadata || !isInteractiveThreadSource(metadata.source) || metadata.originator === "openclaw") throw unverified();
|
|
1517
|
+
await index.upsertThread(thread);
|
|
1518
|
+
remaining();
|
|
1519
|
+
}
|
|
1520
|
+
return thread;
|
|
1521
|
+
};
|
|
1522
|
+
return await withTimeout(verify(), requests.requestTimeoutMs, "Codex session eligibility could not be verified", unverified);
|
|
1523
|
+
}
|
|
1524
|
+
//#endregion
|
|
1525
|
+
//#region extensions/codex/src/session-catalog-homes.ts
|
|
1526
|
+
/** Discovers path facts on demand; runtime projections belong only to their requesting owner. */
|
|
1527
|
+
function createCodexCatalogHomeResolver(params) {
|
|
1528
|
+
const env = params.env ?? process.env;
|
|
1529
|
+
const generations = /* @__PURE__ */ new WeakMap();
|
|
1530
|
+
let lastConfig = params.config;
|
|
1531
|
+
const currentConfig = () => lastConfig = params.getRuntimeConfig() ?? lastConfig;
|
|
1532
|
+
const generation = () => {
|
|
1533
|
+
const config = currentConfig();
|
|
1534
|
+
let current = generations.get(config);
|
|
1535
|
+
if (!current) {
|
|
1536
|
+
current = {
|
|
1537
|
+
config,
|
|
1538
|
+
assertCurrent: () => {
|
|
1539
|
+
if (currentConfig() !== config) throw new CatalogParamsError("Codex session catalog configuration changed; retry the request");
|
|
1540
|
+
},
|
|
1541
|
+
pluginConfig: params.getPluginConfig(),
|
|
1542
|
+
agentDirs: /* @__PURE__ */ new Map(),
|
|
1543
|
+
paths: /* @__PURE__ */ new Map(),
|
|
1544
|
+
directories: /* @__PURE__ */ new Set()
|
|
1545
|
+
};
|
|
1546
|
+
generations.set(config, current);
|
|
1547
|
+
}
|
|
1548
|
+
return current;
|
|
1549
|
+
};
|
|
1550
|
+
const agentIds = (snapshot) => snapshot.agentIds ??= listAgentIds(snapshot.config).toSorted((a, b) => a.localeCompare(b));
|
|
1551
|
+
const agentDir = (snapshot, agentId) => {
|
|
1552
|
+
let directory = snapshot.agentDirs.get(agentId);
|
|
1553
|
+
if (!directory) {
|
|
1554
|
+
directory = resolveAgentDir(snapshot.config, agentId, env);
|
|
1555
|
+
snapshot.agentDirs.set(agentId, directory);
|
|
1556
|
+
}
|
|
1557
|
+
return directory;
|
|
1558
|
+
};
|
|
1559
|
+
const homePath = async (snapshot, value) => {
|
|
1560
|
+
const cached = snapshot.paths.get(value);
|
|
1561
|
+
if (cached !== void 0) return cached;
|
|
1562
|
+
const resolved = path.resolve(value);
|
|
1563
|
+
const discovery = fs.realpath(resolved).catch(() => resolved).then(async (canonical) => {
|
|
1564
|
+
if (await fs.stat(canonical).then((stat) => stat.isDirectory(), () => false)) snapshot.directories.add(value);
|
|
1565
|
+
snapshot.paths.set(value, canonical);
|
|
1566
|
+
return canonical;
|
|
1567
|
+
});
|
|
1568
|
+
snapshot.paths.set(value, discovery);
|
|
1569
|
+
return discovery;
|
|
1570
|
+
};
|
|
1571
|
+
const sharedCandidates = (snapshot) => snapshot.candidates ??= (async () => {
|
|
1572
|
+
const candidates = [];
|
|
1573
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1574
|
+
const append = async (value, label) => {
|
|
1575
|
+
const codexHome = await homePath(snapshot, value);
|
|
1576
|
+
if (!snapshot.directories.has(value) || seen.has(codexHome)) return;
|
|
1577
|
+
seen.add(codexHome);
|
|
1578
|
+
candidates.push({
|
|
1579
|
+
codexHome,
|
|
1580
|
+
label: `Local Codex · ${label ?? path.basename(codexHome)}`
|
|
1581
|
+
});
|
|
1582
|
+
};
|
|
1583
|
+
for (const id of agentIds(snapshot)) {
|
|
1584
|
+
await scheduler.yield();
|
|
1585
|
+
await append(resolveCodexAppServerHomeDir(agentDir(snapshot, id)), id);
|
|
1586
|
+
if (candidates.length === 100) return candidates;
|
|
1587
|
+
}
|
|
1588
|
+
for (const entry of readCodexPluginConfig(snapshot.pluginConfig).sessionCatalog?.homes ?? []) {
|
|
1589
|
+
await scheduler.yield();
|
|
1590
|
+
const { path: home, label } = typeof entry === "string" ? { path: entry } : entry;
|
|
1591
|
+
await append(home, label);
|
|
1592
|
+
if (candidates.length === 100) break;
|
|
1593
|
+
}
|
|
1594
|
+
return candidates;
|
|
1595
|
+
})().then((candidates) => snapshot.candidates = candidates, (error) => {
|
|
1596
|
+
snapshot.candidates = void 0;
|
|
1597
|
+
throw error;
|
|
1598
|
+
});
|
|
1599
|
+
const prepareAgentHomes = async (snapshot, agentId, fleet) => {
|
|
1600
|
+
await scheduler.yield();
|
|
1601
|
+
snapshot.assertCurrent();
|
|
1602
|
+
if (!agentIds(snapshot).includes(agentId)) return [];
|
|
1603
|
+
const ownerAgentDir = agentDir(snapshot, agentId);
|
|
1604
|
+
const base = params.resolveRuntimeOptions({
|
|
1605
|
+
config: snapshot.config,
|
|
1606
|
+
pluginConfig: snapshot.pluginConfig,
|
|
1607
|
+
agentDir: ownerAgentDir,
|
|
1608
|
+
env
|
|
1609
|
+
});
|
|
1610
|
+
const processHomeConfigured = Boolean(env.CODEX_HOME?.trim());
|
|
1611
|
+
const candidates = [{
|
|
1612
|
+
codexHome: await homePath(snapshot, resolveCodexAppServerLocalHomeDir(base.start, ownerAgentDir, env)),
|
|
1613
|
+
label: "Local Codex",
|
|
1614
|
+
usesProcessHomeFallback: base.start.transport === "stdio" && base.start.homeScope === "user" && !processHomeConfigured
|
|
1615
|
+
}];
|
|
1616
|
+
if (fleet && base.start.transport === "stdio") {
|
|
1617
|
+
candidates.push({
|
|
1618
|
+
codexHome: await homePath(snapshot, resolveCodexAppServerUserHomeDir(env)),
|
|
1619
|
+
label: "Local Codex · user",
|
|
1620
|
+
usesProcessHomeFallback: !processHomeConfigured
|
|
1621
|
+
});
|
|
1622
|
+
const ownerHome = resolveCodexAppServerHomeDir(ownerAgentDir);
|
|
1623
|
+
const codexHome = await homePath(snapshot, ownerHome);
|
|
1624
|
+
if (snapshot.directories.has(ownerHome)) candidates.push({
|
|
1625
|
+
codexHome,
|
|
1626
|
+
label: `Local Codex · ${agentId}`
|
|
1627
|
+
});
|
|
1628
|
+
candidates.push(...await sharedCandidates(snapshot));
|
|
1629
|
+
}
|
|
1630
|
+
snapshot.assertCurrent();
|
|
1631
|
+
const homes = [];
|
|
1632
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1633
|
+
for (const candidate of candidates) {
|
|
1634
|
+
if (seen.has(candidate.codexHome)) continue;
|
|
1635
|
+
seen.add(candidate.codexHome);
|
|
1636
|
+
const sourceHomeId = codexCatalogHomeIdFromCanonicalPath(candidate.codexHome);
|
|
1637
|
+
const primary = homes.length === 0;
|
|
1638
|
+
homes.push({
|
|
1639
|
+
assertCurrent: snapshot.assertCurrent.bind(snapshot),
|
|
1640
|
+
sourceHomeId,
|
|
1641
|
+
hostId: primary ? CODEX_LOCAL_SESSION_HOST_ID : `${CODEX_LOCAL_SESSION_HOST_ID}:${sourceHomeId}`,
|
|
1642
|
+
label: candidate.label,
|
|
1643
|
+
agentDir: ownerAgentDir,
|
|
1644
|
+
appServer: primary ? base : {
|
|
1645
|
+
...base,
|
|
1646
|
+
start: {
|
|
1647
|
+
...base.start,
|
|
1648
|
+
homeScope: "user",
|
|
1649
|
+
env: {
|
|
1650
|
+
...base.start.env,
|
|
1651
|
+
CODEX_HOME: candidate.codexHome
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
},
|
|
1655
|
+
...base.connectionClass === "remote" ? {} : { localSessionsRoot: path.join(candidate.codexHome, "sessions") },
|
|
1656
|
+
usesProcessHomeFallback: candidate.usesProcessHomeFallback ?? false
|
|
1657
|
+
});
|
|
1658
|
+
if (homes.length === 100) break;
|
|
1659
|
+
}
|
|
1660
|
+
return homes;
|
|
1661
|
+
};
|
|
1662
|
+
setCodexCatalogConnectionHomeResolver(async (directory) => {
|
|
1663
|
+
const snapshot = generation();
|
|
1664
|
+
for (const id of agentIds(snapshot)) {
|
|
1665
|
+
await scheduler.yield();
|
|
1666
|
+
if (agentDir(snapshot, id) === directory) return prepareAgentHomes(snapshot, id, true);
|
|
1667
|
+
}
|
|
1668
|
+
snapshot.assertCurrent();
|
|
1669
|
+
return [];
|
|
1670
|
+
});
|
|
1671
|
+
return {
|
|
1672
|
+
forAgent: (agentId) => prepareAgentHomes(generation(), agentId, true),
|
|
1673
|
+
async forNode(requestedAgentId) {
|
|
1674
|
+
const snapshot = generation();
|
|
1675
|
+
const configured = readCodexPluginConfig(snapshot.pluginConfig).appServer;
|
|
1676
|
+
if (configured?.homeScope === "agent" || configured?.transport && configured.transport !== "stdio") {
|
|
1677
|
+
const agentId = resolveSessionAgentIdsStrict({
|
|
1678
|
+
config: snapshot.config,
|
|
1679
|
+
agentId: requestedAgentId
|
|
1680
|
+
}).sessionAgentId;
|
|
1681
|
+
const source = (await prepareAgentHomes(snapshot, agentId, false))[0];
|
|
1682
|
+
if (!source) throw new CatalogParamsError(`unknown Codex session catalog agent: ${agentId}`);
|
|
1683
|
+
return {
|
|
1684
|
+
...source,
|
|
1685
|
+
agentId,
|
|
1686
|
+
codexHome: resolveCodexAppServerLocalHomeDir(source.appServer.start, source.agentDir, env)
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
const codexHome = await homePath(snapshot, resolveCodexAppServerUserHomeDir(env));
|
|
1690
|
+
snapshot.assertCurrent();
|
|
1691
|
+
const appServer = params.resolveRuntimeOptions({
|
|
1692
|
+
pluginConfig: snapshot.pluginConfig,
|
|
1693
|
+
config: snapshot.config,
|
|
1694
|
+
env
|
|
1695
|
+
});
|
|
1696
|
+
return {
|
|
1697
|
+
assertCurrent: snapshot.assertCurrent.bind(snapshot),
|
|
1698
|
+
sourceHomeId: codexCatalogHomeIdFromCanonicalPath(codexHome),
|
|
1699
|
+
codexHome,
|
|
1700
|
+
localSessionsRoot: path.join(codexHome, "sessions"),
|
|
1701
|
+
appServer: {
|
|
1702
|
+
...appServer,
|
|
1703
|
+
start: {
|
|
1704
|
+
...appServer.start,
|
|
1705
|
+
env: {
|
|
1706
|
+
...appServer.start.env,
|
|
1707
|
+
CODEX_HOME: codexHome
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
//#endregion
|
|
1716
|
+
//#region extensions/codex/src/session-catalog-source-backoff.ts
|
|
1717
|
+
const INITIAL_BACKOFF_MS = 5e3;
|
|
1718
|
+
const MAX_BACKOFF_MS = 6e4;
|
|
1719
|
+
/** Source health is separate from query-keyed page sharing and cached page delivery. */
|
|
1720
|
+
var CodexCatalogSourceBackoff = class {
|
|
1721
|
+
constructor(now) {
|
|
1722
|
+
this.now = now;
|
|
1723
|
+
this.sourcesByConfig = /* @__PURE__ */ new WeakMap();
|
|
1724
|
+
}
|
|
1725
|
+
begin(config, agentId, sourceHomeId, scope) {
|
|
1726
|
+
let sources = this.sourcesByConfig.get(config);
|
|
1727
|
+
if (!sources) {
|
|
1728
|
+
sources = /* @__PURE__ */ new Map();
|
|
1729
|
+
this.sourcesByConfig.set(config, sources);
|
|
1730
|
+
}
|
|
1731
|
+
const key = JSON.stringify([agentId, sourceHomeId ?? null]);
|
|
1732
|
+
if (scope) return scope.attempt(sources, key, () => this.begin(config, agentId, sourceHomeId));
|
|
1733
|
+
let state = sources.get(key);
|
|
1734
|
+
if (!state) {
|
|
1735
|
+
state = {};
|
|
1736
|
+
sources.set(key, state);
|
|
1737
|
+
}
|
|
1738
|
+
const failure = state.failure;
|
|
1739
|
+
if (failure && (failure.probing || failure.retryAt > this.now())) return {
|
|
1740
|
+
allowed: false,
|
|
1741
|
+
error: failure.error
|
|
1742
|
+
};
|
|
1743
|
+
if (failure) failure.probing = true;
|
|
1744
|
+
return {
|
|
1745
|
+
allowed: true,
|
|
1746
|
+
resolved: () => {
|
|
1747
|
+
if (sources.get(key) === state) sources.set(key, {});
|
|
1748
|
+
},
|
|
1749
|
+
rejected: (error) => {
|
|
1750
|
+
if (sources.get(key) !== state) return;
|
|
1751
|
+
if (error instanceof CatalogParamsError || error instanceof Error && error.name === "AbortError" || error instanceof CodexAppServerRpcError && (error.code === -32600 || error.code === -32602)) {
|
|
1752
|
+
if (failure) failure.probing = false;
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
const delayMs = failure ? Math.min(failure.delayMs * 2 || INITIAL_BACKOFF_MS, MAX_BACKOFF_MS) : 0;
|
|
1756
|
+
sources.set(key, { failure: {
|
|
1757
|
+
error,
|
|
1758
|
+
delayMs,
|
|
1759
|
+
retryAt: this.now() + delayMs,
|
|
1760
|
+
probing: false
|
|
1761
|
+
} });
|
|
1762
|
+
},
|
|
1763
|
+
abandoned: () => {
|
|
1764
|
+
if (sources.get(key) === state && failure) failure.probing = false;
|
|
1765
|
+
}
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
};
|
|
1769
|
+
//#endregion
|
|
1770
|
+
//#region extensions/codex/src/session-catalog-control.ts
|
|
1771
|
+
function createCodexCatalogRequestSnapshot(requestTimeoutMs, request, index, beginList, catalogRead = false) {
|
|
1772
|
+
const read = (method, params, timeoutMs, observation) => {
|
|
1773
|
+
const scope = catalogRead ? void 0 : currentCodexCatalogListRequest();
|
|
1774
|
+
if (!scope) return request(method, params, timeoutMs, void 0, observation);
|
|
1775
|
+
return scope.read(timeoutMs ?? requestTimeoutMs, async (remaining) => {
|
|
1776
|
+
const attempt = beginList(scope);
|
|
1777
|
+
if (!attempt.allowed) throw attempt.error;
|
|
1778
|
+
return await request(method, params, remaining, void 0, observation);
|
|
1779
|
+
});
|
|
1780
|
+
};
|
|
1781
|
+
return {
|
|
1782
|
+
index,
|
|
1783
|
+
beginList,
|
|
1784
|
+
get requestTimeoutMs() {
|
|
1785
|
+
return catalogRead ? requestTimeoutMs : currentCodexCatalogListRequest()?.remaining(requestTimeoutMs) ?? requestTimeoutMs;
|
|
1786
|
+
},
|
|
1787
|
+
listThreads: (params, timeoutMs, observation) => read(CODEX_CONTROL_METHODS.listThreads, params, timeoutMs, observation),
|
|
1788
|
+
listThreadTurns: (params) => read(CODEX_CONTROL_METHODS.listThreadTurns, params),
|
|
1789
|
+
listThreadItems: (params) => read(CODEX_CONTROL_METHODS.listThreadItems, params),
|
|
1790
|
+
forkThread: (params, assertCurrent) => request(CODEX_CONTROL_METHODS.forkThread, assertCodexThreadForkParams(params), void 0, assertCurrent),
|
|
1791
|
+
readThread: async (threadId, includeTurns, timeoutMs) => (await read(CODEX_CONTROL_METHODS.readThread, {
|
|
1792
|
+
threadId,
|
|
1793
|
+
includeTurns
|
|
1794
|
+
}, timeoutMs)).thread,
|
|
1795
|
+
archiveThread: async (threadId, assertCurrent) => {
|
|
1796
|
+
await request(CODEX_CONTROL_METHODS.archiveThread, { threadId }, void 0, assertCurrent);
|
|
1797
|
+
}
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
function createCodexSessionCatalogControlFromRequests(params) {
|
|
1801
|
+
return {
|
|
1802
|
+
forkContext: params.forkContext,
|
|
1803
|
+
...params.clientId ? { clientId: params.clientId } : {},
|
|
1804
|
+
...params.connectionFingerprint ? { connectionFingerprint: params.connectionFingerprint } : {},
|
|
1805
|
+
withPinnedConnection: params.withPinnedConnection,
|
|
1806
|
+
async initialize() {
|
|
1807
|
+
await (await params.createRequestSnapshot().index()).initialize();
|
|
1808
|
+
},
|
|
1809
|
+
requireEligibleThread: (threadId) => requireEligibleCodexThread({
|
|
1810
|
+
threadId,
|
|
1811
|
+
requests: params.createRequestSnapshot(),
|
|
1812
|
+
localSessionsRoot: params.localSessionsRoot,
|
|
1813
|
+
sourceHomeId: params.sourceHomeId,
|
|
1814
|
+
managedThreads: params.managedThreads,
|
|
1815
|
+
now: params.now
|
|
1816
|
+
}),
|
|
1817
|
+
retireConnection: params.retireConnection,
|
|
1818
|
+
async listPage(pageParams) {
|
|
1819
|
+
readControlCursor(pageParams.cursor, "request");
|
|
1820
|
+
const query = readPageParams(pageParams);
|
|
1821
|
+
return await withCodexCatalogListRequest(async (request) => {
|
|
1822
|
+
const requests = params.createRequestSnapshot();
|
|
1823
|
+
const deadline = request.deadline(requests.requestTimeoutMs);
|
|
1824
|
+
return await (await withTimeout(requests.index(), request.remaining(requests.requestTimeoutMs), "Codex session catalog is still loading", () => new CodexCatalogLoadingError())).list(query, deadline);
|
|
1825
|
+
});
|
|
1826
|
+
},
|
|
1827
|
+
async listDescendantPage(listParams) {
|
|
1828
|
+
const requests = params.createRequestSnapshot();
|
|
1829
|
+
return await requests.listThreads(listParams, requests.requestTimeoutMs);
|
|
1830
|
+
},
|
|
1831
|
+
async readThread(threadId, includeTurns = false) {
|
|
1832
|
+
return await params.createRequestSnapshot().readThread(threadId, includeTurns);
|
|
1833
|
+
},
|
|
1834
|
+
async listTurnPage(listParams) {
|
|
1835
|
+
return await params.createRequestSnapshot().listThreadTurns(listParams);
|
|
1836
|
+
},
|
|
1837
|
+
listItemPage: (listParams) => params.createRequestSnapshot().listThreadItems(listParams),
|
|
1838
|
+
async forkThread(forkParams, assertCurrent) {
|
|
1839
|
+
const requests = params.createRequestSnapshot();
|
|
1840
|
+
const index = forkParams.ephemeral === true ? void 0 : await requests.index();
|
|
1841
|
+
const response = await requests.forkThread(forkParams, assertCurrent);
|
|
1842
|
+
if (index && !index.get(response.thread.id)) await index.upsertThread(response.thread);
|
|
1843
|
+
return response;
|
|
1844
|
+
},
|
|
1845
|
+
async archiveThread(threadId, assertCurrent) {
|
|
1846
|
+
const requests = params.createRequestSnapshot();
|
|
1847
|
+
const index = await requests.index();
|
|
1848
|
+
await requests.archiveThread(threadId, assertCurrent);
|
|
1849
|
+
index.archive(threadId);
|
|
1850
|
+
}
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
/** Builds the passive catalog over the Codex plugin's canonical shared client. */
|
|
1854
|
+
function createCodexSessionCatalogControl(params) {
|
|
1855
|
+
const now = params.now ?? Date.now;
|
|
1856
|
+
const sourceBackoff = new CodexCatalogSourceBackoff(now);
|
|
1857
|
+
const noConfig = {};
|
|
1858
|
+
const getPluginConfig = () => params.getPluginConfig();
|
|
1859
|
+
const homeResolver = createCodexCatalogHomeResolver({
|
|
1860
|
+
config: params.config ?? {},
|
|
1861
|
+
getRuntimeConfig: params.getRuntimeConfig,
|
|
1862
|
+
getPluginConfig: params.getPluginConfig,
|
|
1863
|
+
resolveRuntimeOptions: params.resolveRuntimeOptions,
|
|
1864
|
+
...params.env ? { env: params.env } : {}
|
|
1865
|
+
});
|
|
1866
|
+
const requestOptionsByConfig = /* @__PURE__ */ new WeakMap();
|
|
1867
|
+
const indexes = /* @__PURE__ */ new Map();
|
|
1868
|
+
const retiringState = /* @__PURE__ */ new Map();
|
|
1869
|
+
const directHomes = /* @__PURE__ */ new Map();
|
|
1870
|
+
let generation = params.getRuntimeConfig();
|
|
1871
|
+
let closed = false;
|
|
1872
|
+
let runBackground = (run) => run();
|
|
1873
|
+
let retiring = Promise.resolve();
|
|
1874
|
+
const residentFor = async (agentId, source) => {
|
|
1875
|
+
if (closed) throw new Error("Codex resident catalog is closed");
|
|
1876
|
+
const config = params.getRuntimeConfig();
|
|
1877
|
+
if (generation !== config) {
|
|
1878
|
+
generation = config;
|
|
1879
|
+
const closing = [];
|
|
1880
|
+
for (const [homeId, index] of indexes) {
|
|
1881
|
+
const writes = index.retire().finally(() => {
|
|
1882
|
+
if (retiringState.get(homeId) === writes) retiringState.delete(homeId);
|
|
1883
|
+
});
|
|
1884
|
+
retiringState.set(homeId, writes);
|
|
1885
|
+
closing.push(writes, index.close());
|
|
1886
|
+
}
|
|
1887
|
+
retiring = Promise.allSettled([retiring, ...closing]).then(() => void 0);
|
|
1888
|
+
indexes.clear();
|
|
1889
|
+
directHomes.clear();
|
|
1890
|
+
}
|
|
1891
|
+
const runtime = source?.appServer ?? params.resolveRuntimeOptions({ pluginConfig: getPluginConfig() });
|
|
1892
|
+
const requestOptions = resolveRequestOptions(runtime.start, agentId, source);
|
|
1893
|
+
const key = source?.sourceHomeId ?? agentId ?? "";
|
|
1894
|
+
let home = directHomes.get(key);
|
|
1895
|
+
if (!home) {
|
|
1896
|
+
home = codexCatalogResidentHomeKey({
|
|
1897
|
+
startOptions: requestOptions.startOptions,
|
|
1898
|
+
agentDir: requestOptions.agentDir,
|
|
1899
|
+
sourceHomeId: source?.sourceHomeId
|
|
1900
|
+
});
|
|
1901
|
+
directHomes.set(key, home);
|
|
1902
|
+
}
|
|
1903
|
+
const homeId = await home;
|
|
1904
|
+
await retiringState.get(homeId);
|
|
1905
|
+
if (closed || generation !== config) throw new Error("Codex catalog configuration changed");
|
|
1906
|
+
let index = indexes.get(homeId);
|
|
1907
|
+
if (!index) {
|
|
1908
|
+
const [{ CodexCatalogIndex }, { canReuseCodexCatalogPreview, projectCodexCatalogDeltaPage, projectCodexCatalogPage }] = await Promise.all([import("./session-catalog-index-B141u15s.mjs"), import("./session-catalog-projection-C1Bj9Jl-.mjs")]);
|
|
1909
|
+
source?.assertCurrent();
|
|
1910
|
+
if (closed || generation !== config) throw new Error("Codex catalog configuration changed");
|
|
1911
|
+
index = indexes.get(homeId);
|
|
1912
|
+
if (index) return index;
|
|
1913
|
+
const root = source?.localSessionsRoot ?? (runtime.connectionClass !== "remote" ? path.join(resolveCodexAppServerLocalHomeDir(requestOptions.startOptions, requestOptions.agentDir, params.env), "sessions") : void 0);
|
|
1914
|
+
let nativeAttempt;
|
|
1915
|
+
const readNativePage = async (query, remainingRows, project, foreground) => {
|
|
1916
|
+
const requests = createRequestSnapshot(agentId, source, true, query.useStateDbOnly ? (thread) => {
|
|
1917
|
+
const row = index?.get(thread.id);
|
|
1918
|
+
return canReuseCodexCatalogPreview(row, thread) ? row?.preview : void 0;
|
|
1919
|
+
} : void 0, remainingRows);
|
|
1920
|
+
const attempt = foreground ? requests.beginList(foreground) : query.cursor && nativeAttempt ? nativeAttempt : requests.beginList();
|
|
1921
|
+
if (!foreground) nativeAttempt = attempt;
|
|
1922
|
+
if (!attempt.allowed) throw attempt.error;
|
|
1923
|
+
const diagnostics = startCodexCatalogPageDiagnostics("cold");
|
|
1924
|
+
if (diagnostics) diagnostics.fields.controlRequestCalls = 1;
|
|
1925
|
+
const observation = startCodexCatalogControlRequestDiagnostics(diagnostics);
|
|
1926
|
+
let outcome = "rejected";
|
|
1927
|
+
try {
|
|
1928
|
+
const started = performance.now();
|
|
1929
|
+
let response;
|
|
1930
|
+
try {
|
|
1931
|
+
response = await requests.listThreads(query, foreground?.remaining(requests.requestTimeoutMs) ?? requests.requestTimeoutMs, observation);
|
|
1932
|
+
} finally {
|
|
1933
|
+
if (diagnostics) {
|
|
1934
|
+
const elapsed = performance.now() - started;
|
|
1935
|
+
diagnostics.fields.inclusiveControlRequestWaitMs = elapsed;
|
|
1936
|
+
diagnostics.fields.inclusiveControlRequestWaitMaxMs = elapsed;
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
foreground?.assertActive();
|
|
1940
|
+
const page = await project(response, diagnostics);
|
|
1941
|
+
foreground?.assertActive();
|
|
1942
|
+
outcome = "resolved";
|
|
1943
|
+
if (!foreground && !page.nextCursor) {
|
|
1944
|
+
attempt.resolved();
|
|
1945
|
+
nativeAttempt = void 0;
|
|
1946
|
+
}
|
|
1947
|
+
return page;
|
|
1948
|
+
} catch (error) {
|
|
1949
|
+
observation?.rejected();
|
|
1950
|
+
if (!foreground) {
|
|
1951
|
+
attempt.rejected(error);
|
|
1952
|
+
nativeAttempt = void 0;
|
|
1953
|
+
}
|
|
1954
|
+
throw error;
|
|
1955
|
+
} finally {
|
|
1956
|
+
observation?.close();
|
|
1957
|
+
diagnostics?.finish(outcome);
|
|
1958
|
+
}
|
|
1959
|
+
};
|
|
1960
|
+
index = new CodexCatalogIndex({
|
|
1961
|
+
homeId,
|
|
1962
|
+
requestTimeoutMs: runtime.requestTimeoutMs,
|
|
1963
|
+
runBackground: (run) => runBackground(run),
|
|
1964
|
+
localSessionsRoot: root,
|
|
1965
|
+
state: params.openResidentState?.(homeId),
|
|
1966
|
+
assertCurrent: () => {
|
|
1967
|
+
source?.assertCurrent();
|
|
1968
|
+
if (closed || params.getRuntimeConfig() !== config) throw new Error("Codex catalog configuration changed");
|
|
1969
|
+
},
|
|
1970
|
+
readNative: (query, remainingRows, foreground) => readNativePage(query, Math.min(64, remainingRows), async (response, diagnostics) => {
|
|
1971
|
+
const { sanitizeTerminalText } = await import("openclaw/plugin-sdk/text-chunking");
|
|
1972
|
+
const bounded = {
|
|
1973
|
+
...response,
|
|
1974
|
+
data: response.data.slice(0, remainingRows)
|
|
1975
|
+
};
|
|
1976
|
+
const projection = {
|
|
1977
|
+
localSessionsRoot: root,
|
|
1978
|
+
sanitize: sanitizeTerminalText,
|
|
1979
|
+
diagnostics
|
|
1980
|
+
};
|
|
1981
|
+
return query.useStateDbOnly ? await projectCodexCatalogDeltaPage(bounded, {
|
|
1982
|
+
...projection,
|
|
1983
|
+
getRow: (threadId) => index?.get(threadId)
|
|
1984
|
+
}) : await projectCodexCatalogPage(bounded, projection);
|
|
1985
|
+
}, foreground)
|
|
1986
|
+
});
|
|
1987
|
+
indexes.set(homeId, index);
|
|
1988
|
+
}
|
|
1989
|
+
return index;
|
|
1990
|
+
};
|
|
1991
|
+
const resolveRequestOptions = (startOptions, agentId, source) => {
|
|
1992
|
+
source?.assertCurrent();
|
|
1993
|
+
const runtimeConfig = params.getRuntimeConfig();
|
|
1994
|
+
const agentDir = source?.agentDir ?? (agentId ? resolveAgentDir(runtimeConfig ?? {}, agentId) : void 0);
|
|
1995
|
+
const resolvedStartOptions = source?.appServer.start ?? startOptions;
|
|
1996
|
+
if (!runtimeConfig) return {
|
|
1997
|
+
agentDir,
|
|
1998
|
+
config: void 0,
|
|
1999
|
+
startOptions: structuredClone(resolvedStartOptions)
|
|
2000
|
+
};
|
|
2001
|
+
let byAgent = requestOptionsByConfig.get(runtimeConfig);
|
|
2002
|
+
const cacheKey = `${agentId ?? ""}\0${source?.sourceHomeId ?? ""}`;
|
|
2003
|
+
const cached = byAgent?.get(cacheKey);
|
|
2004
|
+
if (cached) return cached;
|
|
2005
|
+
const resolved = {
|
|
2006
|
+
agentDir,
|
|
2007
|
+
config: structuredClone(runtimeConfig),
|
|
2008
|
+
startOptions: structuredClone(resolvedStartOptions)
|
|
2009
|
+
};
|
|
2010
|
+
if (!byAgent) {
|
|
2011
|
+
byAgent = /* @__PURE__ */ new Map();
|
|
2012
|
+
requestOptionsByConfig.set(runtimeConfig, byAgent);
|
|
2013
|
+
}
|
|
2014
|
+
byAgent.set(cacheKey, resolved);
|
|
2015
|
+
return resolved;
|
|
2016
|
+
};
|
|
2017
|
+
const createRequestSnapshot = (agentId, source, catalogPreview, catalogPreviewCache, catalogRows) => {
|
|
2018
|
+
const pluginConfig = getPluginConfig();
|
|
2019
|
+
const runtime = source?.appServer ?? params.resolveRuntimeOptions({ pluginConfig });
|
|
2020
|
+
const requestOptions = resolveRequestOptions(runtime.start, agentId, source);
|
|
2021
|
+
return createCodexCatalogRequestSnapshot(runtime.requestTimeoutMs, async (method, requestParams, timeoutMs, assertCurrent, observation) => {
|
|
2022
|
+
const { codexControlRequest } = await import("./command-rpc-DHFpn4le.mjs").then((n) => n.n);
|
|
2023
|
+
return await codexControlRequest(pluginConfig, method, requestParams, {
|
|
2024
|
+
...requestOptions,
|
|
2025
|
+
authProfileId: null,
|
|
2026
|
+
assertCurrent,
|
|
2027
|
+
...catalogPreview && method === CODEX_CONTROL_METHODS.listThreads ? {
|
|
2028
|
+
catalogPreview,
|
|
2029
|
+
catalogRows,
|
|
2030
|
+
...catalogPreviewCache ? { catalogPreviewCache } : {}
|
|
2031
|
+
} : {},
|
|
2032
|
+
...observation ? { controlObservation: observation } : {},
|
|
2033
|
+
...timeoutMs === void 0 ? {} : { timeoutMs }
|
|
2034
|
+
});
|
|
2035
|
+
}, () => residentFor(agentId, source), (request) => sourceBackoff.begin(requestOptions.config ?? noConfig, agentId, source?.sourceHomeId, request), catalogPreview === true);
|
|
2036
|
+
};
|
|
2037
|
+
const forRequest = (agentId, source) => {
|
|
2038
|
+
source?.assertCurrent();
|
|
2039
|
+
const withPinnedConnection = async (run) => {
|
|
2040
|
+
const pluginConfig = getPluginConfig();
|
|
2041
|
+
const runtime = source?.appServer ?? params.resolveRuntimeOptions({ pluginConfig });
|
|
2042
|
+
const { agentDir, config: runtimeConfig, startOptions } = resolveRequestOptions(runtime.start, agentId, source);
|
|
2043
|
+
let catalogIndex;
|
|
2044
|
+
const { getLeasedSharedCodexAppServerClient, releaseLeasedSharedCodexAppServerClient, retireSharedCodexAppServerClientIfCurrent } = await import("./shared-client-BpTtuLQ5.mjs").then((n) => n.y);
|
|
2045
|
+
const { resolveCodexAppServerClientInstanceId } = await import("./client-vXs2929Q.mjs").then((n) => n.n);
|
|
2046
|
+
const { requestCodexAppServerClientJson } = await import("./request-BioXJLYa.mjs").then((n) => n.a);
|
|
2047
|
+
const client = await getLeasedSharedCodexAppServerClient({
|
|
2048
|
+
agentDir,
|
|
2049
|
+
config: runtimeConfig,
|
|
2050
|
+
startOptions,
|
|
2051
|
+
authProfileId: null,
|
|
2052
|
+
timeoutMs: currentCodexCatalogListRequest()?.remaining(runtime.requestTimeoutMs) ?? runtime.requestTimeoutMs
|
|
2053
|
+
});
|
|
2054
|
+
try {
|
|
2055
|
+
const requests = createCodexCatalogRequestSnapshot(runtime.requestTimeoutMs, async (method, requestParams, timeoutMs, assertCurrent, observation) => await requestCodexAppServerClientJson({
|
|
2056
|
+
client,
|
|
2057
|
+
method,
|
|
2058
|
+
requestParams,
|
|
2059
|
+
config: runtimeConfig,
|
|
2060
|
+
timeoutMs: timeoutMs ?? runtime.requestTimeoutMs,
|
|
2061
|
+
assertCurrent,
|
|
2062
|
+
...observation ? { controlObservation: observation } : {}
|
|
2063
|
+
}), () => catalogIndex ??= residentFor(agentId, source), (request) => sourceBackoff.begin(runtimeConfig ?? noConfig, agentId, source?.sourceHomeId, request));
|
|
2064
|
+
const pinnedControl = createCodexSessionCatalogControlFromRequests({
|
|
2065
|
+
forkContext: agentDir ? {
|
|
2066
|
+
client,
|
|
2067
|
+
appServer: runtime,
|
|
2068
|
+
pluginConfig,
|
|
2069
|
+
agentDir,
|
|
2070
|
+
localSessionsRoot: source?.localSessionsRoot
|
|
2071
|
+
} : void 0,
|
|
2072
|
+
clientId: resolveCodexAppServerClientInstanceId(client),
|
|
2073
|
+
retireConnection: () => {
|
|
2074
|
+
retireSharedCodexAppServerClientIfCurrent(client);
|
|
2075
|
+
},
|
|
2076
|
+
connectionFingerprint: buildCodexAppServerConnectionFingerprint(runtime, agentDir),
|
|
2077
|
+
createRequestSnapshot: () => requests,
|
|
2078
|
+
...source?.localSessionsRoot ? { localSessionsRoot: source.localSessionsRoot } : {},
|
|
2079
|
+
sourceHomeId: source?.sourceHomeId,
|
|
2080
|
+
managedThreads: params.managedThreads,
|
|
2081
|
+
now,
|
|
2082
|
+
withPinnedConnection: async (nestedRun) => await nestedRun(pinnedControl)
|
|
2083
|
+
});
|
|
2084
|
+
return await run(pinnedControl);
|
|
2085
|
+
} finally {
|
|
2086
|
+
releaseLeasedSharedCodexAppServerClient(client);
|
|
2087
|
+
}
|
|
2088
|
+
};
|
|
2089
|
+
const control = createCodexSessionCatalogControlFromRequests({
|
|
2090
|
+
createRequestSnapshot: () => createRequestSnapshot(agentId, source),
|
|
2091
|
+
...source?.localSessionsRoot ? { localSessionsRoot: source.localSessionsRoot } : {},
|
|
2092
|
+
now,
|
|
2093
|
+
withPinnedConnection
|
|
2094
|
+
});
|
|
2095
|
+
return {
|
|
2096
|
+
...control,
|
|
2097
|
+
requireEligibleThread: (threadId) => withPinnedConnection((pinned) => pinned.requireEligibleThread(threadId)),
|
|
2098
|
+
async listPage(pageParams) {
|
|
2099
|
+
source?.assertCurrent();
|
|
2100
|
+
return await control.listPage(pageParams);
|
|
2101
|
+
}
|
|
2102
|
+
};
|
|
2103
|
+
};
|
|
2104
|
+
const forUpstream = async (agentId, connectionFingerprint) => {
|
|
2105
|
+
const source = (await homeResolver.forAgent(agentId)).find((home) => buildCodexAppServerConnectionFingerprint(home.appServer, home.agentDir) === connectionFingerprint);
|
|
2106
|
+
return source ? forRequest(agentId, source) : void 0;
|
|
2107
|
+
};
|
|
2108
|
+
return {
|
|
2109
|
+
async start() {
|
|
2110
|
+
const serviceScope = AsyncLocalStorage.snapshot();
|
|
2111
|
+
runBackground = (run) => serviceScope(run);
|
|
2112
|
+
for (const agentId of listAgentIds(params.getRuntimeConfig() ?? params.config ?? {})) for (const source of await homeResolver.forAgent(agentId)) {
|
|
2113
|
+
if (source.usesProcessHomeFallback) continue;
|
|
2114
|
+
forRequest(agentId, source).initialize().catch((error) => embeddedAgentLog.warn("Codex catalog hydration failed", { error }));
|
|
2115
|
+
}
|
|
2116
|
+
},
|
|
2117
|
+
async stop() {
|
|
2118
|
+
closed = true;
|
|
2119
|
+
await Promise.allSettled([...indexes.values()].map((index) => index.close()));
|
|
2120
|
+
await retiring;
|
|
2121
|
+
indexes.clear();
|
|
2122
|
+
directHomes.clear();
|
|
2123
|
+
},
|
|
2124
|
+
forRequest,
|
|
2125
|
+
forUpstream,
|
|
2126
|
+
homesForAgent: homeResolver.forAgent,
|
|
2127
|
+
async forNode(agentId) {
|
|
2128
|
+
const source = await homeResolver.forNode(agentId);
|
|
2129
|
+
return {
|
|
2130
|
+
control: forRequest(source.agentId, source),
|
|
2131
|
+
sourceHomeId: source.sourceHomeId,
|
|
2132
|
+
codexHome: source.codexHome
|
|
2133
|
+
};
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
//#endregion
|
|
2138
|
+
//#region extensions/codex/src/session-catalog.ts
|
|
2139
|
+
/** Allows read-only catalog and transcript commands on supported paired-node platforms. */
|
|
2140
|
+
function createCodexSessionCatalogNodeInvokePolicies() {
|
|
2141
|
+
return [{
|
|
2142
|
+
commands: [
|
|
2143
|
+
CODEX_APP_SERVER_THREADS_LIST_COMMAND,
|
|
2144
|
+
CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND,
|
|
2145
|
+
CODEX_CATALOG_TRANSCRIPT_READ_COMMAND,
|
|
2146
|
+
CODEX_TERMINAL_RESUME_COMMAND,
|
|
2147
|
+
CODEX_TERMINAL_START_COMMAND
|
|
2148
|
+
],
|
|
2149
|
+
defaultPlatforms: [
|
|
2150
|
+
"macos",
|
|
2151
|
+
"linux",
|
|
2152
|
+
"windows"
|
|
2153
|
+
],
|
|
2154
|
+
handle: (context) => {
|
|
2155
|
+
if (context.command === "codex.terminal.start.v1") return context.client?.scopes?.includes("operator.admin") && context.config.gateway?.cliAgents?.enabled === true && context.config.gateway?.terminal?.enabled !== false ? { ok: true } : {
|
|
2156
|
+
ok: false,
|
|
2157
|
+
message: "Native terminal start requires operator.admin and enabled CLI agents and terminals"
|
|
2158
|
+
};
|
|
2159
|
+
return context.command === "codex.terminal.resume.v1" ? { ok: true } : context.invokeNode();
|
|
2160
|
+
}
|
|
2161
|
+
}];
|
|
2162
|
+
}
|
|
2163
|
+
function toGenericCatalogHost(host, localTerminalAvailable) {
|
|
2164
|
+
const local = isLocalCodexCatalogHost(host.hostId);
|
|
2165
|
+
return {
|
|
2166
|
+
hostId: host.hostId,
|
|
2167
|
+
label: host.label,
|
|
2168
|
+
kind: host.kind,
|
|
2169
|
+
connected: host.connected,
|
|
2170
|
+
...host.nodeId ? { nodeId: host.nodeId } : {},
|
|
2171
|
+
sessions: host.sessions.map((session) => {
|
|
2172
|
+
const continuableStatus = !session.archived && (session.status === "idle" || session.status === "notLoaded");
|
|
2173
|
+
const canContinue = (local || host.canContinueCodex === true) && continuableStatus && isInteractiveThreadSource(session.source);
|
|
2174
|
+
const canArchive = local && continuableStatus && isInteractiveThreadSource(session.source);
|
|
2175
|
+
const canOpenTerminal = isInteractiveThreadSource(session.source) && (local ? localTerminalAvailable : host.canOpenTerminalCodex === true);
|
|
2176
|
+
const name = session.name ?? session.fallbackName;
|
|
2177
|
+
return {
|
|
2178
|
+
threadId: session.threadId,
|
|
2179
|
+
...session.sourceHomeId ? { sourceHomeId: session.sourceHomeId } : {},
|
|
2180
|
+
...name ? { name } : {},
|
|
2181
|
+
...session.cwd ? { cwd: session.cwd } : {},
|
|
2182
|
+
status: session.status,
|
|
2183
|
+
...session.createdAt != null ? { createdAt: session.createdAt } : {},
|
|
2184
|
+
...session.updatedAt != null ? { updatedAt: session.updatedAt } : {},
|
|
2185
|
+
...session.recencyAt != null ? { recencyAt: session.recencyAt } : {},
|
|
2186
|
+
...session.source ? { source: session.source } : {},
|
|
2187
|
+
...session.modelProvider ? { modelProvider: session.modelProvider } : {},
|
|
2188
|
+
...session.cliVersion ? { cliVersion: session.cliVersion } : {},
|
|
2189
|
+
...session.gitBranch ? { gitBranch: session.gitBranch } : {},
|
|
2190
|
+
archived: session.archived,
|
|
2191
|
+
...session.sessionKey ? { sessionKey: session.sessionKey } : {},
|
|
2192
|
+
canContinue,
|
|
2193
|
+
canArchive,
|
|
2194
|
+
canOpenTerminal
|
|
2195
|
+
};
|
|
2196
|
+
}),
|
|
2197
|
+
...host.nextCursor ? { nextCursor: host.nextCursor } : {},
|
|
2198
|
+
...host.error ? { error: host.error } : {}
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
function isLocalCodexCatalogHost(hostId) {
|
|
2202
|
+
return hostId === "gateway:local" || hostId.startsWith(`gateway:local:`);
|
|
2203
|
+
}
|
|
2204
|
+
function resolveLocalCatalogHomeForThread(params) {
|
|
2205
|
+
if (params.homes.length === 0) throw new CatalogParamsError("local Codex sessions are unavailable in isolated state");
|
|
2206
|
+
const exact = params.sourceHomeId ? params.homes.filter((home) => home.sourceHomeId === params.sourceHomeId) : params.homes.filter((home) => home.hostId === params.hostId);
|
|
2207
|
+
if (exact.length === 0 || params.sourceHomeId && exact[0]?.hostId !== params.hostId) throw new CatalogParamsError("Codex session source home is unavailable");
|
|
2208
|
+
return exact[0];
|
|
2209
|
+
}
|
|
2210
|
+
function withCatalogListScope(initialize) {
|
|
2211
|
+
let create = initialize;
|
|
2212
|
+
let operation;
|
|
2213
|
+
let scope;
|
|
2214
|
+
let running = false;
|
|
2215
|
+
let closed = false;
|
|
2216
|
+
let completed = false;
|
|
2217
|
+
return {
|
|
2218
|
+
async next() {
|
|
2219
|
+
if (closed || running) throw new Error("Codex catalog list operation cannot advance");
|
|
2220
|
+
running = true;
|
|
2221
|
+
try {
|
|
2222
|
+
scope ??= createCodexCatalogListScope();
|
|
2223
|
+
return await scope.run(async () => {
|
|
2224
|
+
if (create) {
|
|
2225
|
+
const initializeOnce = create;
|
|
2226
|
+
create = void 0;
|
|
2227
|
+
operation = await initializeOnce();
|
|
2228
|
+
}
|
|
2229
|
+
if (!operation) throw new Error("Codex catalog list operation did not initialize");
|
|
2230
|
+
const step = await operation.next();
|
|
2231
|
+
completed ||= step.done;
|
|
2232
|
+
return step;
|
|
2233
|
+
});
|
|
2234
|
+
} finally {
|
|
2235
|
+
running = false;
|
|
2236
|
+
}
|
|
2237
|
+
},
|
|
2238
|
+
close() {
|
|
2239
|
+
if (closed) return;
|
|
2240
|
+
if (running) throw new Error("Cannot close an active Codex catalog list step");
|
|
2241
|
+
closed = true;
|
|
2242
|
+
create = void 0;
|
|
2243
|
+
const current = operation;
|
|
2244
|
+
operation = void 0;
|
|
2245
|
+
const currentScope = scope;
|
|
2246
|
+
scope = void 0;
|
|
2247
|
+
try {
|
|
2248
|
+
if (currentScope) currentScope.run(() => current?.close());
|
|
2249
|
+
} finally {
|
|
2250
|
+
currentScope?.finish(completed ? "resolved" : "rejected");
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
2255
|
+
function catalogHostMapper(localTerminalAvailable, localHomes) {
|
|
2256
|
+
return (host) => {
|
|
2257
|
+
const diagnostics = currentCodexCatalogListDiagnostics();
|
|
2258
|
+
const started = diagnostics ? performance.now() : 0;
|
|
2259
|
+
try {
|
|
2260
|
+
return {
|
|
2261
|
+
...toGenericCatalogHost(host, localTerminalAvailable),
|
|
2262
|
+
canStartTerminal: host.kind === "gateway" ? localTerminalAvailable && host.hostId === "gateway:local" && localHomes.some((home) => home.hostId === host.hostId && home.appServer.start.transport === "stdio") : host.canStartTerminal === true
|
|
2263
|
+
};
|
|
2264
|
+
} finally {
|
|
2265
|
+
if (diagnostics && !diagnostics.closed) diagnostics.fields.mappingMs = (diagnostics.fields.mappingMs ?? 0) + performance.now() - started;
|
|
2266
|
+
}
|
|
2267
|
+
};
|
|
2268
|
+
}
|
|
2269
|
+
function mappedHostPublisher(onHost, mapHost) {
|
|
2270
|
+
return (host) => onHost(mapHost(host));
|
|
2271
|
+
}
|
|
2272
|
+
function mapCatalogListOperation(operation, mapHost) {
|
|
2273
|
+
return {
|
|
2274
|
+
async next() {
|
|
2275
|
+
const step = await operation.next();
|
|
2276
|
+
return step.done ? {
|
|
2277
|
+
done: true,
|
|
2278
|
+
hosts: step.hosts.map(mapHost)
|
|
2279
|
+
} : step;
|
|
2280
|
+
},
|
|
2281
|
+
close: () => operation.close()
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
function registerCodexSessionCatalog(params) {
|
|
2285
|
+
const catalogHomes = async (agentId, allowProcessHomeFallback) => {
|
|
2286
|
+
const homes = await params.control.homesForAgent(agentId);
|
|
2287
|
+
return allowProcessHomeFallback === false ? homes.filter((home) => !home.usesProcessHomeFallback) : homes;
|
|
2288
|
+
};
|
|
2289
|
+
const resolveRequestAgentId = (agentId) => resolveSessionAgentIdsStrict({
|
|
2290
|
+
config: params.getRuntimeConfig() ?? params.api.config,
|
|
2291
|
+
agentId
|
|
2292
|
+
}).sessionAgentId;
|
|
2293
|
+
const bindRequest = async (request) => {
|
|
2294
|
+
const agentId = resolveRequestAgentId(request.agentId);
|
|
2295
|
+
const source = isLocalCodexCatalogHost(request.hostId) ? resolveLocalCatalogHomeForThread({
|
|
2296
|
+
homes: [...await catalogHomes(agentId, request.allowProcessHomeFallback)],
|
|
2297
|
+
hostId: request.hostId,
|
|
2298
|
+
...request.sourceHomeId ? { sourceHomeId: request.sourceHomeId } : {}
|
|
2299
|
+
}) : void 0;
|
|
2300
|
+
return {
|
|
2301
|
+
agentId,
|
|
2302
|
+
source,
|
|
2303
|
+
control: params.control.forRequest(agentId, source)
|
|
2304
|
+
};
|
|
2305
|
+
};
|
|
2306
|
+
const bindLocalRequest = async (request) => {
|
|
2307
|
+
const bound = await bindRequest(request);
|
|
2308
|
+
if (!bound.source) throw new CatalogParamsError("Codex session catalog hostId is invalid");
|
|
2309
|
+
return {
|
|
2310
|
+
...bound,
|
|
2311
|
+
source: bound.source
|
|
2312
|
+
};
|
|
2313
|
+
};
|
|
2314
|
+
const checkUpstreamActivity = createChecker(params);
|
|
2315
|
+
const createListOperation = (query) => withCatalogListScope(async () => {
|
|
2316
|
+
const { agentId: requestedAgentId, allowProcessHomeFallback, listNodes, onHost, waitUntil, signal, sessionEntries, ...gatewayQuery } = query;
|
|
2317
|
+
const agentId = resolveRequestAgentId(requestedAgentId);
|
|
2318
|
+
const selectedQuery = readGatewayParams(gatewayQuery);
|
|
2319
|
+
const localHomes = selectedQuery.hostIds && !selectedQuery.hostIds.some(isLocalCodexCatalogHost) ? [] : [...await catalogHomes(agentId, allowProcessHomeFallback)];
|
|
2320
|
+
const mapHost = catalogHostMapper(resolveLocalCodexTerminalExecutable() !== void 0, localHomes);
|
|
2321
|
+
return mapCatalogListOperation(createCodexSessionCatalogListOperation({
|
|
2322
|
+
agentId,
|
|
2323
|
+
bindingStore: params.bindingStore,
|
|
2324
|
+
config: params.getRuntimeConfig(),
|
|
2325
|
+
runtime: params.api.runtime,
|
|
2326
|
+
control: params.control,
|
|
2327
|
+
query: selectedQuery,
|
|
2328
|
+
listNodes,
|
|
2329
|
+
waitUntil,
|
|
2330
|
+
signal,
|
|
2331
|
+
sessionEntries,
|
|
2332
|
+
localHomes,
|
|
2333
|
+
...onHost ? { onHost: mappedHostPublisher(onHost, mapHost) } : {}
|
|
2334
|
+
}), mapHost);
|
|
2335
|
+
});
|
|
2336
|
+
const provider = {
|
|
2337
|
+
id: "codex",
|
|
2338
|
+
label: "Codex",
|
|
2339
|
+
supportsProcessHomeIsolation: true,
|
|
2340
|
+
resolveCreateSession: ({ agentId }) => resolveCodexCatalogCreateSession(params.api.runtime.modelConfig, params.getRuntimeConfig() ?? params.api.config, agentId),
|
|
2341
|
+
list: (query) => runCatalogListInline(createListOperation(query)),
|
|
2342
|
+
createListOperation,
|
|
2343
|
+
read: async (request) => {
|
|
2344
|
+
const { agentId, source, control } = await bindRequest(request);
|
|
2345
|
+
return await readCodexSessionTranscript({
|
|
2346
|
+
agentId,
|
|
2347
|
+
runtime: params.api.runtime,
|
|
2348
|
+
control,
|
|
2349
|
+
hostId: request.hostId,
|
|
2350
|
+
threadId: request.threadId,
|
|
2351
|
+
cursor: request.cursor,
|
|
2352
|
+
limit: request.limit ?? 20,
|
|
2353
|
+
...source ? { source } : {}
|
|
2354
|
+
});
|
|
2355
|
+
},
|
|
2356
|
+
continueSession: async (request) => {
|
|
2357
|
+
const config = params.getRuntimeConfig();
|
|
2358
|
+
if (!config) throw new Error("OpenClaw runtime config is unavailable");
|
|
2359
|
+
if (request.hostId.startsWith("node:")) return await continueNodeCodexSession({
|
|
2360
|
+
agentId: resolveRequestAgentId(request.agentId),
|
|
2361
|
+
api: params.api,
|
|
2362
|
+
config,
|
|
2363
|
+
hostId: request.hostId,
|
|
2364
|
+
threadId: request.threadId,
|
|
2365
|
+
clientScopes: request.clientScopes
|
|
2366
|
+
});
|
|
2367
|
+
if (!isLocalCodexCatalogHost(request.hostId)) throw new CatalogParamsError("Codex session catalog hostId is invalid");
|
|
2368
|
+
const { agentId, source, control } = await bindLocalRequest(request);
|
|
2369
|
+
source.assertCurrent();
|
|
2370
|
+
let upstreamBaseline;
|
|
2371
|
+
const continued = await continueLocalCodexSession({
|
|
2372
|
+
agentId,
|
|
2373
|
+
api: params.api,
|
|
2374
|
+
bindingStore: params.bindingStore,
|
|
2375
|
+
config,
|
|
2376
|
+
control,
|
|
2377
|
+
threadId: request.threadId,
|
|
2378
|
+
hostId: source.hostId,
|
|
2379
|
+
sourceHomeId: source.sourceHomeId,
|
|
2380
|
+
...source.hostId === "gateway:local" ? { allowLegacy: true } : {},
|
|
2381
|
+
onContinued: (baseline) => {
|
|
2382
|
+
upstreamBaseline = baseline;
|
|
2383
|
+
}
|
|
2384
|
+
});
|
|
2385
|
+
return codexUpstreamContinueResult(continued.sessionKey, request.threadId, upstreamBaseline);
|
|
2386
|
+
},
|
|
2387
|
+
checkUpstreamActivity: async (probes, policy) => {
|
|
2388
|
+
const filtered = [];
|
|
2389
|
+
for (const probe of probes) if (!isLocalCodexCatalogHost(probe.hostId) || policy?.allowProcessHomeFallback !== false || (await catalogHomes(probe.agentId, false)).some((home) => home.hostId === probe.hostId)) filtered.push(probe);
|
|
2390
|
+
return checkUpstreamActivity(filtered);
|
|
2391
|
+
},
|
|
2392
|
+
archive: async (request) => {
|
|
2393
|
+
if (request.confirmNoOtherRunner !== true) throw new CatalogParamsError("archive requires confirmation that no other runner is active");
|
|
2394
|
+
if (!isLocalCodexCatalogHost(request.hostId)) throw new CatalogParamsError("paired-node Codex sessions are view-only");
|
|
2395
|
+
const config = params.getRuntimeConfig();
|
|
2396
|
+
if (!config) throw new Error("OpenClaw runtime config is unavailable");
|
|
2397
|
+
const { agentId, source, control } = await bindLocalRequest(request);
|
|
2398
|
+
source.assertCurrent();
|
|
2399
|
+
await archiveLocalCodexSession({
|
|
2400
|
+
agentId,
|
|
2401
|
+
bindingStore: params.bindingStore,
|
|
2402
|
+
config,
|
|
2403
|
+
control,
|
|
2404
|
+
runtime: params.api.runtime,
|
|
2405
|
+
threadId: request.threadId,
|
|
2406
|
+
hostId: source.hostId,
|
|
2407
|
+
sourceHomeId: source.sourceHomeId,
|
|
2408
|
+
...source.hostId === "gateway:local" ? { allowLegacy: true } : {}
|
|
2409
|
+
});
|
|
2410
|
+
return { ok: true };
|
|
2411
|
+
},
|
|
2412
|
+
openTerminal: async (request) => {
|
|
2413
|
+
const { agentId, source, control } = await bindRequest(request);
|
|
2414
|
+
return await openCodexCatalogTerminal({
|
|
2415
|
+
api: params.api,
|
|
2416
|
+
control,
|
|
2417
|
+
getPluginConfig: params.getPluginConfig,
|
|
2418
|
+
getRuntimeConfig: params.getRuntimeConfig,
|
|
2419
|
+
resolveRuntimeOptions: params.resolveRuntimeOptions,
|
|
2420
|
+
...source ? { source } : {},
|
|
2421
|
+
...request,
|
|
2422
|
+
agentId
|
|
2423
|
+
});
|
|
2424
|
+
},
|
|
2425
|
+
startTerminalSession: async (request) => {
|
|
2426
|
+
if (!request.nodeId && request.hostId && request.hostId !== "gateway:local") throw new CatalogParamsError("Codex terminal host is unavailable; select the local machine or a connected node");
|
|
2427
|
+
const source = request.nodeId ? void 0 : resolveLocalCatalogHomeForThread({
|
|
2428
|
+
homes: [...await catalogHomes(request.agentId, request.allowProcessHomeFallback)],
|
|
2429
|
+
hostId: request.hostId ?? "gateway:local"
|
|
2430
|
+
});
|
|
2431
|
+
if (source && source.appServer.start.transport !== "stdio") throw new CatalogParamsError("Native terminal start requires a local Codex source");
|
|
2432
|
+
return await startCodexCatalogTerminal({
|
|
2433
|
+
getPluginConfig: params.getPluginConfig,
|
|
2434
|
+
getRuntimeConfig: params.getRuntimeConfig,
|
|
2435
|
+
resolveRuntimeOptions: params.resolveRuntimeOptions,
|
|
2436
|
+
...request,
|
|
2437
|
+
source
|
|
2438
|
+
});
|
|
2439
|
+
}
|
|
2440
|
+
};
|
|
2441
|
+
params.api.registerSessionCatalog(provider);
|
|
2442
|
+
}
|
|
2443
|
+
const codexSessionCatalogRuntime = {
|
|
2444
|
+
register: registerCodexSessionCatalog,
|
|
2445
|
+
list: listCodexSessionCatalog,
|
|
2446
|
+
readTranscript: readCodexSessionTranscript,
|
|
2447
|
+
continueLocal: continueLocalCodexSession,
|
|
2448
|
+
continueNode: continueNodeCodexSession,
|
|
2449
|
+
archiveLocal: archiveLocalCodexSession
|
|
2450
|
+
};
|
|
2451
|
+
async function continueLocalCodexSession(...args) {
|
|
2452
|
+
const { continueLocalCodexSession: run } = await import("./session-catalog-adoption-CR_whkEW.mjs");
|
|
2453
|
+
return run(...args);
|
|
2454
|
+
}
|
|
2455
|
+
async function archiveLocalCodexSession(...args) {
|
|
2456
|
+
const { archiveLocalCodexSession: run } = await import("./session-catalog-archive-CwZaPacf.mjs");
|
|
2457
|
+
return run(...args);
|
|
2458
|
+
}
|
|
2459
|
+
async function continueNodeCodexSession(...args) {
|
|
2460
|
+
const { continueNodeCodexSession: run } = await import("./session-catalog-node-continue-Dowv8osw.mjs");
|
|
2461
|
+
return run(...args);
|
|
2462
|
+
}
|
|
2463
|
+
//#endregion
|
|
2464
|
+
export { withCodexCatalogListRequest as a, createCodexSessionCatalogNodeHostCommands as i, createCodexSessionCatalogNodeInvokePolicies as n, lookupNodeCodexCatalogRecord as o, createCodexSessionCatalogControl as r, CodexCatalogAvailability as s, codexSessionCatalogRuntime as t };
|