@openclaw/codex 2026.9.3 → 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-B6el6hdO.js → .setup/apply-CJ0ZfmBG.mjs} +12 -13
- package/dist/.setup/async-questions-DdhOCwGi.mjs +27 -0
- package/dist/{attempt-client-cleanup-D6cR54TV.js → .setup/attempt-client-cleanup-BpYKdTA0.mjs} +30 -211
- package/dist/{attempt-context-AO4DSjE0.js → .setup/attempt-context-VXVwTCfS.mjs} +6 -6
- package/dist/.setup/attempt-terminal-gZrxQ35N.mjs +5 -0
- package/dist/{auth-binding-Co2Vfcf_.js → .setup/auth-binding-DhLWa_7K.mjs} +2 -2
- package/dist/{auth-cache-key-Kq6oerr6.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-lRpmZHFv.js → .setup/bounded-turn-BtlZcivC.mjs} +19 -14
- package/dist/{build-state-6RL6qohz.js → .setup/build-state-Xx9ybZqa.mjs} +1 -1
- package/dist/{capabilities-BeqLPbtM.js → .setup/capabilities-_vXcZGSx.mjs} +1 -1
- package/dist/{client-BEKu4yDx.js → .setup/client-vXs2929Q.mjs} +355 -164
- package/dist/{command-dispatch-CisT9Jqq.js → .setup/command-dispatch-naHIZwwF.mjs} +3 -3
- package/dist/{command-formatters-DeTfrapr.js → .setup/command-formatters-CGEkdawF.mjs} +4 -3
- package/dist/{command-handlers-BfIsJEuA.js → .setup/command-handlers-BtAJEW1X.mjs} +812 -152
- package/dist/{command-rpc-BMqRQOFX.js → .setup/command-rpc-DHFpn4le.mjs} +37 -21
- package/dist/{compact-CGJZ2rlb.js → .setup/compact-C3i3qlzj.mjs} +28 -43
- package/dist/{config-JDfTI4VI.js → .setup/config-CedDWjM-.mjs} +5 -4
- package/dist/{config-options-jnIFvwxD.js → .setup/config-options-BsanJ8QG.mjs} +9 -421
- package/dist/{protocol-CAAcVIKF.js → .setup/config-parsing-CcB9iPoq.mjs} +9 -51
- package/dist/{config-runtime-BWf6JLWG.js → .setup/config-runtime-CeAhl_h4.mjs} +2 -2
- package/dist/.setup/config-security-BRQ2mgW9.mjs +465 -0
- package/dist/{config-utils-C57JiJrU.js → .setup/config-utils-DujwEnhg.mjs} +2 -4
- package/dist/{conversation-binding-CxOlHiix.js → .setup/conversation-binding-DhzrpTM6.mjs} +60 -47
- package/dist/{conversation-binding-data-BiodpIzq.js → .setup/conversation-binding-data-CrcyZk6O.mjs} +1 -1
- package/dist/{conversation-control-CUCrSIQI.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-D-JBoaLt.js → .setup/dynamic-tools-D16lOp67.mjs} +168 -61
- package/dist/{effective-mcp-catalog-J_5Ep5Um.js → .setup/effective-mcp-catalog-K6kLCN8X.mjs} +3 -3
- package/dist/{ephemeral-turn-DSwzP8YY.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/.setup/event-projector-items-DyTQ_XqL.mjs +200 -0
- package/dist/{attempt-notifications--H8zXR5-.js → .setup/event-projector-usage-C7Ntzs-f.mjs} +102 -9
- package/dist/{harness-FR6eQgyO.js → .setup/harness-rxaaoN-o.mjs} +90 -19
- package/dist/.setup/inference-context-fulhZuz8.mjs +94 -0
- package/dist/.setup/inference-proxy-CC15HuSi.mjs +311 -0
- package/dist/{isolated-completion-DHBefrzg.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-DrjjRRUD.js → .setup/managed-binary-DP6Rib3P.mjs} +11 -2
- package/dist/{managed-thread-store-D1t5jh3G.js → .setup/managed-thread-store-BMqgThVL.mjs} +48 -14
- package/dist/{model-catalog-B_6M04Wo.js → .setup/model-catalog-DkvUuBey.mjs} +35 -10
- package/dist/{models-P9k2qAof.js → .setup/models-BDFgm_G-.mjs} +3 -3
- package/dist/.setup/native-auth-BLZGqFjw.mjs +57 -0
- package/dist/.setup/native-subagent-history-YXaL6dMd.mjs +135 -0
- package/dist/.setup/native-subagent-monitor-B1hxzIOy.mjs +5031 -0
- package/dist/.setup/native-subagent-task-ids-DsuOonKv.mjs +411 -0
- package/dist/{node-cli-sessions-rj0ysJwM.js → .setup/node-cli-sessions-Cl8b2ZF3.mjs} +1 -1
- package/dist/{node-exec-server.runtime-BY_ctYnT.js → .setup/node-exec-server.runtime-BaViBKNz.mjs} +5 -5
- package/dist/{plan-BMJdMOEF.js → .setup/plan-xD7WKYTM.mjs} +331 -79
- package/dist/{plugin-activation-BQK-f5sV.js → .setup/plugin-activation-r8nq5Jzp.mjs} +24 -19
- package/dist/{plugin-app-cache-key-CdSQzWyX.js → .setup/plugin-app-cache-key-BOxKP7db.mjs} +29 -25
- package/dist/{plugin-approval-roundtrip-DeW95wB5.js → .setup/plugin-approval-roundtrip-CjfzafAZ.mjs} +1 -1
- package/dist/{plugin-inventory-CoRCzWW4.js → .setup/plugin-inventory-DUle8DLz.mjs} +12 -8
- package/dist/{plugin-marketplace-discovery-3akBizzF.js → .setup/plugin-marketplace-discovery-P-Vndfqq.mjs} +18 -15
- package/dist/{plugin-metadata-cache-DgNfRAGA.js → .setup/plugin-metadata-cache-DsS5buNa.mjs} +33 -14
- package/dist/{provider-capabilities-W4tfK5Y5.js → .setup/provider-capabilities-B8J4Dy-O.mjs} +2 -2
- package/dist/.setup/provider-fFh_-VR3.mjs +50 -0
- package/dist/{rate-limits-Bqnx1N4z.js → .setup/rate-limits-DOEGYRNs.mjs} +90 -46
- package/dist/.setup/request-BioXJLYa.mjs +292 -0
- package/dist/.setup/run-attempt-lifecycle-B7JbY4-F.mjs +71 -0
- package/dist/{run-attempt-DwKds3DN.js → .setup/run-attempt-rlA07pUv.mjs} +644 -491
- package/dist/{runtime-artifact-T-NwqeSH.js → .setup/runtime-artifact-Dy38Z5iU.mjs} +2 -2
- package/dist/{sandbox-guard-BcT66fWy.js → .setup/sandbox-guard-BBP-RbTf.mjs} +4 -3
- package/dist/{session-binding-CKSepwIC.js → .setup/session-binding-_0W_fHRr.mjs} +45 -63
- package/dist/{session-binding-record-CaZjYwNS.js → .setup/session-binding-record-TH3xA0zj.mjs} +181 -3
- package/dist/.setup/session-binding-sidecars-CKYEcfNn.mjs +543 -0
- package/dist/{session-catalog-adoption-BKLtSrwi.js → .setup/session-catalog-adoption-CR_whkEW.mjs} +6 -6
- package/dist/{session-catalog-archive-D0i2pGj2.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/{session-catalog-parsing-CdD3JhdM.js → .setup/session-catalog-native-projection-ChV3cCtm.mjs} +213 -21
- package/dist/{session-catalog-node-adoption-BUuDYY8R.js → .setup/session-catalog-node-adoption-BVRsYOSh.mjs} +3 -3
- package/dist/{session-catalog-node-continue-CBtc4wkI.js → .setup/session-catalog-node-continue-Dowv8osw.mjs} +7 -7
- package/dist/.setup/session-catalog-projection-C1Bj9Jl-.mjs +414 -0
- package/dist/{session-cli-CbgaHh0f.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-DFN7sWRU.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-CNEvGaU5.js → .setup/session-history.worker-CpNj7H_P.mjs} +192 -89
- package/dist/{session-initialization-Jo86C60I.js → .setup/session-initialization-DH1z6fXn.mjs} +2 -2
- package/dist/{session-permission-policy-B22InTp_.js → .setup/session-permission-policy-DhurXY0l.mjs} +2 -2
- package/dist/{session-retirement-BSNG_mhf.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-BuLUrZl2.js → .setup/settled-turn-finalizer-CiY2bLFK.mjs} +10 -10
- package/dist/{shared-client-CVhez2Uo.js → .setup/shared-client-BpTtuLQ5.mjs} +495 -156
- package/dist/{side-question-DLbr2vUU.js → .setup/side-question-DSCZyoBD.mjs} +64 -47
- package/dist/{source-Da-h6f7e.js → .setup/source-Bjp4UQij.mjs} +15 -14
- package/dist/{thread-archive-guard-D1bP04Tf.js → .setup/thread-archive-guard-jmecf6OJ.mjs} +1 -1
- package/dist/.setup/thread-history-page-BTx-4xsg.mjs +83 -0
- package/dist/{thread-lifecycle-D1pzRwHB.js → .setup/thread-lifecycle-Cik28qWn.mjs} +330 -150
- package/dist/{thread-ownership-D6oASVRu.js → .setup/thread-ownership-skomxnIt.mjs} +4 -4
- package/dist/{thread-policy-D8kI7yAO.js → .setup/thread-policy-Cjs4B8xf.mjs} +3 -3
- package/dist/{thread-resume-T5cveyxA.js → .setup/thread-resume-BkNpj9Pz.mjs} +8 -5
- package/dist/.setup/timeout-DZF05XrR.mjs +63 -0
- package/dist/{session-catalog-home-id-D7-aLiNt.js → .setup/transcript-history-projection-DHNBDhCU.mjs} +73 -20
- package/dist/{transcript-mirror-t5TOccct.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-De-sLJ2r.js → .setup/upstream-session-fork-D3UKxgVD.mjs} +27 -26
- package/dist/{usage-CPPTmBvZ.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-CSVgQL0w.js → .setup/web-search-provider.runtime-CTGuJt6i.mjs} +3 -3
- package/dist/api.js +28 -20
- 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 +130 -135
- package/dist/media-understanding-provider.js +3 -3
- package/dist/migration-provider-api.js +2 -0
- 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 -68
- package/package.json +11 -8
- package/dist/binding-connection-QqgcPCgk.js +0 -60
- package/dist/event-projector-usage-CIyrYYid.js +0 -102
- package/dist/native-subagent-monitor-BbCUKRwC.js +0 -3350
- package/dist/request-CGvGOpeR.js +0 -198
- package/dist/session-catalog-HaDcp7Sv.js +0 -1533
- package/dist/session-catalog-provenance-DkJHo735.js +0 -124
- package/dist/timeout-BVw8aaM1.js +0 -33
- package/dist/{auth-profile-selection-DFu9e167.js → .setup/auth-profile-selection-DFu9e167.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/{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
|
@@ -1,3350 +0,0 @@
|
|
|
1
|
-
import { o as isJsonObject } from "./protocol-CAAcVIKF.js";
|
|
2
|
-
import { t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-LphB8slG.js";
|
|
3
|
-
import { t as readCodexMirroredSessionHistoryMessages } from "./session-history-Cz-tOOYj.js";
|
|
4
|
-
import { N as claimCodexAppServerLiveThread, U as releaseCodexAppServerLiveThread, W as retainCodexAppServerLiveThread } from "./shared-client-CVhez2Uo.js";
|
|
5
|
-
import { A as shouldRecordNativeToolTranscript, B as readNonEmptyStringArray, C as isSideEffectingNativeToolItem, D as itemTitle, E as itemStatus, H as readNullableString$1, I as readHookOutputEntries, L as readItem, N as normalizeOptionalString$1, O as matchesCodexSnapshotTurn, S as isNonSuccessItemStatus, T as itemName, j as shouldSynthesizeToolProgressForItem, w as itemKind, x as isMutatingNativeToolItem, y as auditNativeToolTerminalStatus, z as readNonEmptyString } from "./attempt-client-cleanup-D6cR54TV.js";
|
|
6
|
-
import { t as resolveCodexLocalRuntimeAttribution } from "./local-runtime-attribution-B_2mnqhb.js";
|
|
7
|
-
import { n as codexApprovalTimeoutText } from "./plugin-approval-roundtrip-DeW95wB5.js";
|
|
8
|
-
import { asFiniteNumber, asNonArrayRecord, normalizeOptionalString, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
9
|
-
import { asDateTimestampMs } from "openclaw/plugin-sdk/number-runtime";
|
|
10
|
-
import path from "node:path";
|
|
11
|
-
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
|
12
|
-
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
13
|
-
import { TOOL_PROGRESS_OUTPUT_MAX_CHARS, embeddedAgentLog, emitAgentEvent, formatErrorMessage, formatToolAggregate, formatToolProgressOutput, inferToolMetaFromArgs, runAgentHarnessAfterToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
14
|
-
import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
15
|
-
import { createAgentHarnessTaskRuntime, deliverAgentHarnessTaskCompletion, isDurableAgentHarnessCompletionDelivery } from "openclaw/plugin-sdk/agent-harness-task-runtime";
|
|
16
|
-
//#region extensions/codex/src/app-server/event-projector-tool-output.ts
|
|
17
|
-
const TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS = 1e4;
|
|
18
|
-
const TOOL_OUTPUT_TRUNCATION_NOTICE_PREFIX = "...(OpenClaw truncated Codex native tool output";
|
|
19
|
-
var ToolOutputAccumulator = class {
|
|
20
|
-
constructor() {
|
|
21
|
-
this.prefixByItem = /* @__PURE__ */ new Map();
|
|
22
|
-
this.originalLengthByItem = /* @__PURE__ */ new Map();
|
|
23
|
-
this.trimStateByItem = /* @__PURE__ */ new Map();
|
|
24
|
-
this.truncatedItemIds = /* @__PURE__ */ new Set();
|
|
25
|
-
this.textByItem = /* @__PURE__ */ new Map();
|
|
26
|
-
}
|
|
27
|
-
append(itemId, delta) {
|
|
28
|
-
const originalLength = (this.originalLengthByItem.get(itemId) ?? this.textByItem.get(itemId)?.length ?? 0) + delta.length;
|
|
29
|
-
this.originalLengthByItem.set(itemId, originalLength);
|
|
30
|
-
const normalizedLength = updateToolOutputTrimState(this.trimStateByItem, itemId, delta);
|
|
31
|
-
const next = appendBoundedToolTranscriptText(this.prefixByItem.get(itemId) ?? this.textByItem.get(itemId) ?? "", this.truncatedItemIds.has(itemId) ? "" : delta, originalLength);
|
|
32
|
-
this.prefixByItem.set(itemId, next.rawPrefix);
|
|
33
|
-
this.textByItem.set(itemId, next.text);
|
|
34
|
-
if (originalLength > 1e4) this.truncatedItemIds.add(itemId);
|
|
35
|
-
return {
|
|
36
|
-
text: next.text,
|
|
37
|
-
originalLength,
|
|
38
|
-
normalizedLength,
|
|
39
|
-
rawPrefix: next.rawPrefix
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
function updateToolOutputTrimState(trimStateByItem, itemId, delta) {
|
|
44
|
-
const state = trimStateByItem.get(itemId) ?? {
|
|
45
|
-
totalLength: 0,
|
|
46
|
-
leadingWhitespaceLength: 0,
|
|
47
|
-
trailingWhitespaceLength: 0,
|
|
48
|
-
sawNonWhitespace: false
|
|
49
|
-
};
|
|
50
|
-
state.totalLength += delta.length;
|
|
51
|
-
const firstNonWhitespace = delta.search(/\S/u);
|
|
52
|
-
if (firstNonWhitespace === -1) {
|
|
53
|
-
if (!state.sawNonWhitespace) state.leadingWhitespaceLength += delta.length;
|
|
54
|
-
state.trailingWhitespaceLength += delta.length;
|
|
55
|
-
trimStateByItem.set(itemId, state);
|
|
56
|
-
return state.sawNonWhitespace ? state.totalLength - state.leadingWhitespaceLength - state.trailingWhitespaceLength : 0;
|
|
57
|
-
}
|
|
58
|
-
if (!state.sawNonWhitespace) {
|
|
59
|
-
state.leadingWhitespaceLength += firstNonWhitespace;
|
|
60
|
-
state.sawNonWhitespace = true;
|
|
61
|
-
}
|
|
62
|
-
state.trailingWhitespaceLength = delta.match(/\s*$/u)?.[0].length ?? 0;
|
|
63
|
-
trimStateByItem.set(itemId, state);
|
|
64
|
-
return state.totalLength - state.leadingWhitespaceLength - state.trailingWhitespaceLength;
|
|
65
|
-
}
|
|
66
|
-
function toolOutputRawEchoSignature(text) {
|
|
67
|
-
const trimmed = text.trim();
|
|
68
|
-
if (!trimmed) return;
|
|
69
|
-
return {
|
|
70
|
-
rawLength: trimmed.length,
|
|
71
|
-
rawPrefix: trimmed.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function normalizeToolTranscriptArguments(value) {
|
|
75
|
-
return asNonArrayRecord(value);
|
|
76
|
-
}
|
|
77
|
-
function collectDynamicToolContentText(contentItems) {
|
|
78
|
-
if (!Array.isArray(contentItems)) return "";
|
|
79
|
-
return contentItems.flatMap((entry) => {
|
|
80
|
-
if (!isJsonObject(entry)) return [];
|
|
81
|
-
const text = readStringField(entry, "text");
|
|
82
|
-
return text ? [text] : [];
|
|
83
|
-
}).join("\n");
|
|
84
|
-
}
|
|
85
|
-
function appendBoundedToolTranscriptText(currentPrefix, delta, originalLength) {
|
|
86
|
-
if (originalLength <= 1e4) {
|
|
87
|
-
const rawPrefix = currentPrefix + delta;
|
|
88
|
-
return {
|
|
89
|
-
text: rawPrefix,
|
|
90
|
-
rawPrefix
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
const notice = toolTranscriptTruncationNotice(originalLength);
|
|
94
|
-
if (notice.length >= 1e4) return {
|
|
95
|
-
text: notice.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS),
|
|
96
|
-
rawPrefix: ""
|
|
97
|
-
};
|
|
98
|
-
const textBudget = TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS - notice.length;
|
|
99
|
-
const remaining = Math.max(0, textBudget - currentPrefix.length);
|
|
100
|
-
const prefix = remaining > 0 ? `${currentPrefix}${truncateUtf16Safe(delta, remaining)}` : currentPrefix;
|
|
101
|
-
const rawPrefix = truncateUtf16Safe(prefix, textBudget);
|
|
102
|
-
return {
|
|
103
|
-
text: `${rawPrefix}${notice}`,
|
|
104
|
-
rawPrefix
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
function toolTranscriptTruncationNotice(originalLength) {
|
|
108
|
-
return `\n${`${TOOL_OUTPUT_TRUNCATION_NOTICE_PREFIX}: original ${originalLength} chars, showing ${TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS}; rerun with narrower args.)`}`;
|
|
109
|
-
}
|
|
110
|
-
function truncateToolTranscriptText(text, originalLength = text.length) {
|
|
111
|
-
if (originalLength <= 1e4 && text.length <= 1e4) return text;
|
|
112
|
-
const notice = toolTranscriptTruncationNotice(originalLength);
|
|
113
|
-
if (notice.length >= 1e4) return notice.slice(1, 10001);
|
|
114
|
-
const textBudget = TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS - notice.length;
|
|
115
|
-
return `${truncateUtf16Safe(text, textBudget)}${notice}`;
|
|
116
|
-
}
|
|
117
|
-
function formatToolSummary(toolName, meta) {
|
|
118
|
-
const trimmedMeta = meta?.trim();
|
|
119
|
-
return formatToolAggregate(toolName, trimmedMeta ? [trimmedMeta] : void 0, { markdown: true });
|
|
120
|
-
}
|
|
121
|
-
function formatToolOutput(toolName, meta, output) {
|
|
122
|
-
const formattedOutput = formatToolProgressOutput(output);
|
|
123
|
-
if (!formattedOutput) return formatToolSummary(toolName, meta);
|
|
124
|
-
const fence = markdownFenceForText(formattedOutput);
|
|
125
|
-
return `${formatToolSummary(toolName, meta)}\n${fence}txt\n${formattedOutput}\n${fence}`;
|
|
126
|
-
}
|
|
127
|
-
function markdownFenceForText(text) {
|
|
128
|
-
return "`".repeat(Math.max(3, longestBacktickRun(text) + 1));
|
|
129
|
-
}
|
|
130
|
-
function longestBacktickRun(value) {
|
|
131
|
-
let longest = 0;
|
|
132
|
-
let current = 0;
|
|
133
|
-
for (const char of value) {
|
|
134
|
-
if (char === "`") {
|
|
135
|
-
current += 1;
|
|
136
|
-
longest = Math.max(longest, current);
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
current = 0;
|
|
140
|
-
}
|
|
141
|
-
return longest;
|
|
142
|
-
}
|
|
143
|
-
//#endregion
|
|
144
|
-
//#region extensions/codex/src/app-server/tool-progress-normalization.ts
|
|
145
|
-
/**
|
|
146
|
-
* Normalizes and sanitizes Codex dynamic-tool progress payloads before they are
|
|
147
|
-
* emitted into OpenClaw events or logs.
|
|
148
|
-
*/
|
|
149
|
-
/** Maps OpenClaw tool-progress config to the mode used by Codex progress metadata. */
|
|
150
|
-
function resolveCodexToolProgressDetailMode(value) {
|
|
151
|
-
return value === "raw" ? "raw" : "explain";
|
|
152
|
-
}
|
|
153
|
-
function isCodexCommandBearingToolCall(name, args) {
|
|
154
|
-
const normalizedName = name?.trim().toLowerCase();
|
|
155
|
-
return normalizedName === "exec" || normalizedName === "bash" || normalizedName === "shell" || typeof args?.command === "string" && args.command.trim().length > 0;
|
|
156
|
-
}
|
|
157
|
-
/** Recursively redacts sensitive strings and handles circular values in event payloads. */
|
|
158
|
-
function sanitizeCodexAgentEventValue(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
159
|
-
if (typeof value === "string") return redactToolPayloadText(value);
|
|
160
|
-
if (Array.isArray(value)) {
|
|
161
|
-
if (seen.has(value)) return "[Circular]";
|
|
162
|
-
seen.add(value);
|
|
163
|
-
return value.map((entry) => sanitizeCodexAgentEventValue(entry, seen));
|
|
164
|
-
}
|
|
165
|
-
if (value && typeof value === "object") {
|
|
166
|
-
if (seen.has(value)) return "[Circular]";
|
|
167
|
-
seen.add(value);
|
|
168
|
-
const out = {};
|
|
169
|
-
for (const [key, child] of Object.entries(value)) out[key] = typeof child === "string" ? redactSensitiveFieldValue(key, child) : sanitizeCodexAgentEventValue(child, seen);
|
|
170
|
-
return out;
|
|
171
|
-
}
|
|
172
|
-
return value;
|
|
173
|
-
}
|
|
174
|
-
/** Sanitizes a record-shaped Codex agent event payload. */
|
|
175
|
-
function sanitizeCodexAgentEventRecord(value) {
|
|
176
|
-
return sanitizeCodexAgentEventValue(value);
|
|
177
|
-
}
|
|
178
|
-
/** Sanitizes dynamic-tool arguments before diagnostic/event emission. */
|
|
179
|
-
function sanitizeCodexToolArguments(value) {
|
|
180
|
-
if (!isJsonObject(value)) return;
|
|
181
|
-
return sanitizeCodexAgentEventRecord(value);
|
|
182
|
-
}
|
|
183
|
-
/** Sanitizes a Codex dynamic-tool response before diagnostic/event emission. */
|
|
184
|
-
function sanitizeCodexToolResponse(response) {
|
|
185
|
-
return sanitizeCodexAgentEventRecord({ ...response });
|
|
186
|
-
}
|
|
187
|
-
/** Infers compact human-readable tool metadata from Codex dynamic-tool arguments. */
|
|
188
|
-
function inferCodexDynamicToolMeta(call, detailMode) {
|
|
189
|
-
return inferToolMetaFromArgs(call.tool, call.arguments, { detailMode });
|
|
190
|
-
}
|
|
191
|
-
//#endregion
|
|
192
|
-
//#region extensions/codex/src/app-server/event-projector-tool-items.ts
|
|
193
|
-
function isNativePostToolUseRelayItem(item) {
|
|
194
|
-
switch (item.type) {
|
|
195
|
-
case "commandExecution":
|
|
196
|
-
case "fileChange":
|
|
197
|
-
case "mcpToolCall": return true;
|
|
198
|
-
default: return false;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
function shouldSuppressChannelProgressForItem(item) {
|
|
202
|
-
if (shouldSynthesizeToolProgressForItem(item)) return true;
|
|
203
|
-
return item.type === "dynamicToolCall";
|
|
204
|
-
}
|
|
205
|
-
function itemToolArgs(item) {
|
|
206
|
-
if (item.type === "commandExecution") return sanitizeCodexAgentEventRecord({
|
|
207
|
-
command: item.command,
|
|
208
|
-
...typeof item.cwd === "string" ? { cwd: item.cwd } : {}
|
|
209
|
-
});
|
|
210
|
-
if (item.type === "fileChange") return sanitizeCodexAgentEventRecord({ changes: itemFileChangesForTranscript(item) });
|
|
211
|
-
if (item.type === "webSearch") return webSearchToolArgs(item);
|
|
212
|
-
if (item.type === "dynamicToolCall" || item.type === "mcpToolCall") return sanitizeCodexToolArguments(item.arguments);
|
|
213
|
-
}
|
|
214
|
-
function isCommandBearingToolItem(item, args) {
|
|
215
|
-
if (item.type === "commandExecution") return true;
|
|
216
|
-
return typeof args?.command === "string" && args.command.trim().length > 0;
|
|
217
|
-
}
|
|
218
|
-
function webSearchToolArgs(item) {
|
|
219
|
-
const action = isJsonObject(item.action) ? item.action : void 0;
|
|
220
|
-
const actionType = action ? readNonEmptyString(action, "type") : void 0;
|
|
221
|
-
const queries = action && actionType === "search" ? readNonEmptyStringArray(action, "queries") : [];
|
|
222
|
-
const query = normalizeOptionalString$1(item.query) ?? (action && actionType === "search" ? readNonEmptyString(action, "query") : void 0) ?? queries[0];
|
|
223
|
-
const url = action ? readNonEmptyString(action, "url") : void 0;
|
|
224
|
-
const pattern = action ? readNonEmptyString(action, "pattern") : void 0;
|
|
225
|
-
const args = {};
|
|
226
|
-
if (query) args.query = query;
|
|
227
|
-
if (queries.length > 0) args.queries = queries;
|
|
228
|
-
if (actionType && actionType !== "search") args.action = actionType;
|
|
229
|
-
if (url) args.url = url;
|
|
230
|
-
if (pattern) args.pattern = pattern;
|
|
231
|
-
if (!query && !url && !pattern) args.queryUnavailable = true;
|
|
232
|
-
return sanitizeCodexAgentEventRecord(args);
|
|
233
|
-
}
|
|
234
|
-
function itemToolResult(item) {
|
|
235
|
-
if (item.type === "commandExecution") return { result: sanitizeCodexAgentEventRecord({
|
|
236
|
-
status: item.status,
|
|
237
|
-
exitCode: item.exitCode,
|
|
238
|
-
durationMs: item.durationMs
|
|
239
|
-
}) };
|
|
240
|
-
if (item.type === "fileChange") return { result: sanitizeCodexAgentEventRecord({
|
|
241
|
-
status: item.status,
|
|
242
|
-
changes: itemFileChanges(item)
|
|
243
|
-
}) };
|
|
244
|
-
if (item.type === "mcpToolCall") return { result: sanitizeCodexAgentEventRecord({
|
|
245
|
-
status: item.status,
|
|
246
|
-
durationMs: item.durationMs,
|
|
247
|
-
...item.error ? { error: item.error } : {},
|
|
248
|
-
...item.result ? { result: item.result } : {}
|
|
249
|
-
}) };
|
|
250
|
-
if (item.type === "webSearch") return { result: webSearchToolResult(item) };
|
|
251
|
-
return {};
|
|
252
|
-
}
|
|
253
|
-
function webSearchToolResult(item) {
|
|
254
|
-
return sanitizeCodexAgentEventRecord({
|
|
255
|
-
status: itemStatus(item),
|
|
256
|
-
...typeof item.durationMs === "number" ? { durationMs: item.durationMs } : {},
|
|
257
|
-
...webSearchToolArgs(item)
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
function itemFileChangeRecords(item) {
|
|
261
|
-
const changes = item.changes;
|
|
262
|
-
return Array.isArray(changes) ? changes.filter(isJsonObject) : [];
|
|
263
|
-
}
|
|
264
|
-
function itemFileChanges(item) {
|
|
265
|
-
return itemFileChangeRecords(item).flatMap((change) => {
|
|
266
|
-
const path = normalizeOptionalString$1(change.path);
|
|
267
|
-
if (!path || change.kind === void 0) return [];
|
|
268
|
-
return [{
|
|
269
|
-
path,
|
|
270
|
-
kind: change.kind
|
|
271
|
-
}];
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
function fileChangeKindType(kind) {
|
|
275
|
-
if (typeof kind === "string") return kind;
|
|
276
|
-
return isJsonObject(kind) ? normalizeOptionalString$1(kind.type) : void 0;
|
|
277
|
-
}
|
|
278
|
-
function countFileContentLines(content) {
|
|
279
|
-
if (!content) return 0;
|
|
280
|
-
const lines = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
281
|
-
if (lines.length > 1 && lines.at(-1) === "") lines.pop();
|
|
282
|
-
return lines.length;
|
|
283
|
-
}
|
|
284
|
-
function fileChangeDiffStat(diff, kind) {
|
|
285
|
-
const kindType = fileChangeKindType(kind);
|
|
286
|
-
if (kindType === "add") return {
|
|
287
|
-
added: countFileContentLines(diff),
|
|
288
|
-
removed: 0
|
|
289
|
-
};
|
|
290
|
-
if (kindType === "delete") return {
|
|
291
|
-
added: 0,
|
|
292
|
-
removed: countFileContentLines(diff)
|
|
293
|
-
};
|
|
294
|
-
let added = 0;
|
|
295
|
-
let removed = 0;
|
|
296
|
-
let inHunk = false;
|
|
297
|
-
for (const line of diff.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n")) {
|
|
298
|
-
if (line.startsWith("@@")) {
|
|
299
|
-
inHunk = true;
|
|
300
|
-
continue;
|
|
301
|
-
}
|
|
302
|
-
if (!inHunk) continue;
|
|
303
|
-
if (line.startsWith("+")) added += 1;
|
|
304
|
-
else if (line.startsWith("-")) removed += 1;
|
|
305
|
-
}
|
|
306
|
-
return {
|
|
307
|
-
added,
|
|
308
|
-
removed
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
function truncateFileChangeDiffAtLineBoundary(diff, maxChars) {
|
|
312
|
-
if (diff.length <= maxChars) return { diff };
|
|
313
|
-
if (maxChars <= 0) return { diffTruncated: true };
|
|
314
|
-
const boundary = diff.lastIndexOf("\n", maxChars - 1);
|
|
315
|
-
return boundary >= 0 ? {
|
|
316
|
-
diff: diff.slice(0, boundary + 1),
|
|
317
|
-
diffTruncated: true
|
|
318
|
-
} : { diffTruncated: true };
|
|
319
|
-
}
|
|
320
|
-
function itemFileChangesForTranscript(item) {
|
|
321
|
-
let remainingDiffChars = 1e4;
|
|
322
|
-
return itemFileChangeRecords(item).flatMap((change) => {
|
|
323
|
-
const path = normalizeOptionalString$1(change.path);
|
|
324
|
-
if (!path || change.kind === void 0) return [];
|
|
325
|
-
const result = {
|
|
326
|
-
path,
|
|
327
|
-
kind: change.kind
|
|
328
|
-
};
|
|
329
|
-
if (typeof change.diff !== "string") return [result];
|
|
330
|
-
result.stat = fileChangeDiffStat(change.diff, change.kind);
|
|
331
|
-
const bounded = truncateFileChangeDiffAtLineBoundary(change.diff, remainingDiffChars);
|
|
332
|
-
if (bounded.diff !== void 0) {
|
|
333
|
-
result.diff = bounded.diff;
|
|
334
|
-
remainingDiffChars -= bounded.diff.length;
|
|
335
|
-
}
|
|
336
|
-
if (bounded.diffTruncated) result.diffTruncated = true;
|
|
337
|
-
return [result];
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
function itemToolError(item, status, outputTextByItem) {
|
|
341
|
-
if (status === "blocked") return "codex native tool blocked";
|
|
342
|
-
if (status !== "failed") return;
|
|
343
|
-
return itemOutputText(item, outputTextByItem) ?? "codex native tool failed";
|
|
344
|
-
}
|
|
345
|
-
function itemMeta(item, detailMode = "explain") {
|
|
346
|
-
if (item.type === "commandExecution" && typeof item.command === "string") return inferToolMetaFromArgs("exec", {
|
|
347
|
-
command: item.command,
|
|
348
|
-
cwd: typeof item.cwd === "string" ? item.cwd : void 0
|
|
349
|
-
}, { detailMode });
|
|
350
|
-
if (item.type === "webSearch") return inferToolMetaFromArgs("web_search", webSearchToolArgs(item), { detailMode });
|
|
351
|
-
const toolName = itemName(item);
|
|
352
|
-
if ((item.type === "dynamicToolCall" || item.type === "mcpToolCall") && toolName) return inferToolMetaFromArgs(toolName, item.arguments, { detailMode });
|
|
353
|
-
}
|
|
354
|
-
function itemOutputText(item, outputTextByItem) {
|
|
355
|
-
if (item.type === "commandExecution") {
|
|
356
|
-
const output = item.aggregatedOutput?.trim() || outputTextByItem?.get(item.id)?.trim();
|
|
357
|
-
return output ? truncateToolTranscriptText(output) : void 0;
|
|
358
|
-
}
|
|
359
|
-
if (item.type === "dynamicToolCall") {
|
|
360
|
-
const output = collectDynamicToolContentText(item.contentItems).trim();
|
|
361
|
-
return output ? truncateToolTranscriptText(output) : void 0;
|
|
362
|
-
}
|
|
363
|
-
if (item.type === "mcpToolCall") {
|
|
364
|
-
const output = item.error ? stringifyJsonValue(item.error) : item.result ? stringifyJsonValue(item.result) : void 0;
|
|
365
|
-
return output ? truncateToolTranscriptText(output) : void 0;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
function itemTranscriptResultText(item, outputTextByItem) {
|
|
369
|
-
const output = itemOutputText(item, outputTextByItem);
|
|
370
|
-
if (output) return output;
|
|
371
|
-
const result = itemToolResult(item).result;
|
|
372
|
-
const resultText = result ? stringifyJsonValue(result) : void 0;
|
|
373
|
-
return resultText ? truncateToolTranscriptText(resultText) : itemStatus(item);
|
|
374
|
-
}
|
|
375
|
-
function stringifyJsonValue(value) {
|
|
376
|
-
try {
|
|
377
|
-
return JSON.stringify(value, null, 2);
|
|
378
|
-
} catch {
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
//#endregion
|
|
383
|
-
//#region extensions/codex/src/app-server/event-projector-tool-progress.ts
|
|
384
|
-
const TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
385
|
-
"message",
|
|
386
|
-
"messages",
|
|
387
|
-
"reply",
|
|
388
|
-
"send",
|
|
389
|
-
"reaction",
|
|
390
|
-
"react",
|
|
391
|
-
"typing"
|
|
392
|
-
]);
|
|
393
|
-
function shouldEmitTranscriptToolProgress(toolName, _args) {
|
|
394
|
-
const normalized = typeof toolName === "string" ? toolName.trim().toLowerCase() : "";
|
|
395
|
-
return Boolean(normalized && !TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES.has(normalized));
|
|
396
|
-
}
|
|
397
|
-
var CodexToolProgressProjection = class {
|
|
398
|
-
constructor(params) {
|
|
399
|
-
this.params = params;
|
|
400
|
-
this.echoesByItem = /* @__PURE__ */ new Map();
|
|
401
|
-
this.resultSummaryItemIds = /* @__PURE__ */ new Set();
|
|
402
|
-
this.resultOutputItemIds = /* @__PURE__ */ new Set();
|
|
403
|
-
this.resultOutputStreamedItemIds = /* @__PURE__ */ new Set();
|
|
404
|
-
this.transcriptProgressSuppressedIds = /* @__PURE__ */ new Set();
|
|
405
|
-
this.transcriptArgumentsById = /* @__PURE__ */ new Map();
|
|
406
|
-
this.resultOutputDeltaState = /* @__PURE__ */ new Map();
|
|
407
|
-
this.output = new ToolOutputAccumulator();
|
|
408
|
-
this.metas = /* @__PURE__ */ new Map();
|
|
409
|
-
this.sideEffectingNativeIds = /* @__PURE__ */ new Set();
|
|
410
|
-
this.sideEffectingDynamicIds = /* @__PURE__ */ new Set();
|
|
411
|
-
this.transcriptProgressCallIds = /* @__PURE__ */ new Set();
|
|
412
|
-
this.approvalTimeoutKinds = /* @__PURE__ */ new Map();
|
|
413
|
-
}
|
|
414
|
-
get outputTextByItem() {
|
|
415
|
-
return this.output.textByItem;
|
|
416
|
-
}
|
|
417
|
-
get toolMetas() {
|
|
418
|
-
return [...this.metas.values()];
|
|
419
|
-
}
|
|
420
|
-
getToolMeta(itemId) {
|
|
421
|
-
return this.metas.get(itemId);
|
|
422
|
-
}
|
|
423
|
-
get lastToolError() {
|
|
424
|
-
return this.lastNativeToolError;
|
|
425
|
-
}
|
|
426
|
-
get hasPotentialSideEffects() {
|
|
427
|
-
return this.sideEffectingNativeIds.size > 0 || this.sideEffectingDynamicIds.size > 0;
|
|
428
|
-
}
|
|
429
|
-
approvalTimeoutExplanation(itemId, status) {
|
|
430
|
-
const kind = isNonSuccessItemStatus(status) && this.approvalTimeoutKinds.get(itemId);
|
|
431
|
-
return kind ? codexApprovalTimeoutText(kind) : void 0;
|
|
432
|
-
}
|
|
433
|
-
setLastToolError(error) {
|
|
434
|
-
if (!error) {
|
|
435
|
-
this.lastNativeToolError = void 0;
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
const terminalResolution = this.params.observeToolTerminal?.({
|
|
439
|
-
toolName: error.toolName,
|
|
440
|
-
...error.meta ? { meta: error.meta } : {},
|
|
441
|
-
outcome: "failure",
|
|
442
|
-
failure: {
|
|
443
|
-
...error.errorCode ? { errorCode: error.errorCode } : {},
|
|
444
|
-
...error.error ? { error: error.error } : {},
|
|
445
|
-
...error.validationErrorSummary ? { validationErrorSummary: error.validationErrorSummary } : {},
|
|
446
|
-
...error.timedOut ? { timedOut: true } : {},
|
|
447
|
-
...error.middlewareError ? { middlewareError: true } : {}
|
|
448
|
-
},
|
|
449
|
-
nativeMutation: {
|
|
450
|
-
mutatingAction: error.mutatingAction === true,
|
|
451
|
-
replaySafe: error.mutatingAction !== true
|
|
452
|
-
}
|
|
453
|
-
});
|
|
454
|
-
this.lastNativeToolError = terminalResolution?.lastToolError ?? (this.lastNativeToolError?.mutatingAction && error.mutatingAction !== true ? this.lastNativeToolError : error);
|
|
455
|
-
}
|
|
456
|
-
recordDynamicToolResult(params) {
|
|
457
|
-
const resultText = collectDynamicToolContentText(params.contentItems);
|
|
458
|
-
const existing = this.metas.get(params.callId);
|
|
459
|
-
this.metas.set(params.callId, {
|
|
460
|
-
toolName: existing?.toolName ?? params.tool,
|
|
461
|
-
...existing?.meta ? { meta: existing.meta } : {},
|
|
462
|
-
...params.asyncStarted === true ? { asyncStarted: true } : {},
|
|
463
|
-
isError: !params.success
|
|
464
|
-
});
|
|
465
|
-
if (params.terminalResolution) this.lastNativeToolError = params.terminalResolution.lastToolError;
|
|
466
|
-
else if (!params.success) this.lastNativeToolError = {
|
|
467
|
-
toolName: params.tool,
|
|
468
|
-
error: resultText || (params.terminalType === "blocked" ? "codex dynamic tool blocked" : "codex dynamic tool failed")
|
|
469
|
-
};
|
|
470
|
-
else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
|
|
471
|
-
if (params.sideEffectEvidence === true) this.sideEffectingDynamicIds.add(params.callId);
|
|
472
|
-
}
|
|
473
|
-
handleOutputDelta(params, toolName) {
|
|
474
|
-
const itemId = readStringField(params, "itemId");
|
|
475
|
-
const delta = readStringField(params, "delta");
|
|
476
|
-
if (!itemId || !delta) return;
|
|
477
|
-
const storedOutput = this.output.append(itemId, delta);
|
|
478
|
-
this.rememberEcho(itemId, {
|
|
479
|
-
displayText: storedOutput.text,
|
|
480
|
-
rawLength: storedOutput.normalizedLength,
|
|
481
|
-
rawPrefix: storedOutput.rawPrefix,
|
|
482
|
-
streamedDisplay: true
|
|
483
|
-
});
|
|
484
|
-
if (!this.shouldEmitToolOutput()) return;
|
|
485
|
-
if (this.transcriptProgressSuppressedIds.has(itemId) || !shouldEmitTranscriptToolProgress(toolName, this.transcriptArgumentsById.get(itemId))) return;
|
|
486
|
-
const state = this.resultOutputDeltaState.get(itemId) ?? {
|
|
487
|
-
chars: 0,
|
|
488
|
-
messages: 0,
|
|
489
|
-
truncated: false
|
|
490
|
-
};
|
|
491
|
-
if (state.truncated) return;
|
|
492
|
-
const remainingChars = Math.max(0, TOOL_PROGRESS_OUTPUT_MAX_CHARS - state.chars);
|
|
493
|
-
const remainingMessages = Math.max(0, 20 - state.messages);
|
|
494
|
-
if (remainingChars === 0 || remainingMessages === 0) {
|
|
495
|
-
state.truncated = true;
|
|
496
|
-
this.resultOutputDeltaState.set(itemId, state);
|
|
497
|
-
this.emitToolResultMessage({
|
|
498
|
-
itemId,
|
|
499
|
-
text: formatToolOutput(toolName, void 0, "(output truncated)")
|
|
500
|
-
});
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
const chunk = delta.length > remainingChars ? truncateUtf16Safe(delta, remainingChars) : delta;
|
|
504
|
-
state.chars += chunk.length;
|
|
505
|
-
state.messages += 1;
|
|
506
|
-
const reachedLimit = delta.length > remainingChars || state.chars >= TOOL_PROGRESS_OUTPUT_MAX_CHARS || state.messages >= 20;
|
|
507
|
-
if (reachedLimit) state.truncated = true;
|
|
508
|
-
this.resultOutputDeltaState.set(itemId, state);
|
|
509
|
-
this.resultOutputStreamedItemIds.add(itemId);
|
|
510
|
-
this.emitToolResultMessage({
|
|
511
|
-
itemId,
|
|
512
|
-
text: formatToolOutput(toolName, void 0, reachedLimit ? `${chunk}\n...(truncated)...` : chunk)
|
|
513
|
-
});
|
|
514
|
-
}
|
|
515
|
-
recordNativeToolError(params) {
|
|
516
|
-
const executionStarted = params.status !== "blocked";
|
|
517
|
-
const mutatingAction = executionStarted && isMutatingNativeToolItem(params.item);
|
|
518
|
-
const isFailure = isNonSuccessItemStatus(params.status);
|
|
519
|
-
const approvalTimeoutExplanation = this.approvalTimeoutExplanation(params.item.id, params.status);
|
|
520
|
-
const error = isFailure ? approvalTimeoutExplanation ?? itemToolError(params.item, params.status, this.output.textByItem) : void 0;
|
|
521
|
-
const failure = error ? {
|
|
522
|
-
...approvalTimeoutExplanation ? { errorCode: "approval_timeout" } : {},
|
|
523
|
-
error,
|
|
524
|
-
...approvalTimeoutExplanation ? { timedOut: true } : {}
|
|
525
|
-
} : {};
|
|
526
|
-
const terminalResolution = this.params.observeToolTerminal?.({
|
|
527
|
-
toolCallId: params.item.id,
|
|
528
|
-
toolName: params.name,
|
|
529
|
-
arguments: itemToolArgs(params.item),
|
|
530
|
-
...params.meta ? { meta: params.meta } : {},
|
|
531
|
-
executionStarted,
|
|
532
|
-
outcome: isFailure ? "failure" : "success",
|
|
533
|
-
...isFailure ? { failure } : {},
|
|
534
|
-
nativeMutation: {
|
|
535
|
-
mutatingAction,
|
|
536
|
-
replaySafe: !mutatingAction
|
|
537
|
-
}
|
|
538
|
-
});
|
|
539
|
-
if (terminalResolution) {
|
|
540
|
-
this.lastNativeToolError = terminalResolution.lastToolError;
|
|
541
|
-
return;
|
|
542
|
-
}
|
|
543
|
-
if (isFailure) this.lastNativeToolError = {
|
|
544
|
-
toolName: params.name,
|
|
545
|
-
...params.meta ? { meta: params.meta } : {},
|
|
546
|
-
...failure,
|
|
547
|
-
...mutatingAction ? { mutatingAction: true } : {}
|
|
548
|
-
};
|
|
549
|
-
else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
|
|
550
|
-
}
|
|
551
|
-
emitToolResultSummary(item) {
|
|
552
|
-
if (!item || item.type === "dynamicToolCall") return;
|
|
553
|
-
if (!this.params.onToolResult || !this.shouldEmitToolResult()) return;
|
|
554
|
-
if (this.resultSummaryItemIds.has(item.id)) return;
|
|
555
|
-
const toolName = itemName(item);
|
|
556
|
-
const args = itemToolArgs(item);
|
|
557
|
-
if (!toolName || !shouldEmitTranscriptToolProgress(toolName, args)) return;
|
|
558
|
-
this.resultSummaryItemIds.add(item.id);
|
|
559
|
-
const meta = this.shouldIncludeFormattedMeta(isCommandBearingToolItem(item, args)) ? itemMeta(item, this.toolProgressDetailMode()) : void 0;
|
|
560
|
-
this.emitToolResultMessage({
|
|
561
|
-
itemId: item.id,
|
|
562
|
-
text: formatToolSummary(toolName, meta)
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
emitToolResultOutput(item) {
|
|
566
|
-
if (!item || item.type === "dynamicToolCall") return;
|
|
567
|
-
if (!this.params.onToolResult || !this.shouldEmitToolOutput()) return;
|
|
568
|
-
if (this.resultOutputItemIds.has(item.id) || this.resultOutputStreamedItemIds.has(item.id)) return;
|
|
569
|
-
const toolName = itemName(item);
|
|
570
|
-
const output = itemOutputText(item, this.output.textByItem);
|
|
571
|
-
if (!toolName || !output || !shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
|
|
572
|
-
const meta = this.shouldIncludeFormattedMeta(isCommandBearingToolItem(item, itemToolArgs(item))) ? itemMeta(item, this.toolProgressDetailMode()) : void 0;
|
|
573
|
-
this.emitToolResultMessage({
|
|
574
|
-
itemId: item.id,
|
|
575
|
-
text: formatToolOutput(toolName, meta, output),
|
|
576
|
-
finalOutput: true,
|
|
577
|
-
isError: isNonSuccessItemStatus(itemStatus(item))
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
|
-
recordToolMeta(item) {
|
|
581
|
-
if (!item) return;
|
|
582
|
-
if (isSideEffectingNativeToolItem(item)) this.sideEffectingNativeIds.add(item.id);
|
|
583
|
-
else this.sideEffectingNativeIds.delete(item.id);
|
|
584
|
-
const toolName = itemName(item);
|
|
585
|
-
if (!toolName) return;
|
|
586
|
-
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
587
|
-
const existing = this.metas.get(item.id);
|
|
588
|
-
const terminalStatus = auditNativeToolTerminalStatus(item);
|
|
589
|
-
const isError = typeof existing?.isError === "boolean" ? existing.isError : terminalStatus === "completed" ? false : terminalStatus === "failed" || terminalStatus === "blocked" ? true : void 0;
|
|
590
|
-
this.metas.set(item.id, {
|
|
591
|
-
toolName,
|
|
592
|
-
...meta ? { meta } : {},
|
|
593
|
-
...existing?.asyncStarted ? { asyncStarted: true } : {},
|
|
594
|
-
...isError === void 0 ? {} : { isError }
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
recordTranscriptCall(params) {
|
|
598
|
-
this.transcriptArgumentsById.set(params.id, params.arguments);
|
|
599
|
-
if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) this.transcriptProgressSuppressedIds.add(params.id);
|
|
600
|
-
else this.transcriptProgressSuppressedIds.delete(params.id);
|
|
601
|
-
this.emitTranscriptToolCallProgress(params);
|
|
602
|
-
}
|
|
603
|
-
recordTranscriptResult(params) {
|
|
604
|
-
this.emitTranscriptToolResultProgress(params);
|
|
605
|
-
}
|
|
606
|
-
matchesEcho(text) {
|
|
607
|
-
for (const state of this.echoesByItem.values()) {
|
|
608
|
-
if (state.streamedDisplayText === text || state.displayTexts.includes(text)) return true;
|
|
609
|
-
if (state.streamedRawSignature && text.length === state.streamedRawSignature.length && text.startsWith(state.streamedRawSignature.prefix)) return true;
|
|
610
|
-
for (const signature of state.rawSignatures) if (text.length === signature.length && text.startsWith(signature.prefix)) return true;
|
|
611
|
-
}
|
|
612
|
-
return false;
|
|
613
|
-
}
|
|
614
|
-
rememberCommandAggregateOutputEcho(item) {
|
|
615
|
-
if (item?.type !== "commandExecution" || typeof item.aggregatedOutput !== "string") return;
|
|
616
|
-
const signature = toolOutputRawEchoSignature(item.aggregatedOutput);
|
|
617
|
-
if (signature) this.rememberEcho(item.id, signature);
|
|
618
|
-
}
|
|
619
|
-
toolProgressDetailMode() {
|
|
620
|
-
return resolveCodexToolProgressDetailMode(this.params.toolProgressDetail);
|
|
621
|
-
}
|
|
622
|
-
emitToolResultMessage(params) {
|
|
623
|
-
const rawText = params.text.trim();
|
|
624
|
-
const text = truncateToolTranscriptText(rawText);
|
|
625
|
-
if (!text) return;
|
|
626
|
-
this.rememberEcho(params.itemId, {
|
|
627
|
-
displayText: text,
|
|
628
|
-
rawText
|
|
629
|
-
});
|
|
630
|
-
if (params.finalOutput) this.resultOutputItemIds.add(params.itemId);
|
|
631
|
-
try {
|
|
632
|
-
Promise.resolve(this.params.onToolResult?.({
|
|
633
|
-
text,
|
|
634
|
-
...(this.params.messageChannel || this.params.messageProvider) && { channelData: { openclawToolProgressId: params.itemId } },
|
|
635
|
-
...params.isError === true ? { isError: true } : {}
|
|
636
|
-
})).catch(() => {});
|
|
637
|
-
} catch {}
|
|
638
|
-
}
|
|
639
|
-
shouldEmitToolResult() {
|
|
640
|
-
return typeof this.params.shouldEmitToolResult === "function" ? this.params.shouldEmitToolResult() : this.params.verboseLevel === "on" || this.params.verboseLevel === "full";
|
|
641
|
-
}
|
|
642
|
-
shouldEmitToolOutput() {
|
|
643
|
-
return typeof this.params.shouldEmitToolOutput === "function" ? this.params.shouldEmitToolOutput() : this.params.verboseLevel === "full";
|
|
644
|
-
}
|
|
645
|
-
shouldIncludeFormattedMeta(commandBearing) {
|
|
646
|
-
const channel = this.params.messageChannel ?? this.params.messageProvider;
|
|
647
|
-
return !commandBearing || !channel && this.shouldEmitToolOutput();
|
|
648
|
-
}
|
|
649
|
-
emitTranscriptToolCallProgress(params) {
|
|
650
|
-
if (params.name === "progress_card" || !shouldEmitTranscriptToolProgress(params.name, params.arguments)) return;
|
|
651
|
-
this.transcriptProgressCallIds.add(params.id);
|
|
652
|
-
const args = normalizeToolTranscriptArguments(params.arguments);
|
|
653
|
-
const meta = this.shouldIncludeFormattedMeta(isCodexCommandBearingToolCall(params.name, args)) ? inferToolMetaFromArgs(params.name, args, { detailMode: this.toolProgressDetailMode() }) : void 0;
|
|
654
|
-
if (!this.params.onToolResult || !this.shouldEmitToolResult() || this.resultSummaryItemIds.has(params.id) || this.resultOutputStreamedItemIds.has(params.id)) return;
|
|
655
|
-
this.resultSummaryItemIds.add(params.id);
|
|
656
|
-
this.emitToolResultMessage({
|
|
657
|
-
itemId: params.id,
|
|
658
|
-
text: formatToolSummary(params.name, meta)
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
emitTranscriptToolResultProgress(params) {
|
|
662
|
-
if (params.name === "progress_card" && !params.isError || this.transcriptProgressSuppressedIds.has(params.id) || !shouldEmitTranscriptToolProgress(params.name, this.transcriptArgumentsById.get(params.id))) return;
|
|
663
|
-
if (params.name === "progress_card" && this.shouldEmitToolResult()) this.emitToolResultMessage({
|
|
664
|
-
itemId: params.id,
|
|
665
|
-
text: formatToolSummary(params.name),
|
|
666
|
-
isError: true
|
|
667
|
-
});
|
|
668
|
-
if (!this.transcriptProgressCallIds.has(params.id)) this.emitTranscriptToolCallProgress({
|
|
669
|
-
id: params.id,
|
|
670
|
-
name: params.name,
|
|
671
|
-
arguments: {}
|
|
672
|
-
});
|
|
673
|
-
if (!this.params.onToolResult || !this.shouldEmitToolOutput() || this.resultOutputItemIds.has(params.id) || this.resultOutputStreamedItemIds.has(params.id)) return;
|
|
674
|
-
const text = params.text?.trim();
|
|
675
|
-
if (text) this.emitToolResultMessage({
|
|
676
|
-
itemId: params.id,
|
|
677
|
-
text: formatToolOutput(params.name, void 0, text),
|
|
678
|
-
finalOutput: true,
|
|
679
|
-
isError: params.isError
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
rememberEcho(itemId, signature) {
|
|
683
|
-
if (!itemId) return;
|
|
684
|
-
const existing = this.echoesByItem.get(itemId) ?? {
|
|
685
|
-
displayTexts: [],
|
|
686
|
-
rawSignatures: []
|
|
687
|
-
};
|
|
688
|
-
const displayText = signature.displayText?.trim();
|
|
689
|
-
if (displayText) {
|
|
690
|
-
if (signature.streamedDisplay) existing.streamedDisplayText = displayText;
|
|
691
|
-
else if (!existing.displayTexts.includes(displayText)) {
|
|
692
|
-
if (existing.displayTexts.length >= 24) existing.displayTexts.shift();
|
|
693
|
-
existing.displayTexts.push(displayText);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
const rawText = signature.rawText?.trim();
|
|
697
|
-
const rawLength = signature.rawLength ?? rawText?.length;
|
|
698
|
-
const rawPrefix = signature.rawPrefix?.trim() ?? rawText;
|
|
699
|
-
if (rawLength !== void 0 && rawPrefix && rawPrefix.length >= 1024) {
|
|
700
|
-
const next = {
|
|
701
|
-
length: rawLength,
|
|
702
|
-
prefix: rawPrefix.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)
|
|
703
|
-
};
|
|
704
|
-
if (signature.streamedDisplay) existing.streamedRawSignature = next;
|
|
705
|
-
else {
|
|
706
|
-
const matchIndex = existing.rawSignatures.findIndex((entry) => entry.prefix === next.prefix);
|
|
707
|
-
if (matchIndex >= 0) existing.rawSignatures[matchIndex] = next;
|
|
708
|
-
else {
|
|
709
|
-
if (existing.rawSignatures.length >= 24) existing.rawSignatures.shift();
|
|
710
|
-
existing.rawSignatures.push(next);
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
this.echoesByItem.set(itemId, existing);
|
|
715
|
-
}
|
|
716
|
-
};
|
|
717
|
-
//#endregion
|
|
718
|
-
//#region extensions/codex/src/app-server/event-projector-tool-transcript.ts
|
|
719
|
-
const ZERO_USAGE = {
|
|
720
|
-
input: 0,
|
|
721
|
-
output: 0,
|
|
722
|
-
cacheRead: 0,
|
|
723
|
-
cacheWrite: 0,
|
|
724
|
-
totalTokens: 0,
|
|
725
|
-
cost: {
|
|
726
|
-
input: 0,
|
|
727
|
-
output: 0,
|
|
728
|
-
cacheRead: 0,
|
|
729
|
-
cacheWrite: 0,
|
|
730
|
-
total: 0
|
|
731
|
-
}
|
|
732
|
-
};
|
|
733
|
-
const MISSING_TOOL_RESULT_ERROR = "OpenClaw recorded a native Codex tool.call without a matching tool.result before the turn completed.";
|
|
734
|
-
const NATIVE_PATCH_REJECTION_RE = /^\s*patch rejected:\s*writing outside of the project;\s*rejected by user approval settings\s*$/iu;
|
|
735
|
-
const CODE_MODE_NATIVE_PATCH_SOURCE_RE = /^\s*(?:\/\/[^\r\n]*\r?\n\s*)?(?:const|let)\s+([A-Za-z_$][\w$]*)\s*=\s*await\s+tools\.apply_patch\(\s*("(?:\\[\s\S]|[^"\\])*")\s*\)\s*;?\s*text\(\s*\1\s*\)\s*;?\s*$/u;
|
|
736
|
-
const CODE_MODE_NATIVE_PATCH_RESULT_RE = /^\s*Script (completed|failed)\s*\r?\nWall time\s+\d+(?:\.\d+)?\s+seconds\s*\r?\nOutput:\s*([\s\S]*?)\s*$/iu;
|
|
737
|
-
const MAX_TOOL_APPROVAL_REVIEWS = 16;
|
|
738
|
-
function toolApprovalReviewOutcome(state) {
|
|
739
|
-
return state.denied ? "denied" : state.unresolvedReviewIds === null || state.unresolvedReviewIds.size > 0 ? "reviewing" : "approved";
|
|
740
|
-
}
|
|
741
|
-
function readCodeModeNativePatchInput(source) {
|
|
742
|
-
if (typeof source !== "string") return;
|
|
743
|
-
const match = CODE_MODE_NATIVE_PATCH_SOURCE_RE.exec(source);
|
|
744
|
-
if (!match?.[2]) return;
|
|
745
|
-
try {
|
|
746
|
-
const patch = JSON.parse(match[2]);
|
|
747
|
-
return typeof patch === "string" && /^\*\*\* Begin Patch\r?\n[\s\S]*\r?\n\*\*\* End Patch(?:\r?\n)?$/u.test(patch) ? patch : void 0;
|
|
748
|
-
} catch {
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
function readInterceptedNativePatchInput(command) {
|
|
753
|
-
if (typeof command !== "string") return;
|
|
754
|
-
const lines = command.replace(/\r\n?/gu, "\n").split("\n");
|
|
755
|
-
const patchStart = lines.indexOf("*** Begin Patch");
|
|
756
|
-
const invocation = /^[\t ]*(?:cd[\t ]+(?:'([^'\n]+)'|([A-Za-z0-9_./-]+))[\t ]+&&[\t ]+)?apply_patch[\t ]*<<-?[\t ]*'([^'\n]+)'[\t ]*$/u.exec(lines[0] ?? "");
|
|
757
|
-
if (!invocation || patchStart !== 1) return;
|
|
758
|
-
const patchEnd = lines.indexOf("*** End Patch", patchStart + 1);
|
|
759
|
-
const cwd = invocation[1] ?? invocation[2];
|
|
760
|
-
const delimiter = invocation[3];
|
|
761
|
-
if (patchEnd < 0 || lines[patchEnd + 1] !== delimiter || lines.slice(patchEnd + 2).some((line) => line.trim().length > 0)) return;
|
|
762
|
-
return {
|
|
763
|
-
input: `${lines.slice(patchStart, patchEnd + 1).join("\n")}\n`,
|
|
764
|
-
...cwd ? { cwd } : {}
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
var CodexToolTranscriptProjection = class {
|
|
768
|
-
constructor(params, threadId, turnId, progress, nextTranscriptTimestamp, options = {}) {
|
|
769
|
-
this.params = params;
|
|
770
|
-
this.threadId = threadId;
|
|
771
|
-
this.turnId = turnId;
|
|
772
|
-
this.progress = progress;
|
|
773
|
-
this.nextTranscriptTimestamp = nextTranscriptTimestamp;
|
|
774
|
-
this.options = options;
|
|
775
|
-
this.messages = [];
|
|
776
|
-
this.callIds = /* @__PURE__ */ new Set();
|
|
777
|
-
this.resultIds = /* @__PURE__ */ new Set();
|
|
778
|
-
this.namesById = /* @__PURE__ */ new Map();
|
|
779
|
-
this.trajectoryCallIds = /* @__PURE__ */ new Set();
|
|
780
|
-
this.trajectoryResultIds = /* @__PURE__ */ new Set();
|
|
781
|
-
this.trajectoryNamesById = /* @__PURE__ */ new Map();
|
|
782
|
-
this.trajectoryItemsById = /* @__PURE__ */ new Map();
|
|
783
|
-
this.afterToolCallObservedItemIds = /* @__PURE__ */ new Set();
|
|
784
|
-
this.nativeMcpAppResultDetails = /* @__PURE__ */ new Map();
|
|
785
|
-
this.nativeMcpAppResultDetailsAttempted = /* @__PURE__ */ new Set();
|
|
786
|
-
this.approvalReviewsByCallId = /* @__PURE__ */ new Map();
|
|
787
|
-
this.rawNativeToolOutputByCallId = /* @__PURE__ */ new Map();
|
|
788
|
-
this.pendingRawPatchOutputIds = /* @__PURE__ */ new Set();
|
|
789
|
-
this.codeModeNativePatchInputsByCallId = /* @__PURE__ */ new Map();
|
|
790
|
-
}
|
|
791
|
-
get transcriptMessages() {
|
|
792
|
-
return this.messages;
|
|
793
|
-
}
|
|
794
|
-
recordToolApprovalReview(toolCallId, reviewId, status, review) {
|
|
795
|
-
const state = this.approvalReviewsByCallId.get(toolCallId) ?? {
|
|
796
|
-
reviews: [],
|
|
797
|
-
denied: false,
|
|
798
|
-
unresolvedReviewIds: /* @__PURE__ */ new Set()
|
|
799
|
-
};
|
|
800
|
-
state.reviews = [...state.reviews.filter((candidate) => candidate.id !== reviewId), review].slice(-16);
|
|
801
|
-
state.denied ||= [
|
|
802
|
-
"denied",
|
|
803
|
-
"timed_out",
|
|
804
|
-
"aborted"
|
|
805
|
-
].includes(status);
|
|
806
|
-
const unresolved = state.unresolvedReviewIds;
|
|
807
|
-
if (status === "in_progress") state.unresolvedReviewIds = unresolved && (unresolved.size < MAX_TOOL_APPROVAL_REVIEWS || unresolved.has(reviewId)) ? unresolved.add(reviewId) : null;
|
|
808
|
-
else unresolved?.delete(reviewId);
|
|
809
|
-
this.approvalReviewsByCallId.set(toolCallId, state);
|
|
810
|
-
return toolApprovalReviewOutcome(state);
|
|
811
|
-
}
|
|
812
|
-
finalizeToolApprovalReviews(toolCallId) {
|
|
813
|
-
const state = this.approvalReviewsByCallId.get(toolCallId);
|
|
814
|
-
if (!state) return;
|
|
815
|
-
state.unresolvedReviewIds = /* @__PURE__ */ new Set();
|
|
816
|
-
return toolApprovalReviewOutcome(state);
|
|
817
|
-
}
|
|
818
|
-
recordDynamicToolCall(params) {
|
|
819
|
-
this.recordToolCall({
|
|
820
|
-
id: params.callId,
|
|
821
|
-
name: params.tool,
|
|
822
|
-
arguments: sanitizeCodexToolArguments(params.arguments)
|
|
823
|
-
});
|
|
824
|
-
}
|
|
825
|
-
recordDynamicToolResult(params, resultContentSource) {
|
|
826
|
-
this.recordToolResult({
|
|
827
|
-
id: params.callId,
|
|
828
|
-
name: params.tool,
|
|
829
|
-
text: collectDynamicToolContentText(params.contentItems),
|
|
830
|
-
isError: !params.success,
|
|
831
|
-
details: params.details,
|
|
832
|
-
...resultContentSource ? { resultContentSource } : {}
|
|
833
|
-
});
|
|
834
|
-
}
|
|
835
|
-
recordNativeToolCall(item) {
|
|
836
|
-
if (!item || !shouldRecordNativeToolTranscript(item)) return;
|
|
837
|
-
const name = itemName(item);
|
|
838
|
-
if (name) this.recordToolCall({
|
|
839
|
-
id: item.id,
|
|
840
|
-
name,
|
|
841
|
-
arguments: itemToolArgs(item)
|
|
842
|
-
});
|
|
843
|
-
}
|
|
844
|
-
recordNativeToolResult(item, details) {
|
|
845
|
-
if (!item || !shouldRecordNativeToolTranscript(item) || this.resultIds.has(item.id)) return;
|
|
846
|
-
const name = itemName(item);
|
|
847
|
-
if (name) {
|
|
848
|
-
const status = itemStatus(item);
|
|
849
|
-
const approvalTimeoutExplanation = this.progress.approvalTimeoutExplanation(item.id, status);
|
|
850
|
-
this.recordToolResult({
|
|
851
|
-
id: item.id,
|
|
852
|
-
name,
|
|
853
|
-
text: approvalTimeoutExplanation ?? this.rawNativeToolOutputByCallId.get(item.id) ?? itemTranscriptResultText(item, this.progress.outputTextByItem),
|
|
854
|
-
isError: isNonSuccessItemStatus(status),
|
|
855
|
-
details,
|
|
856
|
-
...item.type === "webSearch" ? { resultContentSource: "network" } : {}
|
|
857
|
-
});
|
|
858
|
-
this.progress.approvalTimeoutKinds.delete(item.id);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
recordRawNativeToolItem(item) {
|
|
862
|
-
const type = typeof item.type === "string" ? item.type : void 0;
|
|
863
|
-
const callId = typeof item.call_id === "string" ? item.call_id : typeof item.callId === "string" ? item.callId : void 0;
|
|
864
|
-
if (!callId) return;
|
|
865
|
-
if ((type === "custom_tool_call" || type === "function_call") && (item.name === "apply_patch" || item.name === "exec_command" || item.name === "exec")) {
|
|
866
|
-
let args;
|
|
867
|
-
if (type === "custom_tool_call" && item.name === "apply_patch" && typeof item.input === "string") args = { input: item.input };
|
|
868
|
-
else if (type === "custom_tool_call" && item.name === "exec") {
|
|
869
|
-
const input = readCodeModeNativePatchInput(item.input);
|
|
870
|
-
if (input) this.codeModeNativePatchInputsByCallId.set(callId, input);
|
|
871
|
-
return;
|
|
872
|
-
} else if (type === "function_call" && typeof item.arguments === "string") try {
|
|
873
|
-
const parsed = JSON.parse(item.arguments);
|
|
874
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
875
|
-
const parsedArguments = parsed;
|
|
876
|
-
if (item.name === "apply_patch") args = parsedArguments;
|
|
877
|
-
else {
|
|
878
|
-
const patch = readInterceptedNativePatchInput(typeof parsedArguments.cmd === "string" ? parsedArguments.cmd : typeof parsedArguments.command === "string" ? parsedArguments.command : void 0);
|
|
879
|
-
if (patch) {
|
|
880
|
-
const workdir = typeof parsedArguments.workdir === "string" ? parsedArguments.workdir : typeof parsedArguments.cwd === "string" ? parsedArguments.cwd : void 0;
|
|
881
|
-
const cwd = patch.cwd ? workdir && !path.isAbsolute(patch.cwd) ? path.join(workdir, patch.cwd) : patch.cwd : workdir;
|
|
882
|
-
args = {
|
|
883
|
-
input: patch.input,
|
|
884
|
-
...cwd ? { cwd } : {}
|
|
885
|
-
};
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
} catch {
|
|
890
|
-
return;
|
|
891
|
-
}
|
|
892
|
-
if (args) {
|
|
893
|
-
this.pendingRawPatchOutputIds.add(callId);
|
|
894
|
-
this.recordToolCall({
|
|
895
|
-
id: callId,
|
|
896
|
-
name: "apply_patch",
|
|
897
|
-
arguments: args
|
|
898
|
-
});
|
|
899
|
-
}
|
|
900
|
-
return;
|
|
901
|
-
}
|
|
902
|
-
if (type !== "custom_tool_call_output" && type !== "function_call_output" || this.namesById.get(callId) !== "apply_patch" && !this.codeModeNativePatchInputsByCallId.has(callId)) return;
|
|
903
|
-
this.pendingRawPatchOutputIds.delete(callId);
|
|
904
|
-
const text = typeof item.output === "string" ? item.output : Array.isArray(item.output) ? collectDynamicToolContentText(item.output) : "";
|
|
905
|
-
if (!text.trim()) return;
|
|
906
|
-
const codeModePatchInput = this.codeModeNativePatchInputsByCallId.get(callId);
|
|
907
|
-
if (codeModePatchInput) {
|
|
908
|
-
this.codeModeNativePatchInputsByCallId.delete(callId);
|
|
909
|
-
const execution = CODE_MODE_NATIVE_PATCH_RESULT_RE.exec(text);
|
|
910
|
-
if (execution?.[1]?.toLowerCase() === "completed" && execution[2]?.trim() === "{}") return;
|
|
911
|
-
if (execution?.[1]?.toLowerCase() === "failed") {
|
|
912
|
-
const failure = execution[2]?.replace(/^Script error:\s*/iu, "").trim() || text;
|
|
913
|
-
this.recordToolCall({
|
|
914
|
-
id: callId,
|
|
915
|
-
name: "apply_patch",
|
|
916
|
-
arguments: { input: codeModePatchInput }
|
|
917
|
-
});
|
|
918
|
-
this.recordToolResult({
|
|
919
|
-
id: callId,
|
|
920
|
-
name: "apply_patch",
|
|
921
|
-
text: failure,
|
|
922
|
-
isError: true
|
|
923
|
-
});
|
|
924
|
-
}
|
|
925
|
-
return;
|
|
926
|
-
}
|
|
927
|
-
this.rawNativeToolOutputByCallId.set(callId, text);
|
|
928
|
-
const result = this.messages.find((message) => message.role === "toolResult" && message.toolCallId === callId);
|
|
929
|
-
if (!result) {
|
|
930
|
-
if (NATIVE_PATCH_REJECTION_RE.test(text)) this.recordToolResult({
|
|
931
|
-
id: callId,
|
|
932
|
-
name: "apply_patch",
|
|
933
|
-
text,
|
|
934
|
-
isError: true
|
|
935
|
-
});
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
|
-
result.content = this.createToolResultMessage({
|
|
939
|
-
id: callId,
|
|
940
|
-
name: "apply_patch",
|
|
941
|
-
text,
|
|
942
|
-
isError: result.isError
|
|
943
|
-
}).content;
|
|
944
|
-
}
|
|
945
|
-
async prepareNativeToolResultDetails(item) {
|
|
946
|
-
const preparedDetails = await this.prepareNativeMcpAppResultDetails(item);
|
|
947
|
-
const approvalReviewState = item ? this.approvalReviewsByCallId.get(item.id) : void 0;
|
|
948
|
-
const reviewDetails = approvalReviewState ? {
|
|
949
|
-
approvalReviews: approvalReviewState.reviews,
|
|
950
|
-
approvalReviewOutcome: toolApprovalReviewOutcome(approvalReviewState)
|
|
951
|
-
} : void 0;
|
|
952
|
-
return reviewDetails ? isJsonObject(preparedDetails) ? {
|
|
953
|
-
...preparedDetails,
|
|
954
|
-
...reviewDetails
|
|
955
|
-
} : {
|
|
956
|
-
...preparedDetails !== void 0 ? { toolDetails: preparedDetails } : {},
|
|
957
|
-
...reviewDetails
|
|
958
|
-
} : preparedDetails;
|
|
959
|
-
}
|
|
960
|
-
async prepareNativeMcpAppResultDetails(item) {
|
|
961
|
-
if (!item || item.type !== "mcpToolCall" || itemStatus(item) === "running") return;
|
|
962
|
-
if (this.nativeMcpAppResultDetails.has(item.id)) return this.nativeMcpAppResultDetails.get(item.id);
|
|
963
|
-
if (this.nativeMcpAppResultDetailsAttempted.has(item.id) || !this.options.prepareNativeMcpAppResultDetails) return;
|
|
964
|
-
this.nativeMcpAppResultDetailsAttempted.add(item.id);
|
|
965
|
-
try {
|
|
966
|
-
const details = await this.options.prepareNativeMcpAppResultDetails(item);
|
|
967
|
-
if (details !== void 0) this.nativeMcpAppResultDetails.set(item.id, details);
|
|
968
|
-
return details;
|
|
969
|
-
} catch (error) {
|
|
970
|
-
embeddedAgentLog.debug("codex native MCP App preview preparation failed", {
|
|
971
|
-
itemId: item.id,
|
|
972
|
-
error
|
|
973
|
-
});
|
|
974
|
-
return;
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
recordTrajectoryEvent(params) {
|
|
978
|
-
if (params.phase === "start") {
|
|
979
|
-
this.trajectoryCallIds.add(params.item.id);
|
|
980
|
-
this.trajectoryNamesById.set(params.item.id, params.name);
|
|
981
|
-
this.trajectoryItemsById.set(params.item.id, params.item);
|
|
982
|
-
this.options.trajectoryRecorder?.recordEvent("tool.call", {
|
|
983
|
-
threadId: this.threadId,
|
|
984
|
-
turnId: this.turnId,
|
|
985
|
-
itemId: params.item.id,
|
|
986
|
-
toolCallId: params.item.id,
|
|
987
|
-
name: params.name,
|
|
988
|
-
arguments: params.args
|
|
989
|
-
});
|
|
990
|
-
return;
|
|
991
|
-
}
|
|
992
|
-
this.trajectoryResultIds.add(params.item.id);
|
|
993
|
-
const toolResult = itemToolResult(params.item).result;
|
|
994
|
-
const output = this.progress.approvalTimeoutExplanation(params.item.id, params.status) ?? itemOutputText(params.item, this.progress.outputTextByItem);
|
|
995
|
-
this.options.trajectoryRecorder?.recordEvent("tool.result", {
|
|
996
|
-
threadId: this.threadId,
|
|
997
|
-
turnId: this.turnId,
|
|
998
|
-
itemId: params.item.id,
|
|
999
|
-
toolCallId: params.item.id,
|
|
1000
|
-
name: params.name,
|
|
1001
|
-
status: params.status,
|
|
1002
|
-
isError: isNonSuccessItemStatus(params.status),
|
|
1003
|
-
...toolResult ? { result: toolResult } : {},
|
|
1004
|
-
...output ? { output } : {}
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
emitAfterToolCallObservation(item) {
|
|
1008
|
-
if (!this.shouldEmitAfterToolCallObservation(item)) return;
|
|
1009
|
-
const name = itemName(item);
|
|
1010
|
-
const status = itemStatus(item);
|
|
1011
|
-
if (!name || status === "running") return;
|
|
1012
|
-
this.afterToolCallObservedItemIds.add(item.id);
|
|
1013
|
-
const result = itemToolResult(item).result;
|
|
1014
|
-
const error = this.progress.approvalTimeoutExplanation(item.id, status) ?? itemToolError(item, status, this.progress.outputTextByItem);
|
|
1015
|
-
const startedAt = resolveStartedAtFromDurationMs(item.durationMs);
|
|
1016
|
-
const hookParams = {
|
|
1017
|
-
toolName: name,
|
|
1018
|
-
toolCallId: item.id,
|
|
1019
|
-
runId: this.params.runId,
|
|
1020
|
-
agentId: this.params.agentId,
|
|
1021
|
-
sessionId: this.params.sessionId,
|
|
1022
|
-
sessionKey: this.params.sessionKey,
|
|
1023
|
-
startArgs: itemToolArgs(item) ?? {},
|
|
1024
|
-
...result !== void 0 ? { result } : {},
|
|
1025
|
-
...error ? { error } : {},
|
|
1026
|
-
...startedAt !== void 0 ? { startedAt } : {}
|
|
1027
|
-
};
|
|
1028
|
-
setImmediate(() => {
|
|
1029
|
-
runAgentHarnessAfterToolCallHook(hookParams);
|
|
1030
|
-
});
|
|
1031
|
-
}
|
|
1032
|
-
synthesizeMissingToolResults(params) {
|
|
1033
|
-
if (!params.synthesize) return;
|
|
1034
|
-
const missingTranscriptIds = [...this.callIds].filter((id) => !this.resultIds.has(id));
|
|
1035
|
-
const missingTrajectoryIds = [...this.trajectoryCallIds].filter((id) => !this.trajectoryResultIds.has(id));
|
|
1036
|
-
if (missingTranscriptIds.length === 0 && missingTrajectoryIds.length === 0) return;
|
|
1037
|
-
for (const id of missingTranscriptIds) {
|
|
1038
|
-
const name = this.namesById.get(id) ?? this.trajectoryNamesById.get(id);
|
|
1039
|
-
if (name) this.recordToolResult({
|
|
1040
|
-
id,
|
|
1041
|
-
name,
|
|
1042
|
-
text: formatMissingToolResultError({
|
|
1043
|
-
id,
|
|
1044
|
-
name
|
|
1045
|
-
}),
|
|
1046
|
-
isError: true,
|
|
1047
|
-
details: { reason: "missing_tool_result" }
|
|
1048
|
-
});
|
|
1049
|
-
}
|
|
1050
|
-
for (const id of missingTrajectoryIds) {
|
|
1051
|
-
const name = this.trajectoryNamesById.get(id) ?? this.namesById.get(id);
|
|
1052
|
-
if (!name) continue;
|
|
1053
|
-
this.trajectoryResultIds.add(id);
|
|
1054
|
-
const text = formatMissingToolResultError({
|
|
1055
|
-
id,
|
|
1056
|
-
name
|
|
1057
|
-
});
|
|
1058
|
-
this.options.trajectoryRecorder?.recordEvent("tool.result", {
|
|
1059
|
-
threadId: this.threadId,
|
|
1060
|
-
turnId: this.turnId,
|
|
1061
|
-
itemId: id,
|
|
1062
|
-
toolCallId: id,
|
|
1063
|
-
name,
|
|
1064
|
-
status: "failed",
|
|
1065
|
-
isError: true,
|
|
1066
|
-
result: {
|
|
1067
|
-
status: "failed",
|
|
1068
|
-
reason: "missing_tool_result"
|
|
1069
|
-
},
|
|
1070
|
-
output: text
|
|
1071
|
-
});
|
|
1072
|
-
}
|
|
1073
|
-
if (params.terminalDisposition === "tool_error") {
|
|
1074
|
-
this.recordMissingToolError(missingTranscriptIds, missingTrajectoryIds);
|
|
1075
|
-
return;
|
|
1076
|
-
}
|
|
1077
|
-
if (params.terminalDisposition === "diagnostic_only") return;
|
|
1078
|
-
const missingCount = (/* @__PURE__ */ new Set([...missingTranscriptIds, ...missingTrajectoryIds])).size;
|
|
1079
|
-
return missingCount === 1 ? MISSING_TOOL_RESULT_ERROR : `${MISSING_TOOL_RESULT_ERROR} missingToolResultCount=${missingCount}`;
|
|
1080
|
-
}
|
|
1081
|
-
async readMirroredSessionMessages(signal) {
|
|
1082
|
-
return await readCodexMirroredSessionHistoryMessages({
|
|
1083
|
-
agentId: this.params.agentId,
|
|
1084
|
-
sessionFile: this.params.sessionFile,
|
|
1085
|
-
sessionId: this.params.sessionId,
|
|
1086
|
-
sessionKey: this.params.sessionKey,
|
|
1087
|
-
sessionTarget: this.params.sessionTarget
|
|
1088
|
-
}, void 0, "native-evidence", signal) ?? [];
|
|
1089
|
-
}
|
|
1090
|
-
recordToolCall(params) {
|
|
1091
|
-
if (!params.id || !params.name || this.callIds.has(params.id)) return;
|
|
1092
|
-
this.callIds.add(params.id);
|
|
1093
|
-
this.namesById.set(params.id, params.name);
|
|
1094
|
-
this.progress.recordTranscriptCall(params);
|
|
1095
|
-
const message = attachCodexMirrorIdentity(this.createToolCallMessage(params), `${this.turnId}:tool:${params.id}:call`);
|
|
1096
|
-
this.messages.push(message);
|
|
1097
|
-
this.options.checkpointMessage?.({ read: () => message });
|
|
1098
|
-
}
|
|
1099
|
-
recordToolResult(params) {
|
|
1100
|
-
if (!params.id || !params.name || this.resultIds.has(params.id)) return;
|
|
1101
|
-
this.resultIds.add(params.id);
|
|
1102
|
-
this.progress.recordTranscriptResult(params);
|
|
1103
|
-
const message = attachCodexMirrorIdentity(this.createToolResultMessage(params), `${this.turnId}:tool:${params.id}:result`);
|
|
1104
|
-
this.messages.push(message);
|
|
1105
|
-
this.options.checkpointMessage?.({
|
|
1106
|
-
read: () => message,
|
|
1107
|
-
ready: () => !this.pendingRawPatchOutputIds.has(params.id)
|
|
1108
|
-
});
|
|
1109
|
-
}
|
|
1110
|
-
recordMissingToolError(missingTranscriptIds, missingTrajectoryIds) {
|
|
1111
|
-
const firstMissingId = missingTranscriptIds.find((id) => Boolean(this.namesById.get(id))) ?? missingTrajectoryIds.find((id) => Boolean(this.trajectoryNamesById.get(id) ?? this.namesById.get(id)));
|
|
1112
|
-
if (!firstMissingId) return;
|
|
1113
|
-
const name = this.namesById.get(firstMissingId) ?? this.trajectoryNamesById.get(firstMissingId);
|
|
1114
|
-
if (!name) return;
|
|
1115
|
-
const item = this.trajectoryItemsById.get(firstMissingId);
|
|
1116
|
-
const meta = item ? itemMeta(item, this.progress.toolProgressDetailMode()) : this.progress.getToolMeta(firstMissingId)?.meta;
|
|
1117
|
-
this.progress.setLastToolError({
|
|
1118
|
-
toolName: name,
|
|
1119
|
-
...meta ? { meta } : {},
|
|
1120
|
-
error: formatMissingToolResultError({
|
|
1121
|
-
id: firstMissingId,
|
|
1122
|
-
name
|
|
1123
|
-
}),
|
|
1124
|
-
...item && isMutatingNativeToolItem(item) ? { mutatingAction: true } : {}
|
|
1125
|
-
});
|
|
1126
|
-
}
|
|
1127
|
-
shouldEmitAfterToolCallObservation(item) {
|
|
1128
|
-
if (!shouldSynthesizeToolProgressForItem(item) || this.afterToolCallObservedItemIds.has(item.id)) return false;
|
|
1129
|
-
return !(this.options.nativePostToolUseRelayEnabled && isNativePostToolUseRelayItem(item));
|
|
1130
|
-
}
|
|
1131
|
-
createToolCallMessage(params) {
|
|
1132
|
-
const args = normalizeToolTranscriptArguments(params.arguments);
|
|
1133
|
-
const attribution = resolveCodexLocalRuntimeAttribution(this.params);
|
|
1134
|
-
return {
|
|
1135
|
-
role: "assistant",
|
|
1136
|
-
content: [{
|
|
1137
|
-
type: "toolCall",
|
|
1138
|
-
id: params.id,
|
|
1139
|
-
name: params.name,
|
|
1140
|
-
arguments: args,
|
|
1141
|
-
input: args
|
|
1142
|
-
}],
|
|
1143
|
-
api: attribution.api ?? "openai-chatgpt-responses",
|
|
1144
|
-
provider: attribution.provider,
|
|
1145
|
-
model: this.params.modelId,
|
|
1146
|
-
usage: ZERO_USAGE,
|
|
1147
|
-
stopReason: "toolUse",
|
|
1148
|
-
timestamp: this.nextTranscriptTimestamp()
|
|
1149
|
-
};
|
|
1150
|
-
}
|
|
1151
|
-
createToolResultMessage(params) {
|
|
1152
|
-
const text = truncateToolTranscriptText(params.text?.trim() || toolResultStatusText(params));
|
|
1153
|
-
return {
|
|
1154
|
-
role: "toolResult",
|
|
1155
|
-
toolCallId: params.id,
|
|
1156
|
-
toolName: params.name,
|
|
1157
|
-
isError: params.isError,
|
|
1158
|
-
content: [{
|
|
1159
|
-
type: "toolResult",
|
|
1160
|
-
id: params.id,
|
|
1161
|
-
name: params.name,
|
|
1162
|
-
toolName: params.name,
|
|
1163
|
-
toolCallId: params.id,
|
|
1164
|
-
toolUseId: params.id,
|
|
1165
|
-
tool_use_id: params.id,
|
|
1166
|
-
content: text,
|
|
1167
|
-
text
|
|
1168
|
-
}],
|
|
1169
|
-
...params.details !== void 0 ? { details: params.details } : {},
|
|
1170
|
-
...params.resultContentSource ? { __openclaw: { resultContentSource: params.resultContentSource } } : {},
|
|
1171
|
-
timestamp: this.nextTranscriptTimestamp()
|
|
1172
|
-
};
|
|
1173
|
-
}
|
|
1174
|
-
};
|
|
1175
|
-
function formatMissingToolResultError(params) {
|
|
1176
|
-
return `${MISSING_TOOL_RESULT_ERROR} toolCallId=${params.id}; toolName=${params.name}`;
|
|
1177
|
-
}
|
|
1178
|
-
function toolResultStatusText(params) {
|
|
1179
|
-
return params.isError ? `${params.name} failed` : `${params.name} completed`;
|
|
1180
|
-
}
|
|
1181
|
-
function resolveStartedAtFromDurationMs(durationMs) {
|
|
1182
|
-
if (typeof durationMs !== "number" || !Number.isFinite(durationMs)) return;
|
|
1183
|
-
return asDateTimestampMs(Date.now() - Math.max(0, durationMs));
|
|
1184
|
-
}
|
|
1185
|
-
//#endregion
|
|
1186
|
-
//#region extensions/codex/src/app-server/event-projector-events.ts
|
|
1187
|
-
/** Downstream event consumers must never corrupt the canonical Codex turn projection. */
|
|
1188
|
-
function emitCodexAgentEvent(params, event) {
|
|
1189
|
-
try {
|
|
1190
|
-
emitAgentEvent({
|
|
1191
|
-
runId: params.runId,
|
|
1192
|
-
stream: event.stream,
|
|
1193
|
-
data: event.data,
|
|
1194
|
-
...params.sessionKey ? { sessionKey: params.sessionKey } : {}
|
|
1195
|
-
});
|
|
1196
|
-
} catch (error) {
|
|
1197
|
-
embeddedAgentLog.debug("codex app-server global agent event emit failed", { error });
|
|
1198
|
-
}
|
|
1199
|
-
try {
|
|
1200
|
-
const maybePromise = params.onAgentEvent?.(event);
|
|
1201
|
-
Promise.resolve(maybePromise).catch((error) => {
|
|
1202
|
-
embeddedAgentLog.debug("codex app-server agent event handler rejected", { error });
|
|
1203
|
-
});
|
|
1204
|
-
} catch (error) {
|
|
1205
|
-
embeddedAgentLog.debug("codex app-server agent event handler threw", { error });
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
function guardianActionCommand(action) {
|
|
1209
|
-
if (!action) return;
|
|
1210
|
-
const directLabel = readStringField(action, "command") ?? readStringField(action, "target") ?? readStringField(action, "toolTitle") ?? readStringField(action, "reason");
|
|
1211
|
-
if (directLabel) return directLabel;
|
|
1212
|
-
const server = readStringField(action, "connectorName") ?? readStringField(action, "server");
|
|
1213
|
-
const tool = readStringField(action, "toolName");
|
|
1214
|
-
if (server && tool) return `${server}/${tool}`;
|
|
1215
|
-
const argv = Array.isArray(action.argv) ? action.argv.filter((value) => typeof value === "string") : [];
|
|
1216
|
-
return argv.length > 0 ? argv.join(" ") : readStringField(action, "program");
|
|
1217
|
-
}
|
|
1218
|
-
function normalizeApprovalReviewStatus(status) {
|
|
1219
|
-
return status === "inProgress" ? "in_progress" : status === "timedOut" ? "timed_out" : status;
|
|
1220
|
-
}
|
|
1221
|
-
const GUARDIAN_TIMEOUT_WARNING = "Automatic approval review timed out while evaluating the requested approval.";
|
|
1222
|
-
const LOG_ONLY_CODEX_WARNING_PATTERNS = [/^Configured service tier `[^`\r\n]+` is not advertised as supported for model `[^`\r\n]+` and will be omitted from requests\.$/, /^Code Mode is enabled in configuration, but model `[^`\r\n]+` does not advertise Code Mode support\. This may degrade model performance\. Disable `features\.code_mode` and `features\.code_mode_only`, or select a model whose metadata enables Code Mode\.$/];
|
|
1223
|
-
function projectNormalizedToolItem(params) {
|
|
1224
|
-
const { item } = params;
|
|
1225
|
-
if (!item || !shouldSynthesizeToolProgressForItem(item)) return;
|
|
1226
|
-
const name = itemName(item);
|
|
1227
|
-
if (!name) return;
|
|
1228
|
-
const status = params.phase === "result" ? itemStatus(item) : "running";
|
|
1229
|
-
const args = itemToolArgs(item);
|
|
1230
|
-
const commandBearing = isCommandBearingToolItem(item, args);
|
|
1231
|
-
const meta = itemMeta(item, params.detailMode);
|
|
1232
|
-
return {
|
|
1233
|
-
name,
|
|
1234
|
-
status,
|
|
1235
|
-
args,
|
|
1236
|
-
meta,
|
|
1237
|
-
event: shouldEmitTranscriptToolProgress(name, args) ? {
|
|
1238
|
-
stream: "tool",
|
|
1239
|
-
data: {
|
|
1240
|
-
phase: params.phase,
|
|
1241
|
-
name,
|
|
1242
|
-
itemId: item.id,
|
|
1243
|
-
toolCallId: item.id,
|
|
1244
|
-
...meta ? { meta } : {},
|
|
1245
|
-
...commandBearing ? { commandBearing: true } : {},
|
|
1246
|
-
...params.phase === "start" && args ? { args } : {},
|
|
1247
|
-
...params.phase === "result" ? {
|
|
1248
|
-
status,
|
|
1249
|
-
isError: isNonSuccessItemStatus(status),
|
|
1250
|
-
...itemToolResult(item)
|
|
1251
|
-
} : {}
|
|
1252
|
-
}
|
|
1253
|
-
} : void 0
|
|
1254
|
-
};
|
|
1255
|
-
}
|
|
1256
|
-
var CodexEventProjection = class {
|
|
1257
|
-
constructor(threadId, turnId, emitAgentEvent, toolProgress, toolTranscript, onNativeToolResultRecorded) {
|
|
1258
|
-
this.threadId = threadId;
|
|
1259
|
-
this.turnId = turnId;
|
|
1260
|
-
this.emitAgentEvent = emitAgentEvent;
|
|
1261
|
-
this.toolProgress = toolProgress;
|
|
1262
|
-
this.toolTranscript = toolTranscript;
|
|
1263
|
-
this.onNativeToolResultRecorded = onNativeToolResultRecorded;
|
|
1264
|
-
this.reviewCount = 0;
|
|
1265
|
-
}
|
|
1266
|
-
get guardianReviewCount() {
|
|
1267
|
-
return this.reviewCount;
|
|
1268
|
-
}
|
|
1269
|
-
emitCompactionEnd(itemId, completed) {
|
|
1270
|
-
this.emitAgentEvent({
|
|
1271
|
-
stream: "compaction",
|
|
1272
|
-
data: {
|
|
1273
|
-
phase: "end",
|
|
1274
|
-
backend: "codex-app-server",
|
|
1275
|
-
completed,
|
|
1276
|
-
threadId: this.threadId,
|
|
1277
|
-
turnId: this.turnId,
|
|
1278
|
-
itemId
|
|
1279
|
-
}
|
|
1280
|
-
});
|
|
1281
|
-
}
|
|
1282
|
-
handleGuardianReview(method, params) {
|
|
1283
|
-
this.reviewCount += 1;
|
|
1284
|
-
const review = isJsonObject(params.review) ? params.review : void 0;
|
|
1285
|
-
const action = isJsonObject(params.action) ? params.action : void 0;
|
|
1286
|
-
const reviewId = readStringField(params, "reviewId");
|
|
1287
|
-
const targetItemId = readNullableString$1(params, "targetItemId");
|
|
1288
|
-
const command = guardianActionCommand(action);
|
|
1289
|
-
const reviewStatus = review ? readStringField(review, "status") : void 0;
|
|
1290
|
-
const status = normalizeApprovalReviewStatus(reviewStatus);
|
|
1291
|
-
const riskLevel = review ? readStringField(review, "riskLevel") : void 0;
|
|
1292
|
-
const userAuthorization = review ? readStringField(review, "userAuthorization") : void 0;
|
|
1293
|
-
const rationale = review ? readNullableString$1(review, "rationale") : void 0;
|
|
1294
|
-
const expectedWarning = status === "timed_out" ? GUARDIAN_TIMEOUT_WARNING : rationale && riskLevel && userAuthorization && (status === "approved" || status === "denied") ? `Automatic approval review ${status} (risk: ${riskLevel}, authorization: ${userAuthorization}): ${rationale}` : void 0;
|
|
1295
|
-
if (Boolean(targetItemId) && Boolean(reviewId) && this.pendingGuardianWarning === expectedWarning) this.pendingGuardianWarning = void 0;
|
|
1296
|
-
else this.flushPendingGuardianWarning();
|
|
1297
|
-
const threadId = readStringField(params, "threadId") ?? this.threadId;
|
|
1298
|
-
const turnId = readStringField(params, "turnId") ?? this.turnId;
|
|
1299
|
-
if (method.endsWith("/started")) this.activeGuardianReview = {
|
|
1300
|
-
reviewId,
|
|
1301
|
-
targetItemId,
|
|
1302
|
-
command,
|
|
1303
|
-
threadId,
|
|
1304
|
-
turnId
|
|
1305
|
-
};
|
|
1306
|
-
this.emitAgentEvent({
|
|
1307
|
-
stream: "codex_app_server.guardian",
|
|
1308
|
-
data: {
|
|
1309
|
-
method,
|
|
1310
|
-
phase: method.endsWith("/started") ? "started" : "completed",
|
|
1311
|
-
threadId,
|
|
1312
|
-
turnId,
|
|
1313
|
-
reviewId,
|
|
1314
|
-
targetItemId,
|
|
1315
|
-
decisionSource: readStringField(params, "decisionSource"),
|
|
1316
|
-
status: reviewStatus,
|
|
1317
|
-
riskLevel,
|
|
1318
|
-
userAuthorization,
|
|
1319
|
-
rationale,
|
|
1320
|
-
actionType: action ? readStringField(action, "type") : void 0,
|
|
1321
|
-
command
|
|
1322
|
-
}
|
|
1323
|
-
});
|
|
1324
|
-
if (reviewId && targetItemId && status) {
|
|
1325
|
-
const approvalReview = {
|
|
1326
|
-
id: reviewId,
|
|
1327
|
-
label: "Guardian",
|
|
1328
|
-
status,
|
|
1329
|
-
...riskLevel ? { riskLevel } : {},
|
|
1330
|
-
...userAuthorization ? { userAuthorization } : {},
|
|
1331
|
-
...rationale ? { rationale } : {}
|
|
1332
|
-
};
|
|
1333
|
-
const approvalReviewOutcome = this.toolTranscript.recordToolApprovalReview(targetItemId, reviewId, status, approvalReview);
|
|
1334
|
-
this.emitAgentEvent({
|
|
1335
|
-
stream: "tool",
|
|
1336
|
-
data: {
|
|
1337
|
-
phase: "review",
|
|
1338
|
-
toolCallId: targetItemId,
|
|
1339
|
-
hideFromChannelProgress: true,
|
|
1340
|
-
approvalReviewOutcome,
|
|
1341
|
-
review: approvalReview
|
|
1342
|
-
}
|
|
1343
|
-
});
|
|
1344
|
-
}
|
|
1345
|
-
if (method.endsWith("/completed") && this.activeGuardianReview?.reviewId === reviewId) this.activeGuardianReview = void 0;
|
|
1346
|
-
}
|
|
1347
|
-
handleGuardianWarning(params) {
|
|
1348
|
-
this.flushPendingGuardianWarning();
|
|
1349
|
-
const message = readStringField(params, "message");
|
|
1350
|
-
if (message) {
|
|
1351
|
-
this.pendingGuardianWarning = message;
|
|
1352
|
-
return;
|
|
1353
|
-
}
|
|
1354
|
-
this.emitAgentEvent({
|
|
1355
|
-
stream: "codex_app_server.guardian",
|
|
1356
|
-
data: {
|
|
1357
|
-
phase: "warning",
|
|
1358
|
-
message
|
|
1359
|
-
}
|
|
1360
|
-
});
|
|
1361
|
-
}
|
|
1362
|
-
handleWarning(params) {
|
|
1363
|
-
const message = [readStringField(params, "summary") ?? readStringField(params, "message"), readStringField(params, "details")].filter(Boolean).join("\n");
|
|
1364
|
-
if (LOG_ONLY_CODEX_WARNING_PATTERNS.some((pattern) => pattern.test(message))) embeddedAgentLog.warn(message);
|
|
1365
|
-
else if (message) this.emitAgentEvent({
|
|
1366
|
-
stream: "notice",
|
|
1367
|
-
data: {
|
|
1368
|
-
phase: "warning",
|
|
1369
|
-
message
|
|
1370
|
-
}
|
|
1371
|
-
});
|
|
1372
|
-
}
|
|
1373
|
-
handleModelRerouted(params) {
|
|
1374
|
-
const fromModel = readStringField(params, "fromModel");
|
|
1375
|
-
const toModel = readStringField(params, "toModel");
|
|
1376
|
-
const reason = readStringField(params, "reason");
|
|
1377
|
-
if (fromModel && toModel && fromModel !== toModel) this.emitAgentEvent({
|
|
1378
|
-
stream: "fallback",
|
|
1379
|
-
data: {
|
|
1380
|
-
fromModel,
|
|
1381
|
-
toModel,
|
|
1382
|
-
...reason ? { reason } : {}
|
|
1383
|
-
}
|
|
1384
|
-
});
|
|
1385
|
-
}
|
|
1386
|
-
handleRetry(params) {
|
|
1387
|
-
const rateLimited = isJsonObject(params.error) && params.error.codexErrorInfo === "rateLimitExceeded";
|
|
1388
|
-
this.emitAgentEvent({
|
|
1389
|
-
stream: "run_status",
|
|
1390
|
-
data: {
|
|
1391
|
-
phase: "retrying",
|
|
1392
|
-
message: rateLimited ? "Rate limited. The provider is retrying." : "Connection interrupted. The provider is retrying."
|
|
1393
|
-
}
|
|
1394
|
-
});
|
|
1395
|
-
}
|
|
1396
|
-
flushPendingGuardianWarning() {
|
|
1397
|
-
const pending = this.pendingGuardianWarning;
|
|
1398
|
-
if (!pending) return;
|
|
1399
|
-
this.pendingGuardianWarning = void 0;
|
|
1400
|
-
this.emitAgentEvent({
|
|
1401
|
-
stream: "codex_app_server.guardian",
|
|
1402
|
-
data: {
|
|
1403
|
-
phase: "warning",
|
|
1404
|
-
message: pending
|
|
1405
|
-
}
|
|
1406
|
-
});
|
|
1407
|
-
}
|
|
1408
|
-
handleStrictReviewRequired(params) {
|
|
1409
|
-
this.emitAgentEvent({
|
|
1410
|
-
stream: "codex_app_server.guardian",
|
|
1411
|
-
data: {
|
|
1412
|
-
method: "autoApprovalReview/strictReviewRequired",
|
|
1413
|
-
phase: "strict_review_required",
|
|
1414
|
-
threadId: readStringField(params, "threadId") ?? this.activeGuardianReview?.threadId ?? this.threadId,
|
|
1415
|
-
turnId: readStringField(params, "turnId") ?? this.activeGuardianReview?.turnId ?? this.turnId,
|
|
1416
|
-
reviewId: this.activeGuardianReview?.reviewId,
|
|
1417
|
-
targetItemId: this.activeGuardianReview?.targetItemId,
|
|
1418
|
-
command: this.activeGuardianReview?.command,
|
|
1419
|
-
startedAtMs: asFiniteNumber(params.startedAtMs)
|
|
1420
|
-
}
|
|
1421
|
-
});
|
|
1422
|
-
}
|
|
1423
|
-
handleHook(method, params) {
|
|
1424
|
-
const run = isJsonObject(params.run) ? params.run : void 0;
|
|
1425
|
-
if (!run) return;
|
|
1426
|
-
const durationMs = asFiniteNumber(run.durationMs);
|
|
1427
|
-
const entries = readHookOutputEntries(run.entries);
|
|
1428
|
-
const hookTurnId = readNullableString$1(params, "turnId");
|
|
1429
|
-
this.emitAgentEvent({
|
|
1430
|
-
stream: "codex_app_server.hook",
|
|
1431
|
-
data: {
|
|
1432
|
-
phase: method === "hook/started" ? "started" : "completed",
|
|
1433
|
-
threadId: this.threadId,
|
|
1434
|
-
turnId: hookTurnId === void 0 ? this.turnId : hookTurnId,
|
|
1435
|
-
hookRunId: readStringField(run, "id"),
|
|
1436
|
-
eventName: readStringField(run, "eventName"),
|
|
1437
|
-
handlerType: readStringField(run, "handlerType"),
|
|
1438
|
-
executionMode: readStringField(run, "executionMode"),
|
|
1439
|
-
scope: readStringField(run, "scope"),
|
|
1440
|
-
source: readStringField(run, "source"),
|
|
1441
|
-
sourcePath: readStringField(run, "sourcePath"),
|
|
1442
|
-
status: readStringField(run, "status"),
|
|
1443
|
-
statusMessage: readNullableString$1(run, "statusMessage"),
|
|
1444
|
-
...durationMs !== void 0 ? { durationMs } : {},
|
|
1445
|
-
...entries.length > 0 ? { entries } : {}
|
|
1446
|
-
}
|
|
1447
|
-
});
|
|
1448
|
-
}
|
|
1449
|
-
emitStandardItemEvent(params) {
|
|
1450
|
-
const { item } = params;
|
|
1451
|
-
if (!item) return;
|
|
1452
|
-
const activity = item.type === "subAgentActivity";
|
|
1453
|
-
if (activity && params.phase === "start") return;
|
|
1454
|
-
const subagent = activity || item.type === "collabAgentToolCall";
|
|
1455
|
-
const kind = subagent ? "tool" : itemKind(item);
|
|
1456
|
-
if (!kind) return;
|
|
1457
|
-
const name = subagent ? "subagents" : itemName(item);
|
|
1458
|
-
const commandBearing = isCommandBearingToolItem(item, itemToolArgs(item));
|
|
1459
|
-
const subagentStatus = readStringField(item, activity ? "kind" : "status");
|
|
1460
|
-
const interaction = activity && subagentStatus === "interacted";
|
|
1461
|
-
const status = subagent && subagentStatus === "interrupted" ? "failed" : activity ? subagentStatus === "completed" || interaction ? "completed" : "running" : params.phase === "start" ? "running" : itemStatus(item);
|
|
1462
|
-
const meta = subagent ? [interaction ? "message sent" : activity ? subagentStatus : status, readStringField(item, activity ? "agentPath" : "tool")].filter(Boolean).join(": ") : itemMeta(item, this.toolProgress.toolProgressDetailMode());
|
|
1463
|
-
const suppressChannelProgress = shouldSuppressChannelProgressForItem(item);
|
|
1464
|
-
this.emitAgentEvent({
|
|
1465
|
-
stream: "item",
|
|
1466
|
-
data: {
|
|
1467
|
-
itemId: activity && !interaction ? `subagent:${readStringField(item, "agentThreadId") ?? item.id}` : item.id,
|
|
1468
|
-
phase: params.phase,
|
|
1469
|
-
kind,
|
|
1470
|
-
title: itemTitle(item),
|
|
1471
|
-
status,
|
|
1472
|
-
...name ? { name } : {},
|
|
1473
|
-
...meta ? { meta } : {},
|
|
1474
|
-
...commandBearing ? { commandBearing: true } : {},
|
|
1475
|
-
...suppressChannelProgress ? { suppressChannelProgress: true } : {}
|
|
1476
|
-
}
|
|
1477
|
-
});
|
|
1478
|
-
}
|
|
1479
|
-
async emitSnapshotOnlyNativeToolProgress(params) {
|
|
1480
|
-
const { item, activeItemIds, completedItemIds, isActive } = params;
|
|
1481
|
-
if (!shouldSynthesizeToolProgressForItem(item) || !matchesCodexSnapshotTurn(item, this.turnId) || completedItemIds.has(item.id) || itemStatus(item) === "running") return;
|
|
1482
|
-
if (!activeItemIds.has(item.id)) {
|
|
1483
|
-
this.emitStandardItemEvent({
|
|
1484
|
-
phase: "start",
|
|
1485
|
-
item
|
|
1486
|
-
});
|
|
1487
|
-
await this.emitNormalizedToolItemEvent({
|
|
1488
|
-
phase: "start",
|
|
1489
|
-
item
|
|
1490
|
-
});
|
|
1491
|
-
}
|
|
1492
|
-
if (!isActive()) return;
|
|
1493
|
-
activeItemIds.delete(item.id);
|
|
1494
|
-
this.emitStandardItemEvent({
|
|
1495
|
-
phase: "end",
|
|
1496
|
-
item
|
|
1497
|
-
});
|
|
1498
|
-
await this.emitNormalizedToolItemEvent({
|
|
1499
|
-
phase: "result",
|
|
1500
|
-
item
|
|
1501
|
-
});
|
|
1502
|
-
completedItemIds.add(item.id);
|
|
1503
|
-
}
|
|
1504
|
-
async emitNormalizedToolItemEvent(params) {
|
|
1505
|
-
const projection = projectNormalizedToolItem({
|
|
1506
|
-
...params,
|
|
1507
|
-
detailMode: this.toolProgress.toolProgressDetailMode()
|
|
1508
|
-
});
|
|
1509
|
-
if (!projection || !params.item) return;
|
|
1510
|
-
const { item } = params;
|
|
1511
|
-
const { name, status, args, meta, event } = projection;
|
|
1512
|
-
const approvalReviewOutcome = params.phase === "result" ? this.toolTranscript.finalizeToolApprovalReviews(item.id) : void 0;
|
|
1513
|
-
if (event && approvalReviewOutcome) event.data.approvalReviewOutcome = approvalReviewOutcome;
|
|
1514
|
-
this.toolTranscript.recordTrajectoryEvent({
|
|
1515
|
-
phase: params.phase,
|
|
1516
|
-
item,
|
|
1517
|
-
name,
|
|
1518
|
-
args,
|
|
1519
|
-
status
|
|
1520
|
-
});
|
|
1521
|
-
if (params.phase === "result") this.toolProgress.recordNativeToolError({
|
|
1522
|
-
item,
|
|
1523
|
-
name,
|
|
1524
|
-
meta,
|
|
1525
|
-
status
|
|
1526
|
-
});
|
|
1527
|
-
if (!event) {
|
|
1528
|
-
if (params.phase === "result") {
|
|
1529
|
-
this.toolTranscript.emitAfterToolCallObservation(item);
|
|
1530
|
-
await this.onNativeToolResultRecorded?.();
|
|
1531
|
-
}
|
|
1532
|
-
return;
|
|
1533
|
-
}
|
|
1534
|
-
this.emitAgentEvent(event);
|
|
1535
|
-
if (params.phase === "result") {
|
|
1536
|
-
this.toolTranscript.emitAfterToolCallObservation(item);
|
|
1537
|
-
await this.onNativeToolResultRecorded?.();
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
};
|
|
1541
|
-
//#endregion
|
|
1542
|
-
//#region extensions/codex/src/app-server/native-subagent-notification.ts
|
|
1543
|
-
/**
|
|
1544
|
-
* Extracts native Codex subagent completion notifications from trusted
|
|
1545
|
-
* inter-agent commentary messages emitted by the app-server.
|
|
1546
|
-
*/
|
|
1547
|
-
const CODEX_SUBAGENT_NOTIFICATION_START = "<subagent_notification>";
|
|
1548
|
-
const CODEX_SUBAGENT_NOTIFICATION_END = "</subagent_notification>";
|
|
1549
|
-
/** Extracts trusted subagent completion payloads from a Codex server notification. */
|
|
1550
|
-
function extractCodexNativeSubagentCompletions(notification) {
|
|
1551
|
-
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
1552
|
-
if (!params) return [];
|
|
1553
|
-
const item = isJsonObject(params.item) ? params.item : void 0;
|
|
1554
|
-
if (!item) return [];
|
|
1555
|
-
const communication = readTrustedInterAgentCommunication(item);
|
|
1556
|
-
const text = communication?.content;
|
|
1557
|
-
if (typeof text !== "string" || !text) return [];
|
|
1558
|
-
return extractCodexNativeSubagentCompletionsFromText(text).filter((completion) => completion.agentPath === communication?.author);
|
|
1559
|
-
}
|
|
1560
|
-
/** Parses one or more tagged subagent completion payloads from commentary text. */
|
|
1561
|
-
function extractCodexNativeSubagentCompletionsFromText(text) {
|
|
1562
|
-
const completions = [];
|
|
1563
|
-
let cursor = 0;
|
|
1564
|
-
while (cursor < text.length) {
|
|
1565
|
-
const start = text.indexOf(CODEX_SUBAGENT_NOTIFICATION_START, cursor);
|
|
1566
|
-
if (start < 0) break;
|
|
1567
|
-
const bodyStart = start + 23;
|
|
1568
|
-
const end = text.indexOf(CODEX_SUBAGENT_NOTIFICATION_END, bodyStart);
|
|
1569
|
-
if (end < 0) break;
|
|
1570
|
-
const parsed = parseCodexNativeSubagentNotificationBody(text.slice(bodyStart, end));
|
|
1571
|
-
if (parsed) completions.push(parsed);
|
|
1572
|
-
cursor = end + 24;
|
|
1573
|
-
}
|
|
1574
|
-
return completions;
|
|
1575
|
-
}
|
|
1576
|
-
const codexNativeSubagentNotifications = {
|
|
1577
|
-
fromNotification: extractCodexNativeSubagentCompletions,
|
|
1578
|
-
fromText: extractCodexNativeSubagentCompletionsFromText,
|
|
1579
|
-
deliveredAgentPaths: readDeliveredNativeCompletionPaths
|
|
1580
|
-
};
|
|
1581
|
-
/** Reads native delivery receipts, leaving status and result ownership with the child lifecycle. */
|
|
1582
|
-
function readDeliveredNativeCompletionPaths(notification) {
|
|
1583
|
-
if (notification.method !== "rawResponseItem/completed") return [];
|
|
1584
|
-
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
1585
|
-
const item = isJsonObject(params?.item) ? params.item : void 0;
|
|
1586
|
-
if (!item || readStringField(item, "type") !== "agent_message") return extractCodexNativeSubagentCompletions(notification).map((completion) => completion.agentPath);
|
|
1587
|
-
const author = readStringField(item, "author");
|
|
1588
|
-
const recipient = readStringField(item, "recipient");
|
|
1589
|
-
const content = item.content;
|
|
1590
|
-
if (!author || !recipient || !Array.isArray(content) || content.length !== 1) return [];
|
|
1591
|
-
const part = content[0];
|
|
1592
|
-
if (!isJsonObject(part) || readStringField(part, "type") !== "input_text") return [];
|
|
1593
|
-
return readStringField(part, "text")?.startsWith(`Message Type: FINAL_ANSWER\nTask name: ${recipient}\nSender: ${author}\nPayload:\n`) ? [author] : [];
|
|
1594
|
-
}
|
|
1595
|
-
function parseCodexNativeSubagentNotificationBody(body) {
|
|
1596
|
-
let payload;
|
|
1597
|
-
try {
|
|
1598
|
-
payload = JSON.parse(body.trim());
|
|
1599
|
-
} catch {
|
|
1600
|
-
return;
|
|
1601
|
-
}
|
|
1602
|
-
if (!isJsonObject(payload)) return;
|
|
1603
|
-
const agentPath = readStringField(payload, "agent_path")?.trim();
|
|
1604
|
-
const status = isJsonObject(payload.status) ? payload.status : void 0;
|
|
1605
|
-
if (!agentPath || !status) return;
|
|
1606
|
-
const statusEntry = readCompletionStatus(status);
|
|
1607
|
-
if (!statusEntry) return;
|
|
1608
|
-
return {
|
|
1609
|
-
agentPath,
|
|
1610
|
-
status: statusEntry.status,
|
|
1611
|
-
statusLabel: statusEntry.label,
|
|
1612
|
-
result: statusEntry.result
|
|
1613
|
-
};
|
|
1614
|
-
}
|
|
1615
|
-
function readCompletionStatus(status) {
|
|
1616
|
-
for (const [rawKey, value] of Object.entries(status)) {
|
|
1617
|
-
const mappedStatus = mapCompletionStatus(normalizeStatusKey(rawKey));
|
|
1618
|
-
if (!mappedStatus) continue;
|
|
1619
|
-
const result = stringifyResult(value, mappedStatus);
|
|
1620
|
-
return {
|
|
1621
|
-
status: mappedStatus,
|
|
1622
|
-
label: mappedStatus === "succeeded" && result.kind === "no_final_assistant_message" ? "completed_without_final_message" : rawKey,
|
|
1623
|
-
result: result.text
|
|
1624
|
-
};
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
function mapCompletionStatus(value) {
|
|
1628
|
-
if (value === "completed" || value === "succeeded" || value === "success") return "succeeded";
|
|
1629
|
-
if (value === "cancelled" || value === "canceled" || value === "interrupted" || value === "shutdown") return "cancelled";
|
|
1630
|
-
if (value === "failed" || value === "error" || value === "errored" || value === "systemerror" || value === "notfound") return "failed";
|
|
1631
|
-
}
|
|
1632
|
-
function stringifyResult(value, status) {
|
|
1633
|
-
if (typeof value === "string") {
|
|
1634
|
-
const text = value.trim();
|
|
1635
|
-
if (text) return { text };
|
|
1636
|
-
return status === "succeeded" ? completedWithoutFinalAssistantMessage() : { text: "(no output)" };
|
|
1637
|
-
}
|
|
1638
|
-
if (value === null || value === void 0) return status === "succeeded" ? completedWithoutFinalAssistantMessage() : { text: "(no output)" };
|
|
1639
|
-
try {
|
|
1640
|
-
return { text: JSON.stringify(value) };
|
|
1641
|
-
} catch {
|
|
1642
|
-
return { text: "(unserializable output)" };
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
function completedWithoutFinalAssistantMessage() {
|
|
1646
|
-
return {
|
|
1647
|
-
text: "Codex native subagent completed without a final assistant message.",
|
|
1648
|
-
kind: "no_final_assistant_message"
|
|
1649
|
-
};
|
|
1650
|
-
}
|
|
1651
|
-
function readTrustedInterAgentCommunication(item) {
|
|
1652
|
-
if (readStringField(item, "type") !== "message" || readStringField(item, "role") !== "assistant" || readStringField(item, "phase") !== "commentary") return;
|
|
1653
|
-
const text = extractSingleTextPart(item);
|
|
1654
|
-
if (!text) return;
|
|
1655
|
-
let parsed;
|
|
1656
|
-
try {
|
|
1657
|
-
parsed = JSON.parse(text);
|
|
1658
|
-
} catch {
|
|
1659
|
-
return;
|
|
1660
|
-
}
|
|
1661
|
-
if (!isJsonObject(parsed)) return;
|
|
1662
|
-
if (typeof parsed.author !== "string" || typeof parsed.recipient !== "string" || typeof parsed.content !== "string" || parsed.trigger_turn !== false) return;
|
|
1663
|
-
return parsed;
|
|
1664
|
-
}
|
|
1665
|
-
function extractSingleTextPart(item) {
|
|
1666
|
-
const content = item.content;
|
|
1667
|
-
if (!Array.isArray(content) || content.length !== 1) return;
|
|
1668
|
-
const [entry] = content;
|
|
1669
|
-
if (!isJsonObject(entry)) return;
|
|
1670
|
-
const type = readStringField(entry, "type");
|
|
1671
|
-
if (type !== "output_text" && type !== "text") return;
|
|
1672
|
-
return readStringField(entry, "text")?.trim();
|
|
1673
|
-
}
|
|
1674
|
-
function normalizeStatusKey(value) {
|
|
1675
|
-
return value.replace(/[^a-z0-9]/giu, "").toLowerCase();
|
|
1676
|
-
}
|
|
1677
|
-
//#endregion
|
|
1678
|
-
//#region extensions/codex/src/app-server/native-subagent-task-ids.ts
|
|
1679
|
-
/**
|
|
1680
|
-
* Shared identifiers for representing Codex native subagents as OpenClaw task
|
|
1681
|
-
* runtime rows.
|
|
1682
|
-
*/
|
|
1683
|
-
/** Task runtime namespace for Codex native subagent task rows. */
|
|
1684
|
-
const CODEX_NATIVE_SUBAGENT_RUNTIME = "subagent";
|
|
1685
|
-
/** Task kind used to distinguish native Codex subagents from other subagent runtimes. */
|
|
1686
|
-
const CODEX_NATIVE_SUBAGENT_TASK_KIND = "codex-native";
|
|
1687
|
-
/** Run id prefix for task rows keyed by Codex child thread ids. */
|
|
1688
|
-
const CODEX_NATIVE_SUBAGENT_RUN_ID_PREFIX = "codex-thread:";
|
|
1689
|
-
//#endregion
|
|
1690
|
-
//#region extensions/codex/src/app-server/native-subagent-task-mirror.ts
|
|
1691
|
-
/** Projects Codex thread and collab-agent notifications into task lifecycle updates. */
|
|
1692
|
-
var CodexNativeSubagentTaskMirror = class {
|
|
1693
|
-
constructor(params, runtime) {
|
|
1694
|
-
this.params = params;
|
|
1695
|
-
this.runtime = runtime;
|
|
1696
|
-
this.mirrorStateByThreadId = /* @__PURE__ */ new Map();
|
|
1697
|
-
this.terminalRunIds = /* @__PURE__ */ new Set();
|
|
1698
|
-
this.authoritativeRunIds = /* @__PURE__ */ new Set();
|
|
1699
|
-
this.expectedAuthoritativeRunIds = /* @__PURE__ */ new Set();
|
|
1700
|
-
this.now = params.now ?? Date.now;
|
|
1701
|
-
}
|
|
1702
|
-
markAuthoritativeCompletion(childThreadId) {
|
|
1703
|
-
const runId = codexNativeSubagentRunId(childThreadId);
|
|
1704
|
-
this.authoritativeRunIds.add(runId);
|
|
1705
|
-
this.terminalRunIds.add(runId);
|
|
1706
|
-
}
|
|
1707
|
-
markAuthoritativeCompletionExpected(childThreadId) {
|
|
1708
|
-
this.expectedAuthoritativeRunIds.add(codexNativeSubagentRunId(childThreadId));
|
|
1709
|
-
}
|
|
1710
|
-
handleNotification(notification) {
|
|
1711
|
-
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
1712
|
-
if (!params) return;
|
|
1713
|
-
if (notification.method === "thread/started") {
|
|
1714
|
-
this.handleThreadStarted(params);
|
|
1715
|
-
return;
|
|
1716
|
-
}
|
|
1717
|
-
if (notification.method === "thread/status/changed") {
|
|
1718
|
-
this.handleThreadStatusChanged(params);
|
|
1719
|
-
return;
|
|
1720
|
-
}
|
|
1721
|
-
if (notification.method === "item/started" || notification.method === "item/completed") {
|
|
1722
|
-
const item = isJsonObject(params.item) ? params.item : void 0;
|
|
1723
|
-
if (notification.method === "item/completed" && item && readStringField(item, "type") === "subAgentActivity") {
|
|
1724
|
-
this.handleSubagentActivityItem(params);
|
|
1725
|
-
return;
|
|
1726
|
-
}
|
|
1727
|
-
this.handleCollabAgentItem(params);
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
handleThreadStarted(params) {
|
|
1731
|
-
const notification = readThreadStartedNotification(params);
|
|
1732
|
-
if (!notification) return;
|
|
1733
|
-
const thread = notification.thread;
|
|
1734
|
-
const spawn = readSubagentThreadSpawnSource(thread.source, this.params.parentThreadId);
|
|
1735
|
-
if (!spawn) return;
|
|
1736
|
-
const threadId = thread.id.trim();
|
|
1737
|
-
const label = normalizeOptionalString(spawn.agent_nickname) ?? normalizeOptionalString(thread.agentNickname) ?? normalizeOptionalString(spawn.agent_role) ?? normalizeOptionalString(thread.agentRole) ?? "Codex subagent";
|
|
1738
|
-
const task = normalizeOptionalString(thread.preview) ?? `Codex native subagent${label === "Codex subagent" ? "" : ` ${label}`}`;
|
|
1739
|
-
const createdAt = secondsToMillis(thread.createdAt) ?? this.now();
|
|
1740
|
-
if (!this.createRunningTask({
|
|
1741
|
-
threadId,
|
|
1742
|
-
label,
|
|
1743
|
-
task,
|
|
1744
|
-
startedAt: createdAt,
|
|
1745
|
-
progressSummary: "Codex native subagent started."
|
|
1746
|
-
})) return;
|
|
1747
|
-
this.applyStatus(threadId, thread.status);
|
|
1748
|
-
}
|
|
1749
|
-
handleThreadStatusChanged(params) {
|
|
1750
|
-
const notification = readThreadStatusChangedNotification(params);
|
|
1751
|
-
if (!notification) return;
|
|
1752
|
-
this.applyStatus(notification.threadId, notification.status);
|
|
1753
|
-
}
|
|
1754
|
-
applyStatus(threadId, status) {
|
|
1755
|
-
if (this.mirrorStateByThreadId.get(threadId) === "failed") return;
|
|
1756
|
-
const statusType = status?.type;
|
|
1757
|
-
if (!statusType) return;
|
|
1758
|
-
const runId = codexNativeSubagentRunId(threadId);
|
|
1759
|
-
if (this.authoritativeRunIds.has(runId)) return;
|
|
1760
|
-
if (this.terminalRunIds.has(runId) && statusType !== "systemError") return;
|
|
1761
|
-
const eventAt = this.now();
|
|
1762
|
-
if (statusType === "active") {
|
|
1763
|
-
this.runtime.recordTaskRunProgressByRunId({
|
|
1764
|
-
runId,
|
|
1765
|
-
lastEventAt: eventAt,
|
|
1766
|
-
progressSummary: "Codex native subagent is active."
|
|
1767
|
-
});
|
|
1768
|
-
return;
|
|
1769
|
-
}
|
|
1770
|
-
if (statusType === "idle") {
|
|
1771
|
-
this.runtime.recordTaskRunProgressByRunId({
|
|
1772
|
-
runId,
|
|
1773
|
-
lastEventAt: eventAt,
|
|
1774
|
-
progressSummary: "Codex native subagent is idle."
|
|
1775
|
-
});
|
|
1776
|
-
return;
|
|
1777
|
-
}
|
|
1778
|
-
if (statusType === "systemError") {
|
|
1779
|
-
if (this.expectedAuthoritativeRunIds.has(runId)) {
|
|
1780
|
-
this.terminalRunIds.delete(runId);
|
|
1781
|
-
this.runtime.recordTaskRunProgressByRunId({
|
|
1782
|
-
runId,
|
|
1783
|
-
lastEventAt: eventAt,
|
|
1784
|
-
progressSummary: "Codex native subagent hit a system error; awaiting recovery."
|
|
1785
|
-
});
|
|
1786
|
-
return;
|
|
1787
|
-
}
|
|
1788
|
-
this.terminalRunIds.add(runId);
|
|
1789
|
-
this.runtime.finalizeTaskRunByRunId({
|
|
1790
|
-
runId,
|
|
1791
|
-
status: "failed",
|
|
1792
|
-
endedAt: eventAt,
|
|
1793
|
-
lastEventAt: eventAt,
|
|
1794
|
-
error: "Codex app-server reported a system error for the native subagent thread.",
|
|
1795
|
-
progressSummary: "Codex native subagent hit a system error.",
|
|
1796
|
-
terminalSummary: "Codex native subagent failed."
|
|
1797
|
-
});
|
|
1798
|
-
return;
|
|
1799
|
-
}
|
|
1800
|
-
if (statusType === "notLoaded") this.runtime.recordTaskRunProgressByRunId({
|
|
1801
|
-
runId,
|
|
1802
|
-
lastEventAt: eventAt,
|
|
1803
|
-
progressSummary: "Codex native subagent is not loaded."
|
|
1804
|
-
});
|
|
1805
|
-
}
|
|
1806
|
-
handleCollabAgentItem(params) {
|
|
1807
|
-
const item = isJsonObject(params.item) ? params.item : void 0;
|
|
1808
|
-
if (!item || readStringField(item, "type") !== "collabAgentToolCall") return;
|
|
1809
|
-
if ((readStringField(item, "senderThreadId") ?? readStringField(params, "threadId")) !== this.params.parentThreadId) return;
|
|
1810
|
-
const isSpawnAgentTool = normalizeToolName(readStringField(item, "tool")) === "spawnagent";
|
|
1811
|
-
const receiverThreadIds = readStringArray$1(item.receiverThreadIds);
|
|
1812
|
-
const agentsStates = readAgentsStates(item.agentsStates);
|
|
1813
|
-
const spawnChildThreadIds = /* @__PURE__ */ new Set([...receiverThreadIds, ...agentsStates.keys()]);
|
|
1814
|
-
if (isSpawnAgentTool) for (const childThreadId of spawnChildThreadIds) this.createTaskFromCollabSpawnItem(childThreadId, item);
|
|
1815
|
-
const toolCallStatus = normalizeCollabToolCallStatus(readStringField(item, "status"));
|
|
1816
|
-
const terminalToolCallThreadIds = /* @__PURE__ */ new Set();
|
|
1817
|
-
if (isSpawnAgentTool && isBlockedOrFailedCollabToolCallStatus(toolCallStatus)) {
|
|
1818
|
-
for (const threadId of spawnChildThreadIds) terminalToolCallThreadIds.add(threadId);
|
|
1819
|
-
for (const threadId of agentsStates.keys()) terminalToolCallThreadIds.add(threadId);
|
|
1820
|
-
}
|
|
1821
|
-
const terminalAgentStateThreadIds = /* @__PURE__ */ new Set();
|
|
1822
|
-
for (const [threadId, state] of agentsStates) {
|
|
1823
|
-
const normalizedStatus = normalizeAgentStateStatus(state.status);
|
|
1824
|
-
if (terminalToolCallThreadIds.has(threadId) && isNonTerminalAgentStateStatus(normalizedStatus)) continue;
|
|
1825
|
-
this.applyCollabAgentStatus(threadId, normalizedStatus, state.message);
|
|
1826
|
-
if (isTerminalAgentStateStatus(normalizedStatus)) terminalAgentStateThreadIds.add(threadId);
|
|
1827
|
-
}
|
|
1828
|
-
if (isBlockedOrFailedCollabToolCallStatus(toolCallStatus)) for (const threadId of terminalToolCallThreadIds) {
|
|
1829
|
-
if (terminalAgentStateThreadIds.has(threadId)) continue;
|
|
1830
|
-
const state = agentsStates.get(threadId);
|
|
1831
|
-
this.applyCollabAgentStatus(threadId, toolCallStatus, state?.message);
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
handleSubagentActivityItem(params) {
|
|
1835
|
-
const item = isJsonObject(params.item) ? params.item : void 0;
|
|
1836
|
-
if (!item || readStringField(item, "type") !== "subAgentActivity" || readStringField(params, "threadId") !== this.params.parentThreadId) return;
|
|
1837
|
-
const threadId = normalizeOptionalString(readStringField(item, "agentThreadId"));
|
|
1838
|
-
const kind = normalizeSubagentActivityKind(readStringField(item, "kind"));
|
|
1839
|
-
if (!threadId || !kind) return;
|
|
1840
|
-
if (kind === "started") {
|
|
1841
|
-
this.createTaskFromSubagentActivity(threadId, normalizeOptionalString(readStringField(item, "agentPath")));
|
|
1842
|
-
return;
|
|
1843
|
-
}
|
|
1844
|
-
if (this.mirrorStateByThreadId.get(threadId) !== "mirrored") return;
|
|
1845
|
-
const message = kind === "interacted" ? "Codex native subagent received more input." : "Codex native subagent was interrupted.";
|
|
1846
|
-
this.applyCollabAgentStatus(threadId, kind === "interacted" ? "running" : "interrupted", message);
|
|
1847
|
-
}
|
|
1848
|
-
createTaskFromSubagentActivity(threadId, agentPath) {
|
|
1849
|
-
const eventAt = this.now();
|
|
1850
|
-
this.createRunningTask({
|
|
1851
|
-
threadId,
|
|
1852
|
-
label: "Codex subagent",
|
|
1853
|
-
task: agentPath ? `Codex native subagent ${agentPath}` : "Codex native subagent",
|
|
1854
|
-
startedAt: eventAt,
|
|
1855
|
-
progressSummary: "Codex native subagent started."
|
|
1856
|
-
});
|
|
1857
|
-
}
|
|
1858
|
-
createTaskFromCollabSpawnItem(threadId, item) {
|
|
1859
|
-
const prompt = normalizeOptionalString(readStringField(item, "prompt"));
|
|
1860
|
-
const createdAt = this.now();
|
|
1861
|
-
this.createRunningTask({
|
|
1862
|
-
threadId,
|
|
1863
|
-
label: "Codex subagent",
|
|
1864
|
-
task: prompt ?? "Codex native subagent",
|
|
1865
|
-
startedAt: createdAt,
|
|
1866
|
-
progressSummary: "Codex native subagent spawned."
|
|
1867
|
-
});
|
|
1868
|
-
}
|
|
1869
|
-
createRunningTask(params) {
|
|
1870
|
-
const threadId = params.threadId.trim();
|
|
1871
|
-
if (!threadId || this.mirrorStateByThreadId.get(threadId) === "mirrored") return false;
|
|
1872
|
-
this.mirrorStateByThreadId.set(threadId, "mirrored");
|
|
1873
|
-
const runId = codexNativeSubagentRunId(threadId);
|
|
1874
|
-
if (!this.runtime.tryCreateRunningTaskRun({
|
|
1875
|
-
sourceId: runId,
|
|
1876
|
-
agentId: this.params.agentId,
|
|
1877
|
-
runId,
|
|
1878
|
-
label: params.label,
|
|
1879
|
-
task: params.task,
|
|
1880
|
-
notifyPolicy: "silent",
|
|
1881
|
-
deliveryStatus: "not_applicable",
|
|
1882
|
-
preferMetadata: true,
|
|
1883
|
-
startedAt: params.startedAt,
|
|
1884
|
-
lastEventAt: this.now(),
|
|
1885
|
-
progressSummary: params.progressSummary
|
|
1886
|
-
})) {
|
|
1887
|
-
this.mirrorStateByThreadId.set(threadId, "failed");
|
|
1888
|
-
return false;
|
|
1889
|
-
}
|
|
1890
|
-
this.terminalRunIds.delete(runId);
|
|
1891
|
-
this.authoritativeRunIds.delete(runId);
|
|
1892
|
-
return true;
|
|
1893
|
-
}
|
|
1894
|
-
applyCollabAgentStatus(threadId, status, message) {
|
|
1895
|
-
if (this.mirrorStateByThreadId.get(threadId) === "failed") return;
|
|
1896
|
-
const normalizedStatus = normalizeAgentStateStatus(status);
|
|
1897
|
-
if (!normalizedStatus) return;
|
|
1898
|
-
const runId = codexNativeSubagentRunId(threadId);
|
|
1899
|
-
if (this.authoritativeRunIds.has(runId)) return;
|
|
1900
|
-
if (this.terminalRunIds.has(runId) && isNonTerminalAgentStateStatus(normalizedStatus)) return;
|
|
1901
|
-
const eventAt = this.now();
|
|
1902
|
-
if (isNonTerminalAgentStateStatus(normalizedStatus)) {
|
|
1903
|
-
this.runtime.recordTaskRunProgressByRunId({
|
|
1904
|
-
runId,
|
|
1905
|
-
lastEventAt: eventAt,
|
|
1906
|
-
progressSummary: normalizeOptionalString(message) ?? (normalizedStatus === "pendingInit" ? "Codex native subagent is initializing." : normalizedStatus === "interrupted" ? "Codex native subagent was interrupted." : "Codex native subagent is running.")
|
|
1907
|
-
});
|
|
1908
|
-
return;
|
|
1909
|
-
}
|
|
1910
|
-
if (normalizedStatus === "completed") {
|
|
1911
|
-
this.terminalRunIds.add(runId);
|
|
1912
|
-
const summary = normalizeOptionalString(message) ?? "Codex native subagent completed.";
|
|
1913
|
-
if (this.expectedAuthoritativeRunIds.has(runId)) this.runtime.recordTaskRunProgressByRunId({
|
|
1914
|
-
runId,
|
|
1915
|
-
lastEventAt: eventAt,
|
|
1916
|
-
progressSummary: summary
|
|
1917
|
-
});
|
|
1918
|
-
else this.runtime.finalizeTaskRunByRunId({
|
|
1919
|
-
runId,
|
|
1920
|
-
status: "succeeded",
|
|
1921
|
-
endedAt: eventAt,
|
|
1922
|
-
lastEventAt: eventAt,
|
|
1923
|
-
progressSummary: summary,
|
|
1924
|
-
terminalSummary: summary
|
|
1925
|
-
});
|
|
1926
|
-
return;
|
|
1927
|
-
}
|
|
1928
|
-
if (normalizedStatus === "blocked") {
|
|
1929
|
-
this.terminalRunIds.add(runId);
|
|
1930
|
-
this.runtime.finalizeTaskRunByRunId({
|
|
1931
|
-
runId,
|
|
1932
|
-
status: "succeeded",
|
|
1933
|
-
endedAt: eventAt,
|
|
1934
|
-
lastEventAt: eventAt,
|
|
1935
|
-
progressSummary: normalizeOptionalString(message) ?? "Codex native subagent blocked.",
|
|
1936
|
-
terminalSummary: normalizeOptionalString(message) ?? "Codex native subagent blocked.",
|
|
1937
|
-
terminalOutcome: "blocked"
|
|
1938
|
-
});
|
|
1939
|
-
return;
|
|
1940
|
-
}
|
|
1941
|
-
this.terminalRunIds.add(runId);
|
|
1942
|
-
this.runtime.finalizeTaskRunByRunId({
|
|
1943
|
-
runId,
|
|
1944
|
-
status: normalizedStatus === "shutdown" ? "cancelled" : "failed",
|
|
1945
|
-
endedAt: eventAt,
|
|
1946
|
-
lastEventAt: eventAt,
|
|
1947
|
-
error: normalizeOptionalString(message) ?? `Codex native subagent status: ${normalizedStatus}`,
|
|
1948
|
-
progressSummary: normalizeOptionalString(message) ?? `Codex native subagent ${normalizedStatus}.`,
|
|
1949
|
-
terminalSummary: normalizeOptionalString(message) ?? "Codex native subagent did not complete."
|
|
1950
|
-
});
|
|
1951
|
-
}
|
|
1952
|
-
};
|
|
1953
|
-
/** Converts a Codex child thread id into the OpenClaw task-runtime run id. */
|
|
1954
|
-
function codexNativeSubagentRunId(threadId) {
|
|
1955
|
-
return `${CODEX_NATIVE_SUBAGENT_RUN_ID_PREFIX}${threadId.trim()}`;
|
|
1956
|
-
}
|
|
1957
|
-
/** Reads a subagent thread-spawn source only when it belongs to the expected parent thread. */
|
|
1958
|
-
function readSubagentThreadSpawnSource(source, parentThreadId) {
|
|
1959
|
-
if (!source || typeof source !== "object" || !("subAgent" in source)) return;
|
|
1960
|
-
const subAgent = source.subAgent;
|
|
1961
|
-
if (!subAgent || typeof subAgent !== "object" || !("thread_spawn" in subAgent)) return;
|
|
1962
|
-
const spawn = subAgent.thread_spawn;
|
|
1963
|
-
if (!spawn || typeof spawn !== "object") return;
|
|
1964
|
-
return spawn.parent_thread_id === parentThreadId ? spawn : void 0;
|
|
1965
|
-
}
|
|
1966
|
-
function readThreadStartedNotification(params) {
|
|
1967
|
-
const thread = params.thread;
|
|
1968
|
-
if (!isJsonObject(thread) || typeof thread.id !== "string") return;
|
|
1969
|
-
return { thread };
|
|
1970
|
-
}
|
|
1971
|
-
function readThreadStatusChangedNotification(params) {
|
|
1972
|
-
if (typeof params.threadId !== "string") return;
|
|
1973
|
-
const status = params.status;
|
|
1974
|
-
if (!isJsonObject(status) || !isCodexThreadStatusType(status.type)) return;
|
|
1975
|
-
return {
|
|
1976
|
-
threadId: params.threadId,
|
|
1977
|
-
status
|
|
1978
|
-
};
|
|
1979
|
-
}
|
|
1980
|
-
function isCodexThreadStatusType(value) {
|
|
1981
|
-
return value === "notLoaded" || value === "idle" || value === "systemError" || value === "active";
|
|
1982
|
-
}
|
|
1983
|
-
function readAgentsStates(value) {
|
|
1984
|
-
const states = /* @__PURE__ */ new Map();
|
|
1985
|
-
if (!isJsonObject(value)) return states;
|
|
1986
|
-
for (const [threadId, rawState] of Object.entries(value)) {
|
|
1987
|
-
if (!isJsonObject(rawState)) continue;
|
|
1988
|
-
const status = readStringField(rawState, "status");
|
|
1989
|
-
const message = readNullableString(rawState, "message");
|
|
1990
|
-
states.set(threadId, {
|
|
1991
|
-
status,
|
|
1992
|
-
message
|
|
1993
|
-
});
|
|
1994
|
-
}
|
|
1995
|
-
return states;
|
|
1996
|
-
}
|
|
1997
|
-
function readStringArray$1(value) {
|
|
1998
|
-
if (!Array.isArray(value)) return [];
|
|
1999
|
-
return value.filter((entry) => typeof entry === "string" && entry.trim() !== "");
|
|
2000
|
-
}
|
|
2001
|
-
function readNullableString(value, key) {
|
|
2002
|
-
const entry = value[key];
|
|
2003
|
-
return typeof entry === "string" || entry === null ? entry : void 0;
|
|
2004
|
-
}
|
|
2005
|
-
function normalizeToolName(value) {
|
|
2006
|
-
return value?.replace(/[^a-z0-9]/giu, "").toLowerCase();
|
|
2007
|
-
}
|
|
2008
|
-
function normalizeSubagentActivityKind(value) {
|
|
2009
|
-
const key = value?.replace(/[^a-z]/giu, "").toLowerCase();
|
|
2010
|
-
return key === "started" || key === "interacted" || key === "interrupted" ? key : void 0;
|
|
2011
|
-
}
|
|
2012
|
-
function normalizeCollabToolCallStatus(value) {
|
|
2013
|
-
const key = value?.replace(/[^a-z0-9]/giu, "").toLowerCase();
|
|
2014
|
-
if (key === "completed" || key === "succeeded" || key === "success") return "completed";
|
|
2015
|
-
if (key === "failed" || key === "error" || key === "errored") return "failed";
|
|
2016
|
-
if (key === "blocked" || key === "declined") return "blocked";
|
|
2017
|
-
if (key === "inprogress" || key === "running") return "running";
|
|
2018
|
-
return value?.trim();
|
|
2019
|
-
}
|
|
2020
|
-
function isBlockedOrFailedCollabToolCallStatus(value) {
|
|
2021
|
-
return value === "failed" || value === "blocked";
|
|
2022
|
-
}
|
|
2023
|
-
function isNonTerminalAgentStateStatus(value) {
|
|
2024
|
-
return value === "pendingInit" || value === "running" || value === "interrupted";
|
|
2025
|
-
}
|
|
2026
|
-
function isTerminalAgentStateStatus(value) {
|
|
2027
|
-
return value !== void 0 && !isNonTerminalAgentStateStatus(value);
|
|
2028
|
-
}
|
|
2029
|
-
function normalizeAgentStateStatus(value) {
|
|
2030
|
-
const key = value?.replace(/[^a-z0-9]/giu, "").toLowerCase();
|
|
2031
|
-
if (!key) return;
|
|
2032
|
-
if (key === "pendinginit") return "pendingInit";
|
|
2033
|
-
if (key === "inprogress" || key === "running") return "running";
|
|
2034
|
-
if (key === "completed" || key === "succeeded" || key === "success") return "completed";
|
|
2035
|
-
if (key === "interrupted" || key === "cancelled" || key === "canceled" || key === "shutdown") return key === "shutdown" ? "shutdown" : "interrupted";
|
|
2036
|
-
if (key === "failed" || key === "error" || key === "systemerror") return "failed";
|
|
2037
|
-
if (key === "blocked" || key === "declined") return "blocked";
|
|
2038
|
-
return value?.trim();
|
|
2039
|
-
}
|
|
2040
|
-
function secondsToMillis(value) {
|
|
2041
|
-
if (typeof value !== "number" || !Number.isFinite(value)) return;
|
|
2042
|
-
return value * 1e3;
|
|
2043
|
-
}
|
|
2044
|
-
//#endregion
|
|
2045
|
-
//#region extensions/codex/src/app-server/native-subagent-monitor.ts
|
|
2046
|
-
/**
|
|
2047
|
-
* Mirrors Codex native subagent lifecycle and completion into OpenClaw task
|
|
2048
|
-
* runtime records, with app-server history as the recovery source.
|
|
2049
|
-
*/
|
|
2050
|
-
const DEFAULT_RECOVERY_POLL_DELAYS_MS = [
|
|
2051
|
-
2e3,
|
|
2052
|
-
5e3,
|
|
2053
|
-
1e4,
|
|
2054
|
-
15e3,
|
|
2055
|
-
3e4,
|
|
2056
|
-
6e4,
|
|
2057
|
-
12e4,
|
|
2058
|
-
3e5
|
|
2059
|
-
];
|
|
2060
|
-
const DEFAULT_COMPLETION_DELIVERY_RETRY_DELAYS_MS = [
|
|
2061
|
-
5e3,
|
|
2062
|
-
15e3,
|
|
2063
|
-
3e4,
|
|
2064
|
-
6e4,
|
|
2065
|
-
12e4,
|
|
2066
|
-
3e5
|
|
2067
|
-
];
|
|
2068
|
-
const RECENT_TERMINAL_TASK_RECONCILE_GRACE_MS = 6e4;
|
|
2069
|
-
const THREAD_READ_TIMEOUT_MS = 3e4;
|
|
2070
|
-
const NATIVE_SUBAGENT_NOTIFICATION_METHODS = /* @__PURE__ */ new Set([
|
|
2071
|
-
"thread/started",
|
|
2072
|
-
"thread/status/changed",
|
|
2073
|
-
"turn/started",
|
|
2074
|
-
"turn/completed",
|
|
2075
|
-
"item/agentMessage/delta",
|
|
2076
|
-
"item/reasoning/summaryTextDelta",
|
|
2077
|
-
"item/started",
|
|
2078
|
-
"item/completed",
|
|
2079
|
-
"rawResponseItem/completed"
|
|
2080
|
-
]);
|
|
2081
|
-
const RECOVERY_REVISION_NOTIFICATION_METHODS = /* @__PURE__ */ new Set([
|
|
2082
|
-
"thread/started",
|
|
2083
|
-
"thread/status/changed",
|
|
2084
|
-
"turn/started",
|
|
2085
|
-
"turn/completed"
|
|
2086
|
-
]);
|
|
2087
|
-
const MAX_PENDING_DIRECT_SPAWN_EVIDENCE = 32;
|
|
2088
|
-
const defaultRuntime = {
|
|
2089
|
-
createAgentHarnessTaskRuntime,
|
|
2090
|
-
deliverAgentHarnessTaskCompletion
|
|
2091
|
-
};
|
|
2092
|
-
const monitors = /* @__PURE__ */ new WeakMap();
|
|
2093
|
-
const completionDeliveryOwners = /* @__PURE__ */ new Map();
|
|
2094
|
-
function registerMonitor(params) {
|
|
2095
|
-
let monitor = monitors.get(params.client);
|
|
2096
|
-
if (!monitor) {
|
|
2097
|
-
const childThreadOwnership = /* @__PURE__ */ new Map();
|
|
2098
|
-
const childThreadTransitions = new KeyedAsyncQueue();
|
|
2099
|
-
monitor = new Monitor(params.client, params.runtime ?? defaultRuntime, {
|
|
2100
|
-
retainClient: params.retainClient,
|
|
2101
|
-
retainParentThread: params.retainParentThread,
|
|
2102
|
-
claimChildThread: (threadId) => childThreadTransitions.enqueue(threadId, async () => {
|
|
2103
|
-
const ownership = await claimCodexAppServerLiveThread(params.client, threadId);
|
|
2104
|
-
if (ownership) childThreadOwnership.set(threadId, ownership);
|
|
2105
|
-
return ownership;
|
|
2106
|
-
}),
|
|
2107
|
-
retainChildThread: (threadId) => childThreadTransitions.enqueue(threadId, async () => {
|
|
2108
|
-
const ownership = childThreadOwnership.get(threadId);
|
|
2109
|
-
let retained = false;
|
|
2110
|
-
try {
|
|
2111
|
-
retained = await retainCodexAppServerLiveThread(params.client, threadId, ownership?.release);
|
|
2112
|
-
return retained;
|
|
2113
|
-
} finally {
|
|
2114
|
-
if (!retained && ownership) await ownership.release(threadId);
|
|
2115
|
-
if (childThreadOwnership.get(threadId) === ownership) childThreadOwnership.delete(threadId);
|
|
2116
|
-
}
|
|
2117
|
-
}),
|
|
2118
|
-
releaseChildThread: (threadId) => childThreadTransitions.enqueue(threadId, async () => {
|
|
2119
|
-
const ownership = childThreadOwnership.get(threadId);
|
|
2120
|
-
if (ownership) {
|
|
2121
|
-
await ownership.release(threadId);
|
|
2122
|
-
if (childThreadOwnership.get(threadId) === ownership) childThreadOwnership.delete(threadId);
|
|
2123
|
-
} else await releaseCodexAppServerLiveThread(params.client, threadId);
|
|
2124
|
-
})
|
|
2125
|
-
});
|
|
2126
|
-
monitors.set(params.client, monitor);
|
|
2127
|
-
}
|
|
2128
|
-
return monitor.registerParent({
|
|
2129
|
-
parentThreadId: params.parentThreadId,
|
|
2130
|
-
requesterSessionKey: params.requesterSessionKey,
|
|
2131
|
-
taskRuntimeScope: params.taskRuntimeScope,
|
|
2132
|
-
agentId: params.agentId,
|
|
2133
|
-
claimDirectChild: params.claimDirectChild,
|
|
2134
|
-
rejectPendingDirectChild: params.rejectPendingDirectChild,
|
|
2135
|
-
onDirectChildAccepted: params.onDirectChildAccepted
|
|
2136
|
-
});
|
|
2137
|
-
}
|
|
2138
|
-
var Monitor = class {
|
|
2139
|
-
constructor(client, runtime = defaultRuntime, options = {}) {
|
|
2140
|
-
this.client = client;
|
|
2141
|
-
this.runtime = runtime;
|
|
2142
|
-
this.parentStates = /* @__PURE__ */ new Map();
|
|
2143
|
-
this.pendingDirectSpawnEvidence = /* @__PURE__ */ new Map();
|
|
2144
|
-
this.retiredParentStates = /* @__PURE__ */ new WeakSet();
|
|
2145
|
-
this.childStates = /* @__PURE__ */ new Map();
|
|
2146
|
-
this.childThreadIdsByAgentPath = /* @__PURE__ */ new Map();
|
|
2147
|
-
this.taskReconciliations = /* @__PURE__ */ new Map();
|
|
2148
|
-
this.taskReconciliationTimers = /* @__PURE__ */ new Map();
|
|
2149
|
-
this.threadStatusRevisions = /* @__PURE__ */ new Map();
|
|
2150
|
-
this.parentThreadRetentions = /* @__PURE__ */ new Map();
|
|
2151
|
-
this.disposed = false;
|
|
2152
|
-
this.recoveryPollDelaysMs = options.recoveryPollDelaysMs ?? DEFAULT_RECOVERY_POLL_DELAYS_MS;
|
|
2153
|
-
this.completionDeliveryRetryDelaysMs = options.completionDeliveryRetryDelaysMs ?? DEFAULT_COMPLETION_DELIVERY_RETRY_DELAYS_MS;
|
|
2154
|
-
this.completionDeliveryMaxRetries = options.completionDeliveryMaxRetries ?? this.completionDeliveryRetryDelaysMs.length;
|
|
2155
|
-
this.now = options.now ?? Date.now;
|
|
2156
|
-
this.retainClient = options.retainClient;
|
|
2157
|
-
this.retainParentThread = options.retainParentThread;
|
|
2158
|
-
this.claimChildThread = options.claimChildThread;
|
|
2159
|
-
this.retainChildThread = options.retainChildThread;
|
|
2160
|
-
this.releaseChildThread = options.releaseChildThread;
|
|
2161
|
-
this.removeNotificationHandler = client.addNotificationHandler(async (notification) => {
|
|
2162
|
-
if (!NATIVE_SUBAGENT_NOTIFICATION_METHODS.has(notification.method)) return;
|
|
2163
|
-
await this.handleNotification(notification);
|
|
2164
|
-
});
|
|
2165
|
-
this.removeCloseHandler = client.addCloseHandler(() => this.dispose());
|
|
2166
|
-
}
|
|
2167
|
-
dispose() {
|
|
2168
|
-
if (this.disposed) return;
|
|
2169
|
-
this.disposed = true;
|
|
2170
|
-
this.removeNotificationHandler();
|
|
2171
|
-
this.removeCloseHandler();
|
|
2172
|
-
for (const timer of this.taskReconciliationTimers.values()) clearTimeout(timer);
|
|
2173
|
-
this.taskReconciliationTimers.clear();
|
|
2174
|
-
for (const childState of this.childStates.values()) {
|
|
2175
|
-
this.releaseDirectChild(childState);
|
|
2176
|
-
if (childState.terminal && childState.pendingCompletion) {
|
|
2177
|
-
this.clearRecoveryTimers(childState);
|
|
2178
|
-
continue;
|
|
2179
|
-
}
|
|
2180
|
-
this.unregisterChild(childState);
|
|
2181
|
-
}
|
|
2182
|
-
this.releaseRetainedClient();
|
|
2183
|
-
for (const release of this.parentThreadRetentions.values()) release();
|
|
2184
|
-
this.parentThreadRetentions.clear();
|
|
2185
|
-
for (const state of this.parentStates.values()) {
|
|
2186
|
-
state.owners.clear();
|
|
2187
|
-
state.turnIds.clear();
|
|
2188
|
-
this.deliverDetachedCompletions(state);
|
|
2189
|
-
}
|
|
2190
|
-
this.pendingDirectSpawnEvidence.clear();
|
|
2191
|
-
for (const [parentThreadId] of this.parentStates) if (![...this.childStates.values()].some((childState) => childState.parentThreadId === parentThreadId)) this.parentStates.delete(parentThreadId);
|
|
2192
|
-
}
|
|
2193
|
-
registerParent(params) {
|
|
2194
|
-
const parentThreadId = params.parentThreadId.trim();
|
|
2195
|
-
if (!parentThreadId) throw new Error("Codex native subagent monitor requires a parent thread id");
|
|
2196
|
-
if (this.disposed) throw new Error("Codex native subagent monitor is closed");
|
|
2197
|
-
let state = this.parentStates.get(parentThreadId);
|
|
2198
|
-
if (state?.requesterSessionKey && params.requesterSessionKey && state.requesterSessionKey !== params.requesterSessionKey) throw new Error(`Codex thread ${parentThreadId} is already bound to another session`);
|
|
2199
|
-
if (!state) {
|
|
2200
|
-
state = {
|
|
2201
|
-
parentThreadId,
|
|
2202
|
-
owners: /* @__PURE__ */ new Map(),
|
|
2203
|
-
turnIds: /* @__PURE__ */ new Set(),
|
|
2204
|
-
nativeCompletionReceipts: /* @__PURE__ */ new Set()
|
|
2205
|
-
};
|
|
2206
|
-
this.parentStates.set(parentThreadId, state);
|
|
2207
|
-
}
|
|
2208
|
-
state.requesterSessionKey ??= params.requesterSessionKey;
|
|
2209
|
-
state.taskRuntimeScope ??= params.taskRuntimeScope;
|
|
2210
|
-
state.agentId ??= params.agentId;
|
|
2211
|
-
const owner = Symbol("codex-native-subagent-owner");
|
|
2212
|
-
state.owners.set(owner, {
|
|
2213
|
-
claimDirectChild: params.claimDirectChild,
|
|
2214
|
-
rejectPendingDirectChild: params.rejectPendingDirectChild,
|
|
2215
|
-
onDirectChildAccepted: params.onDirectChildAccepted
|
|
2216
|
-
});
|
|
2217
|
-
this.prepareParentTaskRuntime(state);
|
|
2218
|
-
for (const childState of this.childStates.values()) if (childState.parentThreadId === parentThreadId && childState.pendingCompletion) this.deliverPendingCompletion(state, childState);
|
|
2219
|
-
let registered = true;
|
|
2220
|
-
const registeredState = state;
|
|
2221
|
-
this.reconcileTaskRowsForParent(registeredState).catch((error) => {
|
|
2222
|
-
embeddedAgentLog.warn("Failed to reconcile Codex native subagent task rows", {
|
|
2223
|
-
parentThreadId,
|
|
2224
|
-
error: formatErrorMessage(error)
|
|
2225
|
-
});
|
|
2226
|
-
});
|
|
2227
|
-
return {
|
|
2228
|
-
bindTurn: (turnIdInput) => {
|
|
2229
|
-
const turnId = turnIdInput.trim();
|
|
2230
|
-
if (!turnId || this.parentStates.get(parentThreadId) !== registeredState) return;
|
|
2231
|
-
const current = registeredState.owners.get(owner);
|
|
2232
|
-
if (!current || [...registeredState.owners.values()].some((other) => other !== current && other.turnId === turnId)) return;
|
|
2233
|
-
current.turnId = turnId;
|
|
2234
|
-
registeredState.turnIds.add(turnId);
|
|
2235
|
-
this.drainPendingDirectSpawnEvidence(registeredState, current, turnId);
|
|
2236
|
-
this.clearUnconsumablePendingDirectSpawnEvidence();
|
|
2237
|
-
},
|
|
2238
|
-
unregister: () => {
|
|
2239
|
-
if (!registered) return;
|
|
2240
|
-
registered = false;
|
|
2241
|
-
const current = this.parentStates.get(parentThreadId);
|
|
2242
|
-
if (current === registeredState) {
|
|
2243
|
-
const turnId = current.owners.get(owner)?.turnId;
|
|
2244
|
-
current.owners.delete(owner);
|
|
2245
|
-
if (turnId) current.turnIds.delete(turnId);
|
|
2246
|
-
if (current.owners.size === 0) {
|
|
2247
|
-
current.turnIds.clear();
|
|
2248
|
-
current.nativeCompletionReceipts = /* @__PURE__ */ new Set();
|
|
2249
|
-
}
|
|
2250
|
-
this.clearUnconsumablePendingDirectSpawnEvidence();
|
|
2251
|
-
this.deliverDetachedCompletions(current);
|
|
2252
|
-
this.pruneParentIfUnused(current);
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
};
|
|
2256
|
-
}
|
|
2257
|
-
retireParent(parentThreadIdInput) {
|
|
2258
|
-
const parentThreadId = parentThreadIdInput.trim();
|
|
2259
|
-
const state = this.parentStates.get(parentThreadId);
|
|
2260
|
-
if (!state) return;
|
|
2261
|
-
this.retiredParentStates.add(state);
|
|
2262
|
-
state.owners.clear();
|
|
2263
|
-
this.clearPendingDirectSpawnEvidenceForParent(parentThreadId);
|
|
2264
|
-
for (const childState of Array.from(this.childStates.values())) if (childState.parentThreadId === parentThreadId) this.retireChild(state, childState, "Codex native subagent parent session ended.");
|
|
2265
|
-
if (this.parentStates.get(parentThreadId) === state) this.parentStates.delete(parentThreadId);
|
|
2266
|
-
}
|
|
2267
|
-
prepareParentTaskRuntime(state) {
|
|
2268
|
-
if (!state.requesterSessionKey || !state.taskRuntimeScope) return;
|
|
2269
|
-
state.taskRuntime ??= this.runtime.createAgentHarnessTaskRuntime({
|
|
2270
|
-
runtime: CODEX_NATIVE_SUBAGENT_RUNTIME,
|
|
2271
|
-
taskKind: CODEX_NATIVE_SUBAGENT_TASK_KIND,
|
|
2272
|
-
scope: state.taskRuntimeScope,
|
|
2273
|
-
runIdPrefix: CODEX_NATIVE_SUBAGENT_RUN_ID_PREFIX
|
|
2274
|
-
});
|
|
2275
|
-
state.mirror ??= new CodexNativeSubagentTaskMirror({
|
|
2276
|
-
parentThreadId: state.parentThreadId,
|
|
2277
|
-
requesterSessionKey: state.requesterSessionKey,
|
|
2278
|
-
agentId: state.agentId
|
|
2279
|
-
}, state.taskRuntime);
|
|
2280
|
-
}
|
|
2281
|
-
/** Handles one notification from the client-wide router observer. */
|
|
2282
|
-
async handleNotification(notification) {
|
|
2283
|
-
if (this.disposed) return;
|
|
2284
|
-
const state = this.resolveMirrorState(notification);
|
|
2285
|
-
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
2286
|
-
const startedThread = isJsonObject(params?.thread) ? params.thread : void 0;
|
|
2287
|
-
const threadId = readStringField(params, "threadId")?.trim() ?? readStringField(startedThread, "id")?.trim();
|
|
2288
|
-
const threadStatus = isJsonObject(params?.status) ? normalizeIdentifier(readStringField(params.status, "type")) : void 0;
|
|
2289
|
-
const parent = threadId ? this.parentStates.get(threadId) : void 0;
|
|
2290
|
-
if (parent && parent.owners.size > 0 && notification.method === "turn/started") {
|
|
2291
|
-
const turnId = isJsonObject(params?.turn) ? readStringField(params.turn, "id") : void 0;
|
|
2292
|
-
if (turnId) parent.turnIds.add(turnId);
|
|
2293
|
-
}
|
|
2294
|
-
const tracksRecoveryRevision = Boolean(threadId && this.threadStatusRevisions.has(threadId));
|
|
2295
|
-
if (RECOVERY_REVISION_NOTIFICATION_METHODS.has(notification.method) && threadId && tracksRecoveryRevision) this.threadStatusRevisions.get(threadId).value += 1;
|
|
2296
|
-
if (!state && (!threadId || !this.parentStates.has(threadId) && !this.childStates.has(threadId) && !tracksRecoveryRevision)) return;
|
|
2297
|
-
if (state?.mirror) try {
|
|
2298
|
-
state.mirror.handleNotification(notification);
|
|
2299
|
-
} catch (error) {
|
|
2300
|
-
embeddedAgentLog.warn("Failed to mirror Codex native subagent lifecycle event", {
|
|
2301
|
-
method: notification.method,
|
|
2302
|
-
error: formatErrorMessage(error)
|
|
2303
|
-
});
|
|
2304
|
-
}
|
|
2305
|
-
if (state) this.handleClosedChild(notification, state);
|
|
2306
|
-
const childState = threadId ? this.childStates.get(threadId) : void 0;
|
|
2307
|
-
if (notification.method === "turn/started" && childState) {
|
|
2308
|
-
childState.nativeCompletionDelivered = false;
|
|
2309
|
-
for (const key of childState.agentPathKeys) state?.nativeCompletionReceipts.delete(key);
|
|
2310
|
-
this.resumeChild(childState);
|
|
2311
|
-
}
|
|
2312
|
-
if (parent && parent.turnIds.has(readStringField(params, "turnId") ?? "")) this.recordNativeCompletionDelivery(parent, notification);
|
|
2313
|
-
if (childState && !childState.terminal) this.emitChildTaskActivity(notification, childState);
|
|
2314
|
-
this.captureChildAssistantMessage(notification);
|
|
2315
|
-
await this.handleChildTurnCompletion(notification);
|
|
2316
|
-
if (notification.method === "thread/status/changed" && threadId && threadStatus) {
|
|
2317
|
-
if (threadStatus !== "systemerror") {
|
|
2318
|
-
if (childState) this.clearSystemErrorFallback(childState);
|
|
2319
|
-
} else {
|
|
2320
|
-
if (childState) {
|
|
2321
|
-
this.resumeChild(childState, { scheduleRecovery: false });
|
|
2322
|
-
this.setRecoveryFallback(childState, systemErrorFallbackCompletion(childState.childThreadId), this.now());
|
|
2323
|
-
}
|
|
2324
|
-
this.reconcileChildThread(threadId).catch((error) => {
|
|
2325
|
-
this.logRecoveryFailure(threadId, error);
|
|
2326
|
-
return false;
|
|
2327
|
-
}).then((reconciled) => {
|
|
2328
|
-
if (!reconciled && childState && this.childStates.get(threadId) === childState) this.scheduleRecoveryPoll(childState);
|
|
2329
|
-
});
|
|
2330
|
-
}
|
|
2331
|
-
}
|
|
2332
|
-
await this.handleCompletionNotification(notification);
|
|
2333
|
-
}
|
|
2334
|
-
emitChildTaskActivity(notification, childState) {
|
|
2335
|
-
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
2336
|
-
if (!params) return;
|
|
2337
|
-
const owner = {
|
|
2338
|
-
runId: codexNativeSubagentRunId(childState.childThreadId),
|
|
2339
|
-
...childState.agentId ? { agentId: childState.agentId } : {}
|
|
2340
|
-
};
|
|
2341
|
-
if (notification.method === "item/agentMessage/delta") {
|
|
2342
|
-
const delta = readStringField(params, "delta");
|
|
2343
|
-
if (delta) emitAgentEvent({
|
|
2344
|
-
...owner,
|
|
2345
|
-
stream: "assistant",
|
|
2346
|
-
data: { delta }
|
|
2347
|
-
});
|
|
2348
|
-
return;
|
|
2349
|
-
}
|
|
2350
|
-
if (notification.method === "item/reasoning/summaryTextDelta") {
|
|
2351
|
-
const delta = readStringField(params, "delta");
|
|
2352
|
-
if (delta) emitAgentEvent({
|
|
2353
|
-
...owner,
|
|
2354
|
-
stream: "thinking",
|
|
2355
|
-
data: { delta }
|
|
2356
|
-
});
|
|
2357
|
-
return;
|
|
2358
|
-
}
|
|
2359
|
-
if (notification.method !== "item/started" && notification.method !== "item/completed") return;
|
|
2360
|
-
const item = readItem(params.item);
|
|
2361
|
-
if (item?.type === "agentMessage" && notification.method === "item/completed" && item.text) emitAgentEvent({
|
|
2362
|
-
...owner,
|
|
2363
|
-
stream: "assistant",
|
|
2364
|
-
data: { text: item.text }
|
|
2365
|
-
});
|
|
2366
|
-
const projection = projectNormalizedToolItem({
|
|
2367
|
-
phase: notification.method === "item/started" ? "start" : "result",
|
|
2368
|
-
item
|
|
2369
|
-
});
|
|
2370
|
-
if (projection?.event) emitAgentEvent({
|
|
2371
|
-
...owner,
|
|
2372
|
-
...projection.event
|
|
2373
|
-
});
|
|
2374
|
-
}
|
|
2375
|
-
resumeChild(childState, options = {}) {
|
|
2376
|
-
if (childState.terminal) return;
|
|
2377
|
-
this.observeActiveChild(childState);
|
|
2378
|
-
this.clearRecoveryTimers(childState);
|
|
2379
|
-
childState.recoveryAttempt = 0;
|
|
2380
|
-
if (options.scheduleRecovery !== false) this.scheduleRecoveryPoll(childState);
|
|
2381
|
-
}
|
|
2382
|
-
observeActiveChild(childState) {
|
|
2383
|
-
childState.settledWithoutCompletion = false;
|
|
2384
|
-
childState.fallbackCompletion = void 0;
|
|
2385
|
-
this.releaseClientRetention ??= this.retainClient?.();
|
|
2386
|
-
}
|
|
2387
|
-
settleResumableChild(childState) {
|
|
2388
|
-
if (childState.terminal) return;
|
|
2389
|
-
childState.settledWithoutCompletion = true;
|
|
2390
|
-
childState.fallbackCompletion = void 0;
|
|
2391
|
-
this.releaseDirectChild(childState);
|
|
2392
|
-
this.clearRecoveryTimers(childState);
|
|
2393
|
-
this.releaseClientRetentionIfIdle();
|
|
2394
|
-
}
|
|
2395
|
-
captureChildAssistantMessage(notification) {
|
|
2396
|
-
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
2397
|
-
const childThreadId = readStringField(params, "threadId")?.trim();
|
|
2398
|
-
const childState = childThreadId ? this.childStates.get(childThreadId) : void 0;
|
|
2399
|
-
if (!childState || childState.terminal) return;
|
|
2400
|
-
if (notification.method === "item/agentMessage/delta") {
|
|
2401
|
-
const turnId = readStringField(params, "turnId");
|
|
2402
|
-
const itemId = readStringField(params, "itemId");
|
|
2403
|
-
const delta = readStringField(params, "delta");
|
|
2404
|
-
if (turnId && itemId && delta) this.recordChildAssistantMessage(childState, turnId, itemId, delta);
|
|
2405
|
-
return;
|
|
2406
|
-
}
|
|
2407
|
-
if (notification.method !== "item/started" && notification.method !== "item/completed") return;
|
|
2408
|
-
this.captureChildAssistantMessageItem(childState, readStringField(params, "turnId"), isJsonObject(params?.item) ? params.item : void 0);
|
|
2409
|
-
}
|
|
2410
|
-
captureChildAssistantMessageItem(childState, turnId, item) {
|
|
2411
|
-
if (readStringField(item, "type") !== "agentMessage" || !turnId) return;
|
|
2412
|
-
const itemId = readStringField(item, "id");
|
|
2413
|
-
if (!itemId) return;
|
|
2414
|
-
const messages = this.getChildAssistantMessages(childState, turnId);
|
|
2415
|
-
if (readStringField(item, "phase") === "commentary") messages.commentaryIds.add(itemId);
|
|
2416
|
-
else messages.finalMessageIds.add(itemId);
|
|
2417
|
-
const text = readStringField(item, "text");
|
|
2418
|
-
if (text) this.recordChildAssistantMessage(childState, turnId, itemId, text, { replace: true });
|
|
2419
|
-
}
|
|
2420
|
-
captureChildTurnAssistantMessages(childState, turn) {
|
|
2421
|
-
const turnId = readStringField(turn, "id");
|
|
2422
|
-
if (!turnId || !Array.isArray(turn.items)) return;
|
|
2423
|
-
for (const item of turn.items) this.captureChildAssistantMessageItem(childState, turnId, isJsonObject(item) ? item : void 0);
|
|
2424
|
-
}
|
|
2425
|
-
recordChildAssistantMessage(childState, turnId, itemId, text, options = {}) {
|
|
2426
|
-
const messages = this.getChildAssistantMessages(childState, turnId);
|
|
2427
|
-
if (!messages.texts.has(itemId)) messages.order.push(itemId);
|
|
2428
|
-
const existing = messages.texts.get(itemId) ?? "";
|
|
2429
|
-
messages.texts.set(itemId, options.replace ? text : `${existing}${text}`);
|
|
2430
|
-
}
|
|
2431
|
-
getChildAssistantMessages(childState, turnId) {
|
|
2432
|
-
let messages = childState.assistantMessagesByTurn.get(turnId);
|
|
2433
|
-
if (!messages) {
|
|
2434
|
-
messages = {
|
|
2435
|
-
texts: /* @__PURE__ */ new Map(),
|
|
2436
|
-
order: [],
|
|
2437
|
-
commentaryIds: /* @__PURE__ */ new Set(),
|
|
2438
|
-
finalMessageIds: /* @__PURE__ */ new Set()
|
|
2439
|
-
};
|
|
2440
|
-
childState.assistantMessagesByTurn.set(turnId, messages);
|
|
2441
|
-
}
|
|
2442
|
-
return messages;
|
|
2443
|
-
}
|
|
2444
|
-
async handleChildTurnCompletion(notification) {
|
|
2445
|
-
if (notification.method !== "turn/completed") return;
|
|
2446
|
-
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
2447
|
-
const childThreadId = readStringField(params, "threadId")?.trim();
|
|
2448
|
-
const childState = childThreadId ? this.childStates.get(childThreadId) : void 0;
|
|
2449
|
-
const state = childState ? this.parentStates.get(childState.parentThreadId) : void 0;
|
|
2450
|
-
const turn = isJsonObject(params?.turn) ? params.turn : void 0;
|
|
2451
|
-
if (!state || !childState || !turn || childState.terminal) return;
|
|
2452
|
-
const turnId = readStringField(turn, "id");
|
|
2453
|
-
const status = normalizeIdentifier(readStringField(turn, "status"));
|
|
2454
|
-
if (status === "interrupted") {
|
|
2455
|
-
this.removePendingDirectSpawnEvidenceForChild(childState.childThreadId);
|
|
2456
|
-
this.rejectPendingDirectChild(state, childState.childThreadId, "Codex child turn interrupted");
|
|
2457
|
-
if (turnId) childState.assistantMessagesByTurn.delete(turnId);
|
|
2458
|
-
this.settleResumableChild(childState);
|
|
2459
|
-
return;
|
|
2460
|
-
}
|
|
2461
|
-
if (status === "completed" || status === "failed") {
|
|
2462
|
-
const revision = this.threadStatusRevisions.get(childState.childThreadId);
|
|
2463
|
-
if (revision) revision.terminal = true;
|
|
2464
|
-
this.rejectPendingDirectChild(state, childState.childThreadId, "Codex child turn completed");
|
|
2465
|
-
this.releaseDirectChild(childState);
|
|
2466
|
-
this.removePendingDirectSpawnEvidenceForChild(childState.childThreadId);
|
|
2467
|
-
}
|
|
2468
|
-
this.captureChildTurnAssistantMessages(childState, turn);
|
|
2469
|
-
const completion = toChildTurnCompletion(childState, turn);
|
|
2470
|
-
if (!completion) return;
|
|
2471
|
-
await this.processObservedCompletion(state, childState, completion);
|
|
2472
|
-
}
|
|
2473
|
-
/** Reads one child through app-server history and delivers a terminal result when present. */
|
|
2474
|
-
async reconcileChildThread(childThreadIdInput) {
|
|
2475
|
-
const childState = this.childStates.get(childThreadIdInput.trim());
|
|
2476
|
-
if (!childState || childState.terminal || this.disposed) return false;
|
|
2477
|
-
if (childState.recoveryInFlight) return await childState.recoveryInFlight;
|
|
2478
|
-
const recovery = this.reconcileChildState(childState);
|
|
2479
|
-
childState.recoveryInFlight = recovery;
|
|
2480
|
-
try {
|
|
2481
|
-
return await recovery;
|
|
2482
|
-
} finally {
|
|
2483
|
-
if (childState.recoveryInFlight === recovery) childState.recoveryInFlight = void 0;
|
|
2484
|
-
}
|
|
2485
|
-
}
|
|
2486
|
-
resolveMirrorState(notification) {
|
|
2487
|
-
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
2488
|
-
if (!params) return;
|
|
2489
|
-
if (notification.method === "thread/started") {
|
|
2490
|
-
const thread = isJsonObject(params.thread) ? params.thread : void 0;
|
|
2491
|
-
const parentThreadId = readThreadParentThreadId(thread);
|
|
2492
|
-
const childThreadId = thread ? readStringField(thread, "id")?.trim() : void 0;
|
|
2493
|
-
const agentPath = readStringField(readThreadSpawnSource(thread), "agent_path")?.trim();
|
|
2494
|
-
const state = parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
|
|
2495
|
-
if (state && childThreadId && parentThreadId) return this.registerChildThread(state, childThreadId, agentPath === void 0 ? {} : { agentPath }) ? state : void 0;
|
|
2496
|
-
return state;
|
|
2497
|
-
}
|
|
2498
|
-
if (notification.method === "thread/status/changed" || notification.method === "turn/started" || notification.method === "turn/completed" || notification.method === "item/agentMessage/delta") {
|
|
2499
|
-
const childThreadId = readStringField(params, "threadId")?.trim();
|
|
2500
|
-
const parentThreadId = childThreadId ? this.childStates.get(childThreadId)?.parentThreadId : void 0;
|
|
2501
|
-
return parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
|
|
2502
|
-
}
|
|
2503
|
-
if (notification.method === "item/started" || notification.method === "item/completed") {
|
|
2504
|
-
const item = isJsonObject(params.item) ? params.item : void 0;
|
|
2505
|
-
const parentThreadId = item ? (readStringField(item, "senderThreadId") ?? readStringField(params, "threadId"))?.trim() : void 0;
|
|
2506
|
-
const state = parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
|
|
2507
|
-
if (state && parentThreadId) {
|
|
2508
|
-
const turnId = readStringField(params, "turnId");
|
|
2509
|
-
const owner = this.resolveParentOwner(state, turnId);
|
|
2510
|
-
if (notification.method === "item/completed" && readStringField(item, "type") === "subAgentActivity" && normalizeIdentifier(readStringField(item, "kind")) === "started") {
|
|
2511
|
-
const childThreadId = readStringField(item, "agentThreadId")?.trim();
|
|
2512
|
-
const agentPath = readStringField(item, "agentPath");
|
|
2513
|
-
if (childThreadId) this.registerDirectSpawnChild(state, turnId, {
|
|
2514
|
-
parentThreadId,
|
|
2515
|
-
childThreadId,
|
|
2516
|
-
...agentPath === void 0 ? {} : { agentPath }
|
|
2517
|
-
}, owner);
|
|
2518
|
-
return state;
|
|
2519
|
-
}
|
|
2520
|
-
const isCompletedSpawnAgentTool = notification.method === "item/completed" && readStringField(item, "type") === "collabAgentToolCall" && normalizeIdentifier(readStringField(item, "tool")) === "spawnagent" && normalizeIdentifier(readStringField(item, "status")) === "completed";
|
|
2521
|
-
if (normalizeIdentifier(readStringField(item, "tool")) === "closeagent") return state;
|
|
2522
|
-
const childThreadIds = new Set(readStringArray(item?.receiverThreadIds));
|
|
2523
|
-
let accepted = true;
|
|
2524
|
-
for (const childThreadId of childThreadIds) accepted = Boolean(isCompletedSpawnAgentTool ? this.registerDirectSpawnChild(state, turnId, {
|
|
2525
|
-
parentThreadId,
|
|
2526
|
-
childThreadId
|
|
2527
|
-
}, owner) : this.registerChildThread(state, childThreadId)) && accepted;
|
|
2528
|
-
if (!accepted) return;
|
|
2529
|
-
}
|
|
2530
|
-
return state;
|
|
2531
|
-
}
|
|
2532
|
-
}
|
|
2533
|
-
handleClosedChild(notification, state) {
|
|
2534
|
-
if (notification.method !== "item/completed") return;
|
|
2535
|
-
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
2536
|
-
const item = isJsonObject(params?.item) ? params.item : void 0;
|
|
2537
|
-
if (readStringField(item, "type") !== "collabAgentToolCall" || normalizeIdentifier(readStringField(item, "tool")) !== "closeagent" || normalizeIdentifier(readStringField(item, "status")) !== "completed") return;
|
|
2538
|
-
const childThreadIds = /* @__PURE__ */ new Set([...readStringArray(item?.receiverThreadIds), ...readObjectStringKeys(item?.agentsStates)]);
|
|
2539
|
-
for (const childThreadId of childThreadIds) {
|
|
2540
|
-
const childState = this.childStates.get(childThreadId);
|
|
2541
|
-
if (childState && childState.parentThreadId !== state.parentThreadId) continue;
|
|
2542
|
-
if (childState) this.retireChild(state, childState, "Codex native subagent was closed.");
|
|
2543
|
-
else this.updateChildThreadOwnership("release", childThreadId, this.releaseChildThread);
|
|
2544
|
-
}
|
|
2545
|
-
}
|
|
2546
|
-
retireChild(state, childState, summary) {
|
|
2547
|
-
if (!childState.terminal) {
|
|
2548
|
-
childState.terminal = true;
|
|
2549
|
-
const revision = this.threadStatusRevisions.get(childState.childThreadId);
|
|
2550
|
-
if (revision) revision.terminal = true;
|
|
2551
|
-
const eventAt = this.now();
|
|
2552
|
-
state.mirror?.markAuthoritativeCompletion(childState.childThreadId);
|
|
2553
|
-
state.taskRuntime?.finalizeTaskRunByRunId({
|
|
2554
|
-
runId: codexNativeSubagentRunId(childState.childThreadId),
|
|
2555
|
-
status: "cancelled",
|
|
2556
|
-
endedAt: eventAt,
|
|
2557
|
-
lastEventAt: eventAt,
|
|
2558
|
-
error: summary,
|
|
2559
|
-
progressSummary: summary,
|
|
2560
|
-
terminalSummary: summary
|
|
2561
|
-
});
|
|
2562
|
-
}
|
|
2563
|
-
if (childState.pendingCompletion) {
|
|
2564
|
-
childState.pendingCompletion = void 0;
|
|
2565
|
-
state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
|
|
2566
|
-
runId: codexNativeSubagentRunId(childState.childThreadId),
|
|
2567
|
-
deliveryStatus: "failed",
|
|
2568
|
-
error: summary
|
|
2569
|
-
});
|
|
2570
|
-
}
|
|
2571
|
-
this.unregisterChild(childState, { retainSubscription: false });
|
|
2572
|
-
this.updateChildThreadOwnership("release", childState.childThreadId, this.releaseChildThread);
|
|
2573
|
-
}
|
|
2574
|
-
async handleCompletionNotification(notification) {
|
|
2575
|
-
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
2576
|
-
const parentThreadId = params ? readStringField(params, "threadId")?.trim() : void 0;
|
|
2577
|
-
const state = parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
|
|
2578
|
-
if (!state) return;
|
|
2579
|
-
for (const nativeCompletion of codexNativeSubagentNotifications.fromNotification(notification)) {
|
|
2580
|
-
const childThreadId = this.childThreadIdsByAgentPath.get(buildParentAgentPathKey(state.parentThreadId, nativeCompletion.agentPath));
|
|
2581
|
-
const childState = childThreadId ? this.childStates.get(childThreadId) : void 0;
|
|
2582
|
-
if (!childState || childState.parentThreadId !== state.parentThreadId || childState.terminal) {
|
|
2583
|
-
embeddedAgentLog.warn("Ignoring Codex native subagent completion for unknown child thread", {
|
|
2584
|
-
parentThreadId: state.parentThreadId,
|
|
2585
|
-
agentPath: nativeCompletion.agentPath
|
|
2586
|
-
});
|
|
2587
|
-
continue;
|
|
2588
|
-
}
|
|
2589
|
-
const completion = {
|
|
2590
|
-
childThreadId: childState.childThreadId,
|
|
2591
|
-
status: nativeCompletion.status,
|
|
2592
|
-
statusLabel: nativeCompletion.statusLabel,
|
|
2593
|
-
result: nativeCompletion.result
|
|
2594
|
-
};
|
|
2595
|
-
await this.processObservedCompletion(state, childState, completion);
|
|
2596
|
-
}
|
|
2597
|
-
}
|
|
2598
|
-
async processObservedCompletion(state, childState, completion) {
|
|
2599
|
-
if (!isNoFinalCompletion(completion)) {
|
|
2600
|
-
await this.processCompletion(state, childState, completion);
|
|
2601
|
-
return;
|
|
2602
|
-
}
|
|
2603
|
-
this.resumeChild(childState, { scheduleRecovery: false });
|
|
2604
|
-
this.setRecoveryFallback(childState, completion, this.now());
|
|
2605
|
-
await this.reconcileChildThread(childState.childThreadId).catch((error) => {
|
|
2606
|
-
this.logRecoveryFailure(childState.childThreadId, error);
|
|
2607
|
-
return false;
|
|
2608
|
-
});
|
|
2609
|
-
}
|
|
2610
|
-
async reconcileChildState(childState) {
|
|
2611
|
-
const state = this.parentStates.get(childState.parentThreadId);
|
|
2612
|
-
if (!state) return false;
|
|
2613
|
-
const statusRead = this.retainThreadStatusRevision(childState.childThreadId);
|
|
2614
|
-
try {
|
|
2615
|
-
const recovery = await this.readThreadRecovery(childState.childThreadId);
|
|
2616
|
-
if (!statusRead.isCurrent() || this.childStates.get(childState.childThreadId) !== childState) return false;
|
|
2617
|
-
if (recovery.parentThreadId && recovery.parentThreadId !== childState.parentThreadId) {
|
|
2618
|
-
embeddedAgentLog.warn("Codex native subagent parent did not match monitor state", {
|
|
2619
|
-
childThreadId: childState.childThreadId,
|
|
2620
|
-
expectedParentThreadId: childState.parentThreadId,
|
|
2621
|
-
actualParentThreadId: recovery.parentThreadId
|
|
2622
|
-
});
|
|
2623
|
-
this.unregisterChild(childState);
|
|
2624
|
-
return false;
|
|
2625
|
-
}
|
|
2626
|
-
if (recovery.agentPath) this.registerAgentPath(childState, recovery.agentPath);
|
|
2627
|
-
if (recovery.threadState === "active") {
|
|
2628
|
-
this.observeActiveChild(childState);
|
|
2629
|
-
return false;
|
|
2630
|
-
}
|
|
2631
|
-
if (recovery.threadState === "other") this.clearSystemErrorFallback(childState);
|
|
2632
|
-
if (recovery.resumable) {
|
|
2633
|
-
this.settleResumableChild(childState);
|
|
2634
|
-
return false;
|
|
2635
|
-
}
|
|
2636
|
-
const completion = recovery.completion;
|
|
2637
|
-
if (!completion) {
|
|
2638
|
-
if (recovery.fallbackCompletion) this.setRecoveryFallback(childState, recovery.fallbackCompletion, recovery.fallbackCompletion.completedAt ?? this.now());
|
|
2639
|
-
return false;
|
|
2640
|
-
}
|
|
2641
|
-
if (isNoFinalCompletion(completion)) {
|
|
2642
|
-
this.setRecoveryFallback(childState, completion, completion.completedAt ?? this.now());
|
|
2643
|
-
return false;
|
|
2644
|
-
}
|
|
2645
|
-
await this.processCompletion(state, childState, completion, completion.completedAt);
|
|
2646
|
-
return true;
|
|
2647
|
-
} finally {
|
|
2648
|
-
statusRead.release();
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
requestThreadRead(childThreadId, includeTurns) {
|
|
2652
|
-
return this.client.request("thread/read", {
|
|
2653
|
-
threadId: childThreadId,
|
|
2654
|
-
includeTurns
|
|
2655
|
-
}, { timeoutMs: THREAD_READ_TIMEOUT_MS });
|
|
2656
|
-
}
|
|
2657
|
-
requestLatestThreadTurn(childThreadId) {
|
|
2658
|
-
return this.client.request("thread/turns/list", {
|
|
2659
|
-
threadId: childThreadId,
|
|
2660
|
-
limit: 1,
|
|
2661
|
-
sortDirection: "desc",
|
|
2662
|
-
itemsView: "full"
|
|
2663
|
-
}, { timeoutMs: THREAD_READ_TIMEOUT_MS });
|
|
2664
|
-
}
|
|
2665
|
-
async readThreadRecovery(childThreadId) {
|
|
2666
|
-
const response = await this.requestThreadRead(childThreadId, true).catch(() => this.requestThreadRead(childThreadId, false));
|
|
2667
|
-
const thread = isJsonObject(response.thread) ? response.thread : void 0;
|
|
2668
|
-
if (!thread || readStringField(thread, "id")?.trim() !== childThreadId) return {
|
|
2669
|
-
resumable: false,
|
|
2670
|
-
threadState: "unavailable"
|
|
2671
|
-
};
|
|
2672
|
-
const threadStatus = isJsonObject(thread.status) ? normalizeIdentifier(readStringField(thread.status, "type")) : void 0;
|
|
2673
|
-
let completion;
|
|
2674
|
-
let fallbackCompletion;
|
|
2675
|
-
let resumable = false;
|
|
2676
|
-
let threadState = threadStatus === "active" ? "active" : threadStatus === "systemerror" ? "system_error" : threadStatus ? "other" : "unavailable";
|
|
2677
|
-
if (threadStatus === "systemerror") {
|
|
2678
|
-
const turnsResponse = await this.requestLatestThreadTurn(childThreadId).catch(() => void 0);
|
|
2679
|
-
const data = isJsonObject(turnsResponse) && Array.isArray(turnsResponse.data) ? turnsResponse.data : [];
|
|
2680
|
-
const latestTurn = isJsonObject(data[0]) ? data[0] : void 0;
|
|
2681
|
-
const latestTurnStatus = normalizeIdentifier(readStringField(latestTurn, "status"));
|
|
2682
|
-
completion = latestTurn && latestTurnStatus === "failed" ? readTurnCompletion(latestTurn, childThreadId) : void 0;
|
|
2683
|
-
if (latestTurnStatus === "inprogress") threadState = "active";
|
|
2684
|
-
else if (!completion) fallbackCompletion = systemErrorFallbackCompletion(childThreadId);
|
|
2685
|
-
} else if (threadStatus !== "active") {
|
|
2686
|
-
const turnRecovery = readThreadTurnRecovery(thread, childThreadId);
|
|
2687
|
-
completion = turnRecovery.completion;
|
|
2688
|
-
resumable = turnRecovery.resumable;
|
|
2689
|
-
}
|
|
2690
|
-
return {
|
|
2691
|
-
parentThreadId: readThreadParentThreadId(thread),
|
|
2692
|
-
agentPath: normalizeOptionalString(readStringField(readThreadSpawnSource(thread), "agent_path")),
|
|
2693
|
-
completion,
|
|
2694
|
-
fallbackCompletion,
|
|
2695
|
-
resumable,
|
|
2696
|
-
threadState
|
|
2697
|
-
};
|
|
2698
|
-
}
|
|
2699
|
-
async processCompletion(state, childState, completion, eventAt = this.now()) {
|
|
2700
|
-
if (childState.terminal) return;
|
|
2701
|
-
if (!this.claimCompletionDelivery(state, childState)) {
|
|
2702
|
-
this.unregisterChild(childState);
|
|
2703
|
-
return;
|
|
2704
|
-
}
|
|
2705
|
-
childState.terminal = true;
|
|
2706
|
-
const revision = this.threadStatusRevisions.get(childState.childThreadId);
|
|
2707
|
-
if (revision) revision.terminal = true;
|
|
2708
|
-
this.releaseDirectChild(childState);
|
|
2709
|
-
this.clearRecoveryTimers(childState);
|
|
2710
|
-
state.mirror?.markAuthoritativeCompletion(completion.childThreadId);
|
|
2711
|
-
state.taskRuntime?.finalizeTaskRunByRunId({
|
|
2712
|
-
runId: codexNativeSubagentRunId(completion.childThreadId),
|
|
2713
|
-
status: completion.status,
|
|
2714
|
-
endedAt: eventAt,
|
|
2715
|
-
lastEventAt: eventAt,
|
|
2716
|
-
...completion.status === "succeeded" ? {} : { error: completion.result },
|
|
2717
|
-
progressSummary: completion.result,
|
|
2718
|
-
terminalSummary: completion.result
|
|
2719
|
-
});
|
|
2720
|
-
if (!state.requesterSessionKey || !state.taskRuntimeScope) {
|
|
2721
|
-
this.unregisterChild(childState);
|
|
2722
|
-
return;
|
|
2723
|
-
}
|
|
2724
|
-
if (childState.nativeCompletionDelivered) {
|
|
2725
|
-
this.finishCompletionDelivery(state, childState);
|
|
2726
|
-
return;
|
|
2727
|
-
}
|
|
2728
|
-
childState.pendingCompletion = completion;
|
|
2729
|
-
state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
|
|
2730
|
-
runId: codexNativeSubagentRunId(completion.childThreadId),
|
|
2731
|
-
deliveryStatus: "pending"
|
|
2732
|
-
});
|
|
2733
|
-
this.releaseClientRetentionIfIdle();
|
|
2734
|
-
await this.deliverPendingCompletion(state, childState);
|
|
2735
|
-
}
|
|
2736
|
-
async deliverPendingCompletion(state, childState) {
|
|
2737
|
-
const completion = childState.pendingCompletion;
|
|
2738
|
-
if (!completion || !state.requesterSessionKey || !state.taskRuntimeScope) return;
|
|
2739
|
-
if (state.owners.size > 0) return;
|
|
2740
|
-
if (childState.deliveringCompletion || childState.completionDeliveryTimer) return;
|
|
2741
|
-
childState.deliveringCompletion = true;
|
|
2742
|
-
try {
|
|
2743
|
-
const delivery = await this.runtime.deliverAgentHarnessTaskCompletion({
|
|
2744
|
-
scope: state.taskRuntimeScope,
|
|
2745
|
-
childSessionKey: codexNativeSubagentRunId(completion.childThreadId),
|
|
2746
|
-
childSessionId: completion.childThreadId,
|
|
2747
|
-
announceId: `codex-native:${state.parentThreadId}:${completion.childThreadId}:${completion.status}`,
|
|
2748
|
-
announceType: "Codex native subagent",
|
|
2749
|
-
taskLabel: "Codex native subagent",
|
|
2750
|
-
status: completion.status,
|
|
2751
|
-
statusLabel: completion.statusLabel,
|
|
2752
|
-
result: completion.result,
|
|
2753
|
-
replyInstruction: "Use the Codex native subagent result to continue or wrap up the parent task. If this is a Discord/channel session, send the visible response with the message tool instead of only writing a transcript final answer. Reply in your normal assistant voice and do not expose internal notification markup."
|
|
2754
|
-
});
|
|
2755
|
-
if (this.childStates.get(childState.childThreadId) !== childState || this.parentStates.get(state.parentThreadId) !== state) return;
|
|
2756
|
-
if (isDurableAgentHarnessCompletionDelivery(delivery)) {
|
|
2757
|
-
this.finishCompletionDelivery(state, childState);
|
|
2758
|
-
return;
|
|
2759
|
-
}
|
|
2760
|
-
const error = delivery.error ?? "completion delivery did not produce a parent response";
|
|
2761
|
-
state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
|
|
2762
|
-
runId: codexNativeSubagentRunId(completion.childThreadId),
|
|
2763
|
-
deliveryStatus: "pending",
|
|
2764
|
-
error
|
|
2765
|
-
});
|
|
2766
|
-
this.scheduleCompletionDeliveryRetry(childState, error);
|
|
2767
|
-
} catch (error) {
|
|
2768
|
-
if (this.childStates.get(childState.childThreadId) !== childState || this.parentStates.get(state.parentThreadId) !== state) return;
|
|
2769
|
-
const message = formatErrorMessage(error);
|
|
2770
|
-
state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
|
|
2771
|
-
runId: codexNativeSubagentRunId(completion.childThreadId),
|
|
2772
|
-
deliveryStatus: "pending",
|
|
2773
|
-
error: message
|
|
2774
|
-
});
|
|
2775
|
-
this.scheduleCompletionDeliveryRetry(childState, message);
|
|
2776
|
-
embeddedAgentLog.warn("Failed to deliver Codex native subagent completion", {
|
|
2777
|
-
parentThreadId: state.parentThreadId,
|
|
2778
|
-
childThreadId: completion.childThreadId,
|
|
2779
|
-
error: message
|
|
2780
|
-
});
|
|
2781
|
-
} finally {
|
|
2782
|
-
childState.deliveringCompletion = false;
|
|
2783
|
-
}
|
|
2784
|
-
}
|
|
2785
|
-
recordNativeCompletionDelivery(state, notification) {
|
|
2786
|
-
for (const agentPath of codexNativeSubagentNotifications.deliveredAgentPaths(notification)) {
|
|
2787
|
-
const key = buildParentAgentPathKey(state.parentThreadId, agentPath);
|
|
2788
|
-
state.nativeCompletionReceipts.add(key);
|
|
2789
|
-
const childThreadId = this.childThreadIdsByAgentPath.get(key);
|
|
2790
|
-
const child = childThreadId ? this.childStates.get(childThreadId) : void 0;
|
|
2791
|
-
if (!child || child.parentThreadId !== state.parentThreadId) continue;
|
|
2792
|
-
child.nativeCompletionDelivered = true;
|
|
2793
|
-
if (child.pendingCompletion && !child.deliveringCompletion) this.finishCompletionDelivery(state, child);
|
|
2794
|
-
}
|
|
2795
|
-
}
|
|
2796
|
-
finishCompletionDelivery(state, child) {
|
|
2797
|
-
child.pendingCompletion = void 0;
|
|
2798
|
-
child.completionDeliveryAttempt = 0;
|
|
2799
|
-
state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
|
|
2800
|
-
runId: codexNativeSubagentRunId(child.childThreadId),
|
|
2801
|
-
deliveryStatus: "delivered"
|
|
2802
|
-
});
|
|
2803
|
-
this.unregisterChild(child);
|
|
2804
|
-
}
|
|
2805
|
-
deliverDetachedCompletions(state) {
|
|
2806
|
-
for (const child of this.childStates.values()) if (child.parentThreadId === state.parentThreadId && child.pendingCompletion) this.deliverPendingCompletion(state, child);
|
|
2807
|
-
}
|
|
2808
|
-
scheduleCompletionDeliveryRetry(childState, error) {
|
|
2809
|
-
if (!childState.pendingCompletion || childState.completionDeliveryTimer || this.childStates.get(childState.childThreadId) !== childState) return;
|
|
2810
|
-
if (childState.completionDeliveryAttempt >= this.completionDeliveryMaxRetries) {
|
|
2811
|
-
this.parentStates.get(childState.parentThreadId)?.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
|
|
2812
|
-
runId: codexNativeSubagentRunId(childState.childThreadId),
|
|
2813
|
-
deliveryStatus: "failed",
|
|
2814
|
-
error
|
|
2815
|
-
});
|
|
2816
|
-
this.unregisterChild(childState);
|
|
2817
|
-
return;
|
|
2818
|
-
}
|
|
2819
|
-
const delayMs = delayForAttempt(this.completionDeliveryRetryDelaysMs, childState.completionDeliveryAttempt++);
|
|
2820
|
-
childState.completionDeliveryTimer = setTimeout(() => {
|
|
2821
|
-
childState.completionDeliveryTimer = void 0;
|
|
2822
|
-
if (this.childStates.get(childState.childThreadId) !== childState) return;
|
|
2823
|
-
const state = this.parentStates.get(childState.parentThreadId);
|
|
2824
|
-
if (state) this.deliverPendingCompletion(state, childState);
|
|
2825
|
-
}, delayMs);
|
|
2826
|
-
unrefTimer(childState.completionDeliveryTimer);
|
|
2827
|
-
}
|
|
2828
|
-
registerChildThread(state, childThreadIdInput, options = {}) {
|
|
2829
|
-
const parentThreadId = state.parentThreadId;
|
|
2830
|
-
const childThreadId = childThreadIdInput.trim();
|
|
2831
|
-
if (!parentThreadId || !childThreadId || this.disposed) return;
|
|
2832
|
-
if (options.claimDirectChild && this.threadStatusRevisions.get(childThreadId)?.terminal) return;
|
|
2833
|
-
let childState = this.childStates.get(childThreadId);
|
|
2834
|
-
if (childState && childState.parentThreadId !== parentThreadId) {
|
|
2835
|
-
embeddedAgentLog.warn("Ignoring Codex native subagent child reparenting", {
|
|
2836
|
-
childThreadId,
|
|
2837
|
-
existingParentThreadId: childState.parentThreadId,
|
|
2838
|
-
attemptedParentThreadId: parentThreadId
|
|
2839
|
-
});
|
|
2840
|
-
return;
|
|
2841
|
-
}
|
|
2842
|
-
if (!childState) {
|
|
2843
|
-
this.updateChildThreadOwnership("claim", childThreadId, this.claimChildThread);
|
|
2844
|
-
this.releaseClientRetention ??= this.retainClient?.();
|
|
2845
|
-
if (!this.parentThreadRetentions.has(parentThreadId)) {
|
|
2846
|
-
const releaseParentThread = this.retainParentThread?.(parentThreadId);
|
|
2847
|
-
if (releaseParentThread) this.parentThreadRetentions.set(parentThreadId, releaseParentThread);
|
|
2848
|
-
}
|
|
2849
|
-
childState = {
|
|
2850
|
-
childThreadId,
|
|
2851
|
-
parentThreadId,
|
|
2852
|
-
agentId: state.agentId,
|
|
2853
|
-
agentPathKeys: /* @__PURE__ */ new Set(),
|
|
2854
|
-
assistantMessagesByTurn: /* @__PURE__ */ new Map(),
|
|
2855
|
-
recoveryAttempt: 0,
|
|
2856
|
-
terminal: false,
|
|
2857
|
-
nativeCompletionDelivered: false,
|
|
2858
|
-
settledWithoutCompletion: false,
|
|
2859
|
-
completionDeliveryAttempt: 0,
|
|
2860
|
-
deliveringCompletion: false
|
|
2861
|
-
};
|
|
2862
|
-
this.childStates.set(childThreadId, childState);
|
|
2863
|
-
this.threadStatusRevisions.set(childThreadId, this.threadStatusRevisions.get(childThreadId) ?? {
|
|
2864
|
-
value: 0,
|
|
2865
|
-
readers: 0,
|
|
2866
|
-
parentThreadId
|
|
2867
|
-
});
|
|
2868
|
-
}
|
|
2869
|
-
if (options.claimDirectChild && !childState.terminal && !childState.settledWithoutCompletion && !childState.releaseDirectChild) childState.releaseDirectChild = options.claimDirectChild(childThreadId);
|
|
2870
|
-
this.registerAgentPath(childState, childThreadId);
|
|
2871
|
-
state.mirror?.markAuthoritativeCompletionExpected(childThreadId);
|
|
2872
|
-
const agentPath = normalizeOptionalString(options.agentPath);
|
|
2873
|
-
if (agentPath) this.registerAgentPath(childState, agentPath);
|
|
2874
|
-
this.scheduleRecoveryPoll(childState);
|
|
2875
|
-
return childState;
|
|
2876
|
-
}
|
|
2877
|
-
resolveParentOwner(state, turnIdInput) {
|
|
2878
|
-
const turnId = turnIdInput?.trim();
|
|
2879
|
-
if (!turnId) return;
|
|
2880
|
-
const owners = [...state.owners.values()].filter((owner) => owner.turnId === turnId);
|
|
2881
|
-
return owners.length === 1 ? owners[0] : void 0;
|
|
2882
|
-
}
|
|
2883
|
-
registerDirectSpawnChild(state, turnIdInput, evidence, owner) {
|
|
2884
|
-
const childState = this.registerChildThread(state, evidence.childThreadId, {
|
|
2885
|
-
...evidence.agentPath === void 0 ? {} : { agentPath: evidence.agentPath },
|
|
2886
|
-
...owner?.claimDirectChild ? { claimDirectChild: owner.claimDirectChild } : {}
|
|
2887
|
-
});
|
|
2888
|
-
if (!owner) this.bufferPendingDirectSpawnEvidence(turnIdInput, evidence);
|
|
2889
|
-
else if (childState) owner.onDirectChildAccepted?.();
|
|
2890
|
-
return childState;
|
|
2891
|
-
}
|
|
2892
|
-
bufferPendingDirectSpawnEvidence(turnIdInput, evidence) {
|
|
2893
|
-
const turnId = turnIdInput?.trim();
|
|
2894
|
-
if (!turnId || !this.hasUnboundParentOwner(evidence.parentThreadId)) return;
|
|
2895
|
-
const pending = this.pendingDirectSpawnEvidence.get(turnId) ?? [];
|
|
2896
|
-
if (pending.some((candidate) => candidate.parentThreadId === evidence.parentThreadId && candidate.childThreadId === evidence.childThreadId && candidate.agentPath === evidence.agentPath) || [...this.pendingDirectSpawnEvidence.values()].reduce((count, entries) => count + entries.length, 0) >= MAX_PENDING_DIRECT_SPAWN_EVIDENCE) return;
|
|
2897
|
-
pending.push(evidence);
|
|
2898
|
-
this.pendingDirectSpawnEvidence.set(turnId, pending);
|
|
2899
|
-
}
|
|
2900
|
-
drainPendingDirectSpawnEvidence(state, owner, turnId) {
|
|
2901
|
-
const pending = this.pendingDirectSpawnEvidence.get(turnId);
|
|
2902
|
-
this.pendingDirectSpawnEvidence.delete(turnId);
|
|
2903
|
-
if (!pending || !owner.claimDirectChild) return;
|
|
2904
|
-
for (const evidence of pending) if (evidence.parentThreadId === state.parentThreadId) {
|
|
2905
|
-
if (this.registerChildThread(state, evidence.childThreadId, {
|
|
2906
|
-
...evidence.agentPath === void 0 ? {} : { agentPath: evidence.agentPath },
|
|
2907
|
-
claimDirectChild: owner.claimDirectChild
|
|
2908
|
-
})) owner.onDirectChildAccepted?.();
|
|
2909
|
-
}
|
|
2910
|
-
}
|
|
2911
|
-
hasUnboundParentOwner(parentThreadId) {
|
|
2912
|
-
return [...parentThreadId ? [this.parentStates.get(parentThreadId)].filter((state) => Boolean(state)) : this.parentStates.values()].some((state) => [...state.owners.values()].some((owner) => owner.turnId === void 0));
|
|
2913
|
-
}
|
|
2914
|
-
clearUnconsumablePendingDirectSpawnEvidence() {
|
|
2915
|
-
if (!this.hasUnboundParentOwner()) this.pendingDirectSpawnEvidence.clear();
|
|
2916
|
-
}
|
|
2917
|
-
clearPendingDirectSpawnEvidenceForParent(parentThreadId) {
|
|
2918
|
-
this.filterPendingDirectSpawnEvidence((evidence) => evidence.parentThreadId !== parentThreadId);
|
|
2919
|
-
}
|
|
2920
|
-
removePendingDirectSpawnEvidenceForChild(childThreadId) {
|
|
2921
|
-
this.filterPendingDirectSpawnEvidence((evidence) => evidence.childThreadId !== childThreadId);
|
|
2922
|
-
}
|
|
2923
|
-
filterPendingDirectSpawnEvidence(keep) {
|
|
2924
|
-
for (const [turnId, pending] of this.pendingDirectSpawnEvidence) {
|
|
2925
|
-
const remaining = pending.filter(keep);
|
|
2926
|
-
if (remaining.length) this.pendingDirectSpawnEvidence.set(turnId, remaining);
|
|
2927
|
-
else this.pendingDirectSpawnEvidence.delete(turnId);
|
|
2928
|
-
}
|
|
2929
|
-
}
|
|
2930
|
-
registerAgentPath(childState, agentPath) {
|
|
2931
|
-
const key = buildParentAgentPathKey(childState.parentThreadId, agentPath);
|
|
2932
|
-
const existingChild = this.childThreadIdsByAgentPath.get(key);
|
|
2933
|
-
if (existingChild && existingChild !== childState.childThreadId) {
|
|
2934
|
-
embeddedAgentLog.warn("Ignoring conflicting Codex native subagent agent path", {
|
|
2935
|
-
parentThreadId: childState.parentThreadId,
|
|
2936
|
-
agentPath,
|
|
2937
|
-
existingChildThreadId: existingChild,
|
|
2938
|
-
attemptedChildThreadId: childState.childThreadId
|
|
2939
|
-
});
|
|
2940
|
-
return;
|
|
2941
|
-
}
|
|
2942
|
-
this.childThreadIdsByAgentPath.set(key, childState.childThreadId);
|
|
2943
|
-
childState.agentPathKeys.add(key);
|
|
2944
|
-
if (this.parentStates.get(childState.parentThreadId)?.nativeCompletionReceipts.has(key)) childState.nativeCompletionDelivered = true;
|
|
2945
|
-
}
|
|
2946
|
-
unregisterChild(childState, options = {}) {
|
|
2947
|
-
this.releaseDirectChild(childState);
|
|
2948
|
-
if (childState.terminal && options.retainSubscription !== false && !this.disposed) this.updateChildThreadOwnership("retain", childState.childThreadId, this.retainChildThread);
|
|
2949
|
-
this.clearRecoveryTimers(childState);
|
|
2950
|
-
if (childState.completionDeliveryTimer) clearTimeout(childState.completionDeliveryTimer);
|
|
2951
|
-
const deliveryOwnerKey = childState.deliveryOwnerKey;
|
|
2952
|
-
if (deliveryOwnerKey && completionDeliveryOwners.get(deliveryOwnerKey) === childState) completionDeliveryOwners.delete(deliveryOwnerKey);
|
|
2953
|
-
childState.deliveryOwnerKey = void 0;
|
|
2954
|
-
for (const key of childState.agentPathKeys) if (this.childThreadIdsByAgentPath.get(key) === childState.childThreadId) this.childThreadIdsByAgentPath.delete(key);
|
|
2955
|
-
if (this.childStates.get(childState.childThreadId) === childState) this.childStates.delete(childState.childThreadId);
|
|
2956
|
-
if (![...this.childStates.values()].some((remainingChild) => remainingChild.parentThreadId === childState.parentThreadId)) {
|
|
2957
|
-
const releaseParentThread = this.parentThreadRetentions.get(childState.parentThreadId);
|
|
2958
|
-
this.parentThreadRetentions.delete(childState.parentThreadId);
|
|
2959
|
-
releaseParentThread?.();
|
|
2960
|
-
}
|
|
2961
|
-
this.collectThreadStatusRevision(childState.childThreadId);
|
|
2962
|
-
this.releaseClientRetentionIfIdle();
|
|
2963
|
-
const state = this.parentStates.get(childState.parentThreadId);
|
|
2964
|
-
if (state) this.pruneParentIfUnused(state);
|
|
2965
|
-
}
|
|
2966
|
-
releaseDirectChild(childState) {
|
|
2967
|
-
const release = childState.releaseDirectChild;
|
|
2968
|
-
childState.releaseDirectChild = void 0;
|
|
2969
|
-
release?.();
|
|
2970
|
-
}
|
|
2971
|
-
rejectPendingDirectChild(state, childThreadId, reason) {
|
|
2972
|
-
for (const owner of state.owners.values()) owner.rejectPendingDirectChild?.(childThreadId, reason);
|
|
2973
|
-
}
|
|
2974
|
-
updateChildThreadOwnership(operation, childThreadId, update) {
|
|
2975
|
-
if (!update) return;
|
|
2976
|
-
update(childThreadId).catch((error) => {
|
|
2977
|
-
embeddedAgentLog.warn("Failed to update Codex native subagent thread ownership", {
|
|
2978
|
-
operation,
|
|
2979
|
-
childThreadId,
|
|
2980
|
-
error: formatErrorMessage(error)
|
|
2981
|
-
});
|
|
2982
|
-
});
|
|
2983
|
-
}
|
|
2984
|
-
releaseClientRetentionIfIdle() {
|
|
2985
|
-
if ([...this.childStates.values()].some((childState) => !childState.terminal && !childState.settledWithoutCompletion)) return;
|
|
2986
|
-
this.releaseRetainedClient();
|
|
2987
|
-
}
|
|
2988
|
-
releaseRetainedClient() {
|
|
2989
|
-
const release = this.releaseClientRetention;
|
|
2990
|
-
this.releaseClientRetention = void 0;
|
|
2991
|
-
release?.();
|
|
2992
|
-
}
|
|
2993
|
-
claimCompletionDelivery(state, childState) {
|
|
2994
|
-
const requesterSessionKey = state.requesterSessionKey?.trim();
|
|
2995
|
-
if (!requesterSessionKey) return true;
|
|
2996
|
-
const key = `${requesterSessionKey}\0${childState.childThreadId}`;
|
|
2997
|
-
const owner = completionDeliveryOwners.get(key);
|
|
2998
|
-
if (owner) return owner === childState;
|
|
2999
|
-
const runId = codexNativeSubagentRunId(childState.childThreadId);
|
|
3000
|
-
if (state.taskRuntime?.listTaskRecords().some((task) => task.runId === runId && task.deliveryStatus === "delivered")) return false;
|
|
3001
|
-
completionDeliveryOwners.set(key, childState);
|
|
3002
|
-
childState.deliveryOwnerKey = key;
|
|
3003
|
-
return true;
|
|
3004
|
-
}
|
|
3005
|
-
pruneParentIfUnused(state) {
|
|
3006
|
-
if (state.owners.size > 0) return;
|
|
3007
|
-
for (const childState of this.childStates.values()) if (childState.parentThreadId === state.parentThreadId) return;
|
|
3008
|
-
if (this.parentStates.get(state.parentThreadId) === state) {
|
|
3009
|
-
this.clearTerminalRevisionsForParent(state.parentThreadId);
|
|
3010
|
-
this.parentStates.delete(state.parentThreadId);
|
|
3011
|
-
}
|
|
3012
|
-
}
|
|
3013
|
-
clearTerminalRevisionsForParent(parentThreadId) {
|
|
3014
|
-
for (const [threadId, revision] of this.threadStatusRevisions) if (revision.parentThreadId === parentThreadId) this.collectThreadStatusRevision(threadId, revision);
|
|
3015
|
-
}
|
|
3016
|
-
collectThreadStatusRevision(threadId, revision = this.threadStatusRevisions.get(threadId)) {
|
|
3017
|
-
if (!revision || revision.readers > 0 || this.childStates.has(threadId)) return;
|
|
3018
|
-
if ((revision.parentThreadId ? this.parentStates.get(revision.parentThreadId) : void 0)?.owners.size) return;
|
|
3019
|
-
if (this.threadStatusRevisions.get(threadId) === revision) this.threadStatusRevisions.delete(threadId);
|
|
3020
|
-
}
|
|
3021
|
-
scheduleRecoveryPoll(childState) {
|
|
3022
|
-
if (childState.terminal || childState.settledWithoutCompletion || childState.recoveryTimer || this.disposed || this.recoveryPollDelaysMs.length === 0) return;
|
|
3023
|
-
const delayMs = delayForAttempt(this.recoveryPollDelaysMs, childState.recoveryAttempt++);
|
|
3024
|
-
childState.recoveryTimer = setTimeout(() => {
|
|
3025
|
-
childState.recoveryTimer = void 0;
|
|
3026
|
-
this.reconcileChildThread(childState.childThreadId).catch((error) => {
|
|
3027
|
-
this.logRecoveryFailure(childState.childThreadId, error);
|
|
3028
|
-
return false;
|
|
3029
|
-
}).then(async (reconciled) => {
|
|
3030
|
-
if (reconciled || this.childStates.get(childState.childThreadId) !== childState) return;
|
|
3031
|
-
const fallback = childState.fallbackCompletion;
|
|
3032
|
-
const state = this.parentStates.get(childState.parentThreadId);
|
|
3033
|
-
if (fallback && state && childState.recoveryAttempt >= 2) {
|
|
3034
|
-
await this.processCompletion(state, childState, fallback, fallback.completedAt ?? this.now());
|
|
3035
|
-
return;
|
|
3036
|
-
}
|
|
3037
|
-
this.scheduleRecoveryPoll(childState);
|
|
3038
|
-
});
|
|
3039
|
-
}, delayMs);
|
|
3040
|
-
unrefTimer(childState.recoveryTimer);
|
|
3041
|
-
}
|
|
3042
|
-
setRecoveryFallback(childState, completion, eventAt) {
|
|
3043
|
-
if (childState.terminal) return;
|
|
3044
|
-
const current = childState.fallbackCompletion;
|
|
3045
|
-
if (current?.status === completion.status && current.statusLabel === completion.statusLabel && current.result === completion.result) return;
|
|
3046
|
-
if (childState.recoveryTimer) {
|
|
3047
|
-
clearTimeout(childState.recoveryTimer);
|
|
3048
|
-
childState.recoveryTimer = void 0;
|
|
3049
|
-
}
|
|
3050
|
-
childState.recoveryAttempt = 0;
|
|
3051
|
-
childState.fallbackCompletion = {
|
|
3052
|
-
...completion,
|
|
3053
|
-
completedAt: eventAt
|
|
3054
|
-
};
|
|
3055
|
-
this.scheduleRecoveryPoll(childState);
|
|
3056
|
-
}
|
|
3057
|
-
clearSystemErrorFallback(childState) {
|
|
3058
|
-
if (childState.fallbackCompletion?.statusLabel !== "system_error") return;
|
|
3059
|
-
childState.fallbackCompletion = void 0;
|
|
3060
|
-
}
|
|
3061
|
-
retainThreadStatusRevision(threadId) {
|
|
3062
|
-
const revision = this.threadStatusRevisions.get(threadId) ?? {
|
|
3063
|
-
value: 0,
|
|
3064
|
-
readers: 0
|
|
3065
|
-
};
|
|
3066
|
-
this.threadStatusRevisions.set(threadId, revision);
|
|
3067
|
-
revision.readers += 1;
|
|
3068
|
-
const capturedValue = revision.value;
|
|
3069
|
-
let retained = true;
|
|
3070
|
-
return {
|
|
3071
|
-
isCurrent: () => this.threadStatusRevisions.get(threadId) === revision && revision.value === capturedValue,
|
|
3072
|
-
release: () => {
|
|
3073
|
-
if (!retained) return;
|
|
3074
|
-
retained = false;
|
|
3075
|
-
revision.readers -= 1;
|
|
3076
|
-
this.collectThreadStatusRevision(threadId, revision);
|
|
3077
|
-
}
|
|
3078
|
-
};
|
|
3079
|
-
}
|
|
3080
|
-
clearRecoveryTimers(childState) {
|
|
3081
|
-
if (childState.recoveryTimer) {
|
|
3082
|
-
clearTimeout(childState.recoveryTimer);
|
|
3083
|
-
childState.recoveryTimer = void 0;
|
|
3084
|
-
}
|
|
3085
|
-
}
|
|
3086
|
-
async reconcileTaskRowsForParent(state) {
|
|
3087
|
-
if (this.disposed || this.parentStates.get(state.parentThreadId) !== state || !state.taskRuntime || !state.requesterSessionKey || !state.taskRuntimeScope) return;
|
|
3088
|
-
const candidates = /* @__PURE__ */ new Map();
|
|
3089
|
-
for (const task of state.taskRuntime.listTaskRecords()) {
|
|
3090
|
-
if (task.requesterSessionKey !== state.requesterSessionKey || !this.shouldReconcileCodexNativeTask(task)) continue;
|
|
3091
|
-
const childThreadId = task.runId.slice(13).trim();
|
|
3092
|
-
candidates.set(childThreadId, {
|
|
3093
|
-
parentState: state,
|
|
3094
|
-
nativeCompletionReceipts: state.nativeCompletionReceipts,
|
|
3095
|
-
requesterSessionKey: state.requesterSessionKey,
|
|
3096
|
-
childThreadId,
|
|
3097
|
-
recoveryAttempt: 0,
|
|
3098
|
-
taskRuntimeScope: state.taskRuntimeScope,
|
|
3099
|
-
agentId: state.agentId,
|
|
3100
|
-
taskRuntime: state.taskRuntime
|
|
3101
|
-
});
|
|
3102
|
-
}
|
|
3103
|
-
for (const candidate of candidates.values()) await this.reconcileTaskCandidate(candidate);
|
|
3104
|
-
}
|
|
3105
|
-
async reconcileTaskCandidate(candidate) {
|
|
3106
|
-
const key = `${candidate.requesterSessionKey}\0${candidate.childThreadId}`;
|
|
3107
|
-
const scheduled = this.taskReconciliationTimers.get(key);
|
|
3108
|
-
if (scheduled) {
|
|
3109
|
-
clearTimeout(scheduled);
|
|
3110
|
-
this.taskReconciliationTimers.delete(key);
|
|
3111
|
-
}
|
|
3112
|
-
const existing = this.taskReconciliations.get(key);
|
|
3113
|
-
if (existing) {
|
|
3114
|
-
await existing;
|
|
3115
|
-
return;
|
|
3116
|
-
}
|
|
3117
|
-
const reconciliation = this.reconcileTaskCandidateOnce(candidate);
|
|
3118
|
-
this.taskReconciliations.set(key, reconciliation);
|
|
3119
|
-
try {
|
|
3120
|
-
await reconciliation;
|
|
3121
|
-
} finally {
|
|
3122
|
-
if (this.taskReconciliations.get(key) === reconciliation) this.taskReconciliations.delete(key);
|
|
3123
|
-
}
|
|
3124
|
-
}
|
|
3125
|
-
scheduleTaskCandidateReconciliation(candidate) {
|
|
3126
|
-
const key = `${candidate.requesterSessionKey}\0${candidate.childThreadId}`;
|
|
3127
|
-
if (this.disposed || this.retiredParentStates.has(candidate.parentState) || this.recoveryPollDelaysMs.length === 0 || this.taskReconciliationTimers.has(key)) return;
|
|
3128
|
-
const delayMs = delayForAttempt(this.recoveryPollDelaysMs, candidate.recoveryAttempt++);
|
|
3129
|
-
const timer = setTimeout(() => {
|
|
3130
|
-
this.taskReconciliationTimers.delete(key);
|
|
3131
|
-
this.reconcileTaskCandidate(candidate).catch((error) => {
|
|
3132
|
-
this.logRecoveryFailure(candidate.childThreadId, error);
|
|
3133
|
-
this.scheduleTaskCandidateReconciliation(candidate);
|
|
3134
|
-
});
|
|
3135
|
-
}, delayMs);
|
|
3136
|
-
this.taskReconciliationTimers.set(key, timer);
|
|
3137
|
-
unrefTimer(timer);
|
|
3138
|
-
}
|
|
3139
|
-
async reconcileTaskCandidateOnce(candidate) {
|
|
3140
|
-
if (this.retiredParentStates.has(candidate.parentState)) return;
|
|
3141
|
-
const runId = codexNativeSubagentRunId(candidate.childThreadId);
|
|
3142
|
-
const task = candidate.taskRuntime.listTaskRecords().find((record) => record.runId === runId);
|
|
3143
|
-
if (!task || task.requesterSessionKey !== candidate.requesterSessionKey || !this.shouldReconcileCodexNativeTask(task)) return;
|
|
3144
|
-
const childBeforeRead = this.childStates.get(candidate.childThreadId);
|
|
3145
|
-
const statusRead = this.retainThreadStatusRevision(candidate.childThreadId);
|
|
3146
|
-
try {
|
|
3147
|
-
let recovery;
|
|
3148
|
-
try {
|
|
3149
|
-
recovery = await this.readThreadRecovery(candidate.childThreadId);
|
|
3150
|
-
} catch (error) {
|
|
3151
|
-
this.logRecoveryFailure(candidate.childThreadId, error);
|
|
3152
|
-
this.scheduleTaskCandidateReconciliation(candidate);
|
|
3153
|
-
return;
|
|
3154
|
-
}
|
|
3155
|
-
if (this.retiredParentStates.has(candidate.parentState)) return;
|
|
3156
|
-
if (!statusRead.isCurrent() || this.childStates.get(candidate.childThreadId) !== childBeforeRead) {
|
|
3157
|
-
this.scheduleTaskCandidateReconciliation(candidate);
|
|
3158
|
-
return;
|
|
3159
|
-
}
|
|
3160
|
-
const parentThreadId = recovery.parentThreadId;
|
|
3161
|
-
if (!parentThreadId) {
|
|
3162
|
-
this.scheduleTaskCandidateReconciliation(candidate);
|
|
3163
|
-
return;
|
|
3164
|
-
}
|
|
3165
|
-
let state = this.parentStates.get(parentThreadId);
|
|
3166
|
-
if (state && state.requesterSessionKey !== candidate.requesterSessionKey) return;
|
|
3167
|
-
if (!state) {
|
|
3168
|
-
state = {
|
|
3169
|
-
parentThreadId,
|
|
3170
|
-
owners: /* @__PURE__ */ new Map(),
|
|
3171
|
-
turnIds: /* @__PURE__ */ new Set(),
|
|
3172
|
-
nativeCompletionReceipts: /* @__PURE__ */ new Set(),
|
|
3173
|
-
requesterSessionKey: candidate.requesterSessionKey,
|
|
3174
|
-
taskRuntimeScope: candidate.taskRuntimeScope,
|
|
3175
|
-
agentId: candidate.agentId,
|
|
3176
|
-
taskRuntime: candidate.taskRuntime
|
|
3177
|
-
};
|
|
3178
|
-
this.prepareParentTaskRuntime(state);
|
|
3179
|
-
this.parentStates.set(parentThreadId, state);
|
|
3180
|
-
}
|
|
3181
|
-
const childState = this.registerChildThread(state, candidate.childThreadId, recovery.agentPath ? { agentPath: recovery.agentPath } : {});
|
|
3182
|
-
if (!childState) {
|
|
3183
|
-
this.pruneParentIfUnused(state);
|
|
3184
|
-
return;
|
|
3185
|
-
}
|
|
3186
|
-
if ([...childState.agentPathKeys].some((key) => candidate.nativeCompletionReceipts.has(key))) childState.nativeCompletionDelivered = true;
|
|
3187
|
-
if (recovery.threadState === "active") this.observeActiveChild(childState);
|
|
3188
|
-
if (recovery.threadState === "other") this.clearSystemErrorFallback(childState);
|
|
3189
|
-
if (recovery.resumable) {
|
|
3190
|
-
this.settleResumableChild(childState);
|
|
3191
|
-
return;
|
|
3192
|
-
}
|
|
3193
|
-
const completion = recovery.completion;
|
|
3194
|
-
if (!completion) {
|
|
3195
|
-
if (recovery.fallbackCompletion) {
|
|
3196
|
-
this.setRecoveryFallback(childState, recovery.fallbackCompletion, recovery.fallbackCompletion.completedAt ?? this.now());
|
|
3197
|
-
return;
|
|
3198
|
-
}
|
|
3199
|
-
this.scheduleRecoveryPoll(childState);
|
|
3200
|
-
return;
|
|
3201
|
-
}
|
|
3202
|
-
if (isNoFinalCompletion(completion)) {
|
|
3203
|
-
this.setRecoveryFallback(childState, completion, completion.completedAt ?? this.now());
|
|
3204
|
-
return;
|
|
3205
|
-
}
|
|
3206
|
-
await this.processCompletion(state, childState, completion, completion.completedAt);
|
|
3207
|
-
} finally {
|
|
3208
|
-
statusRead.release();
|
|
3209
|
-
}
|
|
3210
|
-
}
|
|
3211
|
-
shouldReconcileCodexNativeTask(task) {
|
|
3212
|
-
if (task.status === "queued" || task.status === "running" || task.deliveryStatus === "pending") return true;
|
|
3213
|
-
if (task.deliveryStatus !== "not_applicable" || task.endedAt === void 0) return false;
|
|
3214
|
-
return task.endedAt >= this.now() - RECENT_TERMINAL_TASK_RECONCILE_GRACE_MS;
|
|
3215
|
-
}
|
|
3216
|
-
logRecoveryFailure(childThreadId, error) {
|
|
3217
|
-
embeddedAgentLog.debug("Codex native subagent history is not ready", {
|
|
3218
|
-
childThreadId,
|
|
3219
|
-
error: formatErrorMessage(error)
|
|
3220
|
-
});
|
|
3221
|
-
}
|
|
3222
|
-
};
|
|
3223
|
-
const codexNativeSubagentMonitorRuntime = {
|
|
3224
|
-
Monitor,
|
|
3225
|
-
register: registerMonitor,
|
|
3226
|
-
retireParent: (client, parentThreadId) => {
|
|
3227
|
-
monitors.get(client)?.retireParent(parentThreadId);
|
|
3228
|
-
}
|
|
3229
|
-
};
|
|
3230
|
-
function readThreadTurnRecovery(thread, childThreadId) {
|
|
3231
|
-
const turns = Array.isArray(thread.turns) ? thread.turns : [];
|
|
3232
|
-
for (let index = turns.length - 1; index >= 0; index -= 1) {
|
|
3233
|
-
const turn = turns[index];
|
|
3234
|
-
if (!isJsonObject(turn)) continue;
|
|
3235
|
-
const status = normalizeIdentifier(readStringField(turn, "status"));
|
|
3236
|
-
return {
|
|
3237
|
-
completion: readTurnCompletion(turn, childThreadId),
|
|
3238
|
-
resumable: status === "interrupted"
|
|
3239
|
-
};
|
|
3240
|
-
}
|
|
3241
|
-
return { resumable: false };
|
|
3242
|
-
}
|
|
3243
|
-
function toChildTurnCompletion(childState, turn) {
|
|
3244
|
-
const status = normalizeIdentifier(readStringField(turn, "status"));
|
|
3245
|
-
if (status === "completed") {
|
|
3246
|
-
const turnId = readStringField(turn, "id");
|
|
3247
|
-
const result = turnId ? lastChildAssistantMessage(childState, turnId) : void 0;
|
|
3248
|
-
return {
|
|
3249
|
-
childThreadId: childState.childThreadId,
|
|
3250
|
-
status: "succeeded",
|
|
3251
|
-
statusLabel: result ? "turn_completed" : "completed_without_final_message",
|
|
3252
|
-
result: result ?? "Codex native subagent completed without a final assistant message."
|
|
3253
|
-
};
|
|
3254
|
-
}
|
|
3255
|
-
if (status === "failed") return {
|
|
3256
|
-
childThreadId: childState.childThreadId,
|
|
3257
|
-
status: "failed",
|
|
3258
|
-
statusLabel: "turn_failed",
|
|
3259
|
-
result: readTurnErrorMessage(turn) ?? "Codex native subagent failed."
|
|
3260
|
-
};
|
|
3261
|
-
}
|
|
3262
|
-
function lastChildAssistantMessage(childState, turnId) {
|
|
3263
|
-
const messages = childState.assistantMessagesByTurn.get(turnId);
|
|
3264
|
-
if (!messages) return;
|
|
3265
|
-
for (const itemId of messages.order.toReversed()) if (messages.finalMessageIds.has(itemId) && !messages.commentaryIds.has(itemId)) {
|
|
3266
|
-
const text = normalizeOptionalString(messages.texts.get(itemId));
|
|
3267
|
-
if (text) return text;
|
|
3268
|
-
}
|
|
3269
|
-
}
|
|
3270
|
-
function readTurnErrorMessage(turn) {
|
|
3271
|
-
const error = isJsonObject(turn.error) ? turn.error : void 0;
|
|
3272
|
-
return normalizeOptionalString(readStringField(error, "message")) ?? normalizeOptionalString(isJsonObject(error?.codexErrorInfo) ? readStringField(error.codexErrorInfo, "message") : void 0);
|
|
3273
|
-
}
|
|
3274
|
-
function systemErrorFallbackCompletion(childThreadId) {
|
|
3275
|
-
return {
|
|
3276
|
-
childThreadId,
|
|
3277
|
-
status: "failed",
|
|
3278
|
-
statusLabel: "system_error",
|
|
3279
|
-
result: "Codex app-server reported a system error for the native subagent thread."
|
|
3280
|
-
};
|
|
3281
|
-
}
|
|
3282
|
-
function readTurnCompletion(turn, childThreadId) {
|
|
3283
|
-
const status = normalizeIdentifier(readStringField(turn, "status"));
|
|
3284
|
-
if (status === "inprogress" || !status) return;
|
|
3285
|
-
const result = readLastAgentMessage(turn);
|
|
3286
|
-
const completedAtSeconds = asFiniteNumber(turn.completedAt);
|
|
3287
|
-
const completedAt = completedAtSeconds === void 0 ? void 0 : Math.round(completedAtSeconds * 1e3);
|
|
3288
|
-
if (status === "completed") return {
|
|
3289
|
-
childThreadId,
|
|
3290
|
-
status: "succeeded",
|
|
3291
|
-
statusLabel: result ? "task_complete" : "completed_without_final_message",
|
|
3292
|
-
result: result ?? "Codex native subagent completed without a final assistant message.",
|
|
3293
|
-
completedAt
|
|
3294
|
-
};
|
|
3295
|
-
if (status === "interrupted") return;
|
|
3296
|
-
if (status === "failed") return {
|
|
3297
|
-
childThreadId,
|
|
3298
|
-
status: "failed",
|
|
3299
|
-
statusLabel: "task_failed",
|
|
3300
|
-
result: readTurnErrorMessage(turn) ?? result ?? "Codex native subagent failed.",
|
|
3301
|
-
completedAt
|
|
3302
|
-
};
|
|
3303
|
-
}
|
|
3304
|
-
function readLastAgentMessage(turn) {
|
|
3305
|
-
const items = Array.isArray(turn.items) ? turn.items : [];
|
|
3306
|
-
let legacyResult;
|
|
3307
|
-
for (let index = items.length - 1; index >= 0; index -= 1) {
|
|
3308
|
-
const item = items[index];
|
|
3309
|
-
if (!isJsonObject(item)) continue;
|
|
3310
|
-
if (normalizeIdentifier(readStringField(item, "type")) !== "agentmessage") continue;
|
|
3311
|
-
const text = readStringField(item, "text")?.trim();
|
|
3312
|
-
if (!text) continue;
|
|
3313
|
-
const phase = normalizeIdentifier(readStringField(item, "phase"));
|
|
3314
|
-
if (phase === "finalanswer") return text;
|
|
3315
|
-
if (!phase) legacyResult ??= text;
|
|
3316
|
-
}
|
|
3317
|
-
return legacyResult;
|
|
3318
|
-
}
|
|
3319
|
-
function buildParentAgentPathKey(parentThreadId, agentPath) {
|
|
3320
|
-
return `${parentThreadId}\0${agentPath}`;
|
|
3321
|
-
}
|
|
3322
|
-
function isNoFinalCompletion(completion) {
|
|
3323
|
-
return completion.status === "succeeded" && completion.statusLabel === "completed_without_final_message";
|
|
3324
|
-
}
|
|
3325
|
-
function delayForAttempt(delays, attempt) {
|
|
3326
|
-
return Math.max(1, delays[Math.min(attempt, delays.length - 1)] ?? 1);
|
|
3327
|
-
}
|
|
3328
|
-
function readThreadParentThreadId(thread) {
|
|
3329
|
-
return readStringField(thread, "parentThreadId")?.trim() ?? readStringField(readThreadSpawnSource(thread), "parent_thread_id")?.trim();
|
|
3330
|
-
}
|
|
3331
|
-
function readThreadSpawnSource(thread) {
|
|
3332
|
-
const source = isJsonObject(thread?.source) ? thread.source : void 0;
|
|
3333
|
-
const subAgent = isJsonObject(source?.subAgent) ? source.subAgent : void 0;
|
|
3334
|
-
return isJsonObject(subAgent?.thread_spawn) ? subAgent.thread_spawn : void 0;
|
|
3335
|
-
}
|
|
3336
|
-
function readStringArray(value) {
|
|
3337
|
-
if (!Array.isArray(value)) return [];
|
|
3338
|
-
return value.filter((entry) => typeof entry === "string" && entry.trim() !== "");
|
|
3339
|
-
}
|
|
3340
|
-
function readObjectStringKeys(value) {
|
|
3341
|
-
return isJsonObject(value) ? Object.keys(value).filter((entry) => entry.trim() !== "") : [];
|
|
3342
|
-
}
|
|
3343
|
-
function normalizeIdentifier(value) {
|
|
3344
|
-
return value?.replace(/[^a-z0-9]/giu, "").toLowerCase();
|
|
3345
|
-
}
|
|
3346
|
-
function unrefTimer(timer) {
|
|
3347
|
-
if (typeof timer === "object" && timer && "unref" in timer) timer.unref();
|
|
3348
|
-
}
|
|
3349
|
-
//#endregion
|
|
3350
|
-
export { CodexToolProgressProjection as a, isCodexCommandBearingToolCall as c, sanitizeCodexToolResponse as d, CodexToolTranscriptProjection as i, resolveCodexToolProgressDetailMode as l, CodexEventProjection as n, shouldEmitTranscriptToolProgress as o, emitCodexAgentEvent as r, inferCodexDynamicToolMeta as s, codexNativeSubagentMonitorRuntime as t, sanitizeCodexToolArguments as u };
|