@workerdeck/core 0.17.0 → 0.18.0
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/build/index.d.mts +38 -2
- package/build/index.mjs +430 -59
- package/build/index.mjs.map +1 -1
- package/package.json +3 -3
package/build/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { createHash, randomUUID } from "node:crypto";
|
|
3
3
|
import { getSessionInfo, getSessionMessages, listSessions, query } from "@anthropic-ai/claude-agent-sdk";
|
|
4
|
-
import { ENGINE_CAPABILITIES, PROTOCOL_VERSION, SUBAGENT_HISTORY, TOOL_RESULT_HEAD_CHARS, imagePartRef, replayCoalesceKey, replayRetains, snapshotRetains, transcriptActivity, transcriptContent } from "@workerdeck/protocol";
|
|
4
|
+
import { ENGINE_CAPABILITIES, PROTOCOL_VERSION, SUBAGENT_HISTORY, TOOL_RESULT_HEAD_CHARS, contextReading, imagePartRef, replayCoalesceKey, replayRetains, snapshotRetains, transcriptActivity, transcriptContent } from "@workerdeck/protocol";
|
|
5
5
|
import { ToolLoopAgent, generateText, isStepCount, tool } from "ai";
|
|
6
6
|
import { execFile, spawn } from "node:child_process";
|
|
7
|
-
import { existsSync, mkdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { appendFileSync, existsSync, mkdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { createVfs, runScript } from "@workerdeck/sandbox";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { lookup } from "node:dns/promises";
|
|
@@ -1100,6 +1100,13 @@ var SessionRunner = class {
|
|
|
1100
1100
|
#events = [];
|
|
1101
1101
|
#subscribers = new SubscriberSet();
|
|
1102
1102
|
#seq = 0;
|
|
1103
|
+
/**
|
|
1104
|
+
* Latest context-window reading, retained from the last `context_usage` this
|
|
1105
|
+
* runner emitted so `GET /sessions` can answer it without an attach — see
|
|
1106
|
+
* `SessionInfo.contextUsage`. Folded in the emit path, so it is by
|
|
1107
|
+
* construction the same number the transcript last drew.
|
|
1108
|
+
*/
|
|
1109
|
+
#contextUsage;
|
|
1103
1110
|
#activityCount = 0;
|
|
1104
1111
|
/**
|
|
1105
1112
|
* Seq of the latest `conversation_reset` event, 0 when none. The log itself is
|
|
@@ -1194,6 +1201,7 @@ var SessionRunner = class {
|
|
|
1194
1201
|
createdAt: this.createdAt,
|
|
1195
1202
|
lastSeq: this.#seq,
|
|
1196
1203
|
activityCount: this.#activityCount,
|
|
1204
|
+
contextUsage: this.#contextUsage,
|
|
1197
1205
|
pendingPermissionCount: this.#pending.size,
|
|
1198
1206
|
subagents: this.#subagents.list(),
|
|
1199
1207
|
meta: this.#config.meta,
|
|
@@ -1768,7 +1776,11 @@ var SessionRunner = class {
|
|
|
1768
1776
|
};
|
|
1769
1777
|
this.#lastActivityAt = event.ts;
|
|
1770
1778
|
this.#activityCount += transcriptActivity(body);
|
|
1771
|
-
|
|
1779
|
+
this.#contextUsage = contextReading(body) ?? this.#contextUsage;
|
|
1780
|
+
if (body.type === "conversation_reset") {
|
|
1781
|
+
this.#resetSeq = event.seq;
|
|
1782
|
+
this.#contextUsage = void 0;
|
|
1783
|
+
}
|
|
1772
1784
|
this.#subagents.observe(body, event.ts);
|
|
1773
1785
|
this.#events.push(event);
|
|
1774
1786
|
this.#subscribers.emit(event);
|
|
@@ -1816,6 +1828,13 @@ var AiSdkRunner = class {
|
|
|
1816
1828
|
#events = [];
|
|
1817
1829
|
#subscribers = new SubscriberSet();
|
|
1818
1830
|
#seq = 0;
|
|
1831
|
+
/**
|
|
1832
|
+
* Latest context-window reading, retained from the last `context_usage` this
|
|
1833
|
+
* runner emitted so `GET /sessions` can answer it without an attach — see
|
|
1834
|
+
* `SessionInfo.contextUsage`. Folded in the emit path, so it is by
|
|
1835
|
+
* construction the same number the transcript last drew.
|
|
1836
|
+
*/
|
|
1837
|
+
#contextUsage;
|
|
1819
1838
|
#activityCount = 0;
|
|
1820
1839
|
#status = "starting";
|
|
1821
1840
|
#permissionMode;
|
|
@@ -1865,7 +1884,12 @@ var AiSdkRunner = class {
|
|
|
1865
1884
|
if (!state || !Array.isArray(state.messages)) throw new Error("session snapshot is missing its provider-engine state");
|
|
1866
1885
|
this.#seq = snapshot.seq;
|
|
1867
1886
|
this.#events = [...snapshot.events];
|
|
1868
|
-
this.#activityCount =
|
|
1887
|
+
this.#activityCount = 0;
|
|
1888
|
+
for (const event of this.#events) {
|
|
1889
|
+
this.#activityCount += transcriptActivity(event);
|
|
1890
|
+
if (event.type === "conversation_reset") this.#contextUsage = void 0;
|
|
1891
|
+
else this.#contextUsage = contextReading(event) ?? this.#contextUsage;
|
|
1892
|
+
}
|
|
1869
1893
|
this.#messages = [...state.messages];
|
|
1870
1894
|
for (const call of state.pendingToolCalls) this.#pendingToolCalls.set(call.toolCallId, call);
|
|
1871
1895
|
this.#dispatched = new Set(state.dispatched);
|
|
@@ -1916,6 +1940,7 @@ var AiSdkRunner = class {
|
|
|
1916
1940
|
createdAt: this.createdAt,
|
|
1917
1941
|
lastSeq: this.#seq,
|
|
1918
1942
|
activityCount: this.#activityCount,
|
|
1943
|
+
contextUsage: this.#contextUsage,
|
|
1919
1944
|
pendingPermissionCount: 0,
|
|
1920
1945
|
meta: this.#config.meta,
|
|
1921
1946
|
scope: this.#config.scope,
|
|
@@ -2602,6 +2627,8 @@ var AiSdkRunner = class {
|
|
|
2602
2627
|
};
|
|
2603
2628
|
this.#lastActivityAt = event.ts;
|
|
2604
2629
|
this.#activityCount += transcriptActivity(body);
|
|
2630
|
+
this.#contextUsage = contextReading(body) ?? this.#contextUsage;
|
|
2631
|
+
if (body.type === "conversation_reset") this.#contextUsage = void 0;
|
|
2605
2632
|
this.#events.push(event);
|
|
2606
2633
|
this.#subscribers.emit(event);
|
|
2607
2634
|
}
|
|
@@ -3822,8 +3849,6 @@ const claudeAdapter = {
|
|
|
3822
3849
|
}));
|
|
3823
3850
|
}
|
|
3824
3851
|
};
|
|
3825
|
-
//#endregion
|
|
3826
|
-
//#region src/engines/codex/jsonrpc.ts
|
|
3827
3852
|
/**
|
|
3828
3853
|
* A JSON-RPC error response from the peer, or one we return to it. `code`
|
|
3829
3854
|
* follows the JSON-RPC 2.0 reserved ranges (-32601 = method not found).
|
|
@@ -3855,9 +3880,12 @@ var JsonRpcStdioConnection = class {
|
|
|
3855
3880
|
#buffer = "";
|
|
3856
3881
|
#closed = false;
|
|
3857
3882
|
#notificationHandler;
|
|
3883
|
+
/** Where {@link CODEX_TRACE_ENV} pointed, or undefined — read once. */
|
|
3884
|
+
#trace;
|
|
3858
3885
|
#requestHandler;
|
|
3859
3886
|
constructor(options) {
|
|
3860
3887
|
this.#output = options.output;
|
|
3888
|
+
this.#trace = process.env["WORKERDECK_CODEX_TRACE"] || void 0;
|
|
3861
3889
|
options.input.on("data", (chunk) => this.#feed(String(chunk)));
|
|
3862
3890
|
options.input.on("error", () => {});
|
|
3863
3891
|
options.output.on("error", () => {});
|
|
@@ -3918,9 +3946,28 @@ var JsonRpcStdioConnection = class {
|
|
|
3918
3946
|
} catch {
|
|
3919
3947
|
continue;
|
|
3920
3948
|
}
|
|
3949
|
+
this.#traceLine(message);
|
|
3921
3950
|
this.#dispatch(message);
|
|
3922
3951
|
}
|
|
3923
3952
|
}
|
|
3953
|
+
/**
|
|
3954
|
+
* Append one inbound message to the trace file, when the operator asked for
|
|
3955
|
+
* one. **Notifications and server→client requests only** — a response body is
|
|
3956
|
+
* not needed to answer the questions this exists for, and `account/*` results
|
|
3957
|
+
* are the one place app-server traffic can carry a masked credential
|
|
3958
|
+
* fragment, which nothing of ours writes to disk (see the auth red lines).
|
|
3959
|
+
* Best-effort and synchronous: a debug sink that loses lines proves nothing,
|
|
3960
|
+
* and a debug sink that throws must not take the session with it.
|
|
3961
|
+
*/
|
|
3962
|
+
#traceLine(message) {
|
|
3963
|
+
if (!this.#trace) return;
|
|
3964
|
+
const method = message.method;
|
|
3965
|
+
if (typeof method !== "string") return;
|
|
3966
|
+
if (method.startsWith("account/") || method.startsWith("login")) return;
|
|
3967
|
+
try {
|
|
3968
|
+
appendFileSync(this.#trace, JSON.stringify(message) + "\n");
|
|
3969
|
+
} catch {}
|
|
3970
|
+
}
|
|
3924
3971
|
#dispatch(message) {
|
|
3925
3972
|
const { id, method } = message;
|
|
3926
3973
|
if (typeof method === "string") {
|
|
@@ -3959,6 +4006,117 @@ var JsonRpcStdioConnection = class {
|
|
|
3959
4006
|
}
|
|
3960
4007
|
};
|
|
3961
4008
|
//#endregion
|
|
4009
|
+
//#region src/engines/codex/subagents.ts
|
|
4010
|
+
/**
|
|
4011
|
+
* The codex side of `SessionInfo.subagents` — and the attribution table that
|
|
4012
|
+
* gives every event a spawned agent produces its `parentToolUseId`.
|
|
4013
|
+
*
|
|
4014
|
+
* Codex's signal is stronger than the claude engine's, so this is deliberately
|
|
4015
|
+
* NOT that tracker generalised (`engines/claude/subagents.ts` infers spawns
|
|
4016
|
+
* from tool names and verdicts from result-text sniffing, ~290 lines of module
|
|
4017
|
+
* doc explaining the inference). Here nothing is inferred: `subAgentActivity
|
|
4018
|
+
* {kind: 'started'}` on the owning thread positively announces an agent, names
|
|
4019
|
+
* it (`agentPath`), keys it (`agentThreadId` — the id every one of its later
|
|
4020
|
+
* notifications carries) and hands over the model's own `spawn_agent` call id;
|
|
4021
|
+
* the agent's end is its own thread's `turn/completed`, status included. So a
|
|
4022
|
+
* record is keyed by **thread id** — the wire's handle — while exposing a
|
|
4023
|
+
* **tool-use id** — the protocol's: `parentToolUseId` on nested events must
|
|
4024
|
+
* equal the anchor `tool_use`'s id for `subagentItems` (the frame membership
|
|
4025
|
+
* rule every client shares) to reassemble the sidechain, and this map is where
|
|
4026
|
+
* the two vocabularies meet.
|
|
4027
|
+
*
|
|
4028
|
+
* Two decisions worth their prose:
|
|
4029
|
+
*
|
|
4030
|
+
* **A record survives the runner's turns.** Codex agents are designed to
|
|
4031
|
+
* outlive the root turn that spawned them (`sendInput`/`resumeAgent` address a
|
|
4032
|
+
* thread that kept existing), so — unlike a pending approval — nothing here is
|
|
4033
|
+
* swept when a root turn ends. What does end every agent is the app-server
|
|
4034
|
+
* process itself: the runner calls {@link sweep} when the child dies or the
|
|
4035
|
+
* session closes, because an agent whose host process is gone can never report,
|
|
4036
|
+
* and `running` on a closed session would be a lie a polled list re-renders
|
|
4037
|
+
* forever (the claude tracker's argument, inherited whole).
|
|
4038
|
+
*
|
|
4039
|
+
* **The settled tail is bounded, running records never are** — the same
|
|
4040
|
+
* {@link SUBAGENT_HISTORY} discipline as the claude tracker, and enforced at
|
|
4041
|
+
* settle time for the same reason: a settle happens once per agent, `list()`
|
|
4042
|
+
* once per row of a 1.2s-polled sessions list.
|
|
4043
|
+
*/
|
|
4044
|
+
var CodexAgentTracker = class {
|
|
4045
|
+
#byThread = /* @__PURE__ */ new Map();
|
|
4046
|
+
#settleCounter = 0;
|
|
4047
|
+
/** The record whose thread this is — the attribution lookup. */
|
|
4048
|
+
get(agentThreadId) {
|
|
4049
|
+
return this.#byThread.get(agentThreadId);
|
|
4050
|
+
}
|
|
4051
|
+
/** Open (or return) the record for a thread. Fill-in, never overwrite: a
|
|
4052
|
+
* label-less fallback record keeps its accumulated count and its already
|
|
4053
|
+
* published toolUseId when the announcing item arrives late. */
|
|
4054
|
+
open(agentThreadId, toolUseId, agentType, ts) {
|
|
4055
|
+
let record = this.#byThread.get(agentThreadId);
|
|
4056
|
+
if (!record) {
|
|
4057
|
+
record = {
|
|
4058
|
+
agentThreadId,
|
|
4059
|
+
toolUseId,
|
|
4060
|
+
status: "running",
|
|
4061
|
+
startedAt: ts,
|
|
4062
|
+
toolCount: 0,
|
|
4063
|
+
counted: /* @__PURE__ */ new Set()
|
|
4064
|
+
};
|
|
4065
|
+
this.#byThread.set(agentThreadId, record);
|
|
4066
|
+
}
|
|
4067
|
+
record.agentType ??= agentType;
|
|
4068
|
+
return record;
|
|
4069
|
+
}
|
|
4070
|
+
/** The agent's thread ran again (`kind: 'interacted'`, or a fresh
|
|
4071
|
+
* `turn/started` on its thread): a settled verdict no longer describes it. */
|
|
4072
|
+
revive(record) {
|
|
4073
|
+
record.status = "running";
|
|
4074
|
+
record.settledOrder = void 0;
|
|
4075
|
+
}
|
|
4076
|
+
#settle(record, status) {
|
|
4077
|
+
record.status = status;
|
|
4078
|
+
record.settledOrder = ++this.#settleCounter;
|
|
4079
|
+
let settled = 0;
|
|
4080
|
+
for (const r of this.#byThread.values()) if (r.settledOrder !== void 0) settled++;
|
|
4081
|
+
while (settled > SUBAGENT_HISTORY) {
|
|
4082
|
+
let oldest;
|
|
4083
|
+
for (const r of this.#byThread.values()) {
|
|
4084
|
+
if (r.settledOrder === void 0) continue;
|
|
4085
|
+
if (!oldest || r.settledOrder < oldest.settledOrder) oldest = r;
|
|
4086
|
+
}
|
|
4087
|
+
if (!oldest) break;
|
|
4088
|
+
this.#byThread.delete(oldest.agentThreadId);
|
|
4089
|
+
settled--;
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
/** A real verdict for one agent — its thread's `turn/completed`, or the
|
|
4093
|
+
* `interrupted` activity edge. */
|
|
4094
|
+
settle(record, status) {
|
|
4095
|
+
if (record.status === status) return;
|
|
4096
|
+
this.#settle(record, status);
|
|
4097
|
+
}
|
|
4098
|
+
/** The process the agents lived in is gone (child death, session close):
|
|
4099
|
+
* everything still running is settled as failed — the report can never come. */
|
|
4100
|
+
sweep() {
|
|
4101
|
+
for (const record of this.#byThread.values()) if (record.status === "running") this.#settle(record, "failed");
|
|
4102
|
+
}
|
|
4103
|
+
/** The rollup as `SessionInfo.subagents` serves it — spawn order, fresh
|
|
4104
|
+
* objects, and `undefined` when there is nothing to say (absent and empty
|
|
4105
|
+
* mean the same thing to a client, and bytes on a polled list are paid for). */
|
|
4106
|
+
list() {
|
|
4107
|
+
if (this.#byThread.size === 0) return void 0;
|
|
4108
|
+
const out = [];
|
|
4109
|
+
for (const r of this.#byThread.values()) out.push({
|
|
4110
|
+
toolUseId: r.toolUseId,
|
|
4111
|
+
agentType: r.agentType,
|
|
4112
|
+
status: r.status,
|
|
4113
|
+
startedAt: r.startedAt,
|
|
4114
|
+
toolCount: r.toolCount
|
|
4115
|
+
});
|
|
4116
|
+
return out;
|
|
4117
|
+
}
|
|
4118
|
+
};
|
|
4119
|
+
//#endregion
|
|
3962
4120
|
//#region src/engines/codex/runner.ts
|
|
3963
4121
|
/**
|
|
3964
4122
|
* thread/start's sandbox axis (string form) — our permission modes as codex
|
|
@@ -3999,16 +4157,27 @@ const GRANULAR_ASK = { granular: {
|
|
|
3999
4157
|
request_permissions: true,
|
|
4000
4158
|
skill_approval: true
|
|
4001
4159
|
} };
|
|
4160
|
+
const GRANULAR_NEVER = { granular: {
|
|
4161
|
+
sandbox_approval: false,
|
|
4162
|
+
rules: false,
|
|
4163
|
+
mcp_elicitations: false,
|
|
4164
|
+
request_permissions: false,
|
|
4165
|
+
skill_approval: false
|
|
4166
|
+
} };
|
|
4167
|
+
/**
|
|
4168
|
+
* Notifications whose meaning is scoped to ONE thread, and which are therefore
|
|
4169
|
+
* only ever read off the session's own. Everything else (items, deltas) is
|
|
4170
|
+
* accepted from any thread on the connection — see `#handleNotification`.
|
|
4171
|
+
*/
|
|
4172
|
+
const THREAD_SCOPED_NOTIFICATIONS = new Set([
|
|
4173
|
+
"turn/started",
|
|
4174
|
+
"turn/completed",
|
|
4175
|
+
"thread/tokenUsage/updated"
|
|
4176
|
+
]);
|
|
4002
4177
|
const APPROVAL_POLICY_BY_MODE = {
|
|
4003
4178
|
default: GRANULAR_ASK,
|
|
4004
4179
|
acceptEdits: GRANULAR_ASK,
|
|
4005
|
-
bypassPermissions:
|
|
4006
|
-
sandbox_approval: false,
|
|
4007
|
-
rules: false,
|
|
4008
|
-
mcp_elicitations: false,
|
|
4009
|
-
request_permissions: false,
|
|
4010
|
-
skill_approval: false
|
|
4011
|
-
} }
|
|
4180
|
+
bypassPermissions: GRANULAR_NEVER
|
|
4012
4181
|
};
|
|
4013
4182
|
/** Fallback timeout for a pending approval nobody answers — the SessionRunner
|
|
4014
4183
|
* default, so unattended codex sessions land the same way Claude ones do. */
|
|
@@ -4019,6 +4188,47 @@ const DEFAULT_APPROVAL_TIMEOUT_MS = 3e5;
|
|
|
4019
4188
|
* host filesystem, an inline preview) off it.
|
|
4020
4189
|
*/
|
|
4021
4190
|
const CODEX_IMAGE_TOOL = "CodexImageGeneration";
|
|
4191
|
+
/**
|
|
4192
|
+
* Tool name for a spawned agent's anchor `tool_use` — the claude engine's
|
|
4193
|
+
* `Task` in this engine's vocabulary. Codex never sends such a call: the model's
|
|
4194
|
+
* `spawn_agent` surfaces only as the `subAgentActivity` marker item, so the
|
|
4195
|
+
* runner authors the call itself, because everything downstream is built on a
|
|
4196
|
+
* top-level `tool_use` existing — `terminalBlocks` absorbs a sidechain into the
|
|
4197
|
+
* call whose id its events carry as `parentToolUseId`, the takeover frames by
|
|
4198
|
+
* it, and `taskIdentity` labels it from the input's `subagent_type`. Not a new
|
|
4199
|
+
* wire idea, just a row: the same shape every other codex tool card uses.
|
|
4200
|
+
*/
|
|
4201
|
+
const CODEX_AGENT_TOOL = "CodexAgent";
|
|
4202
|
+
/** Tool name for the model's collab-agent calls (`wait`, `sendInput`, …), the
|
|
4203
|
+
* `tool` field carried in the input. One name for the whole open axis rather
|
|
4204
|
+
* than a name per verb, so a future verb renders instead of vanishing. */
|
|
4205
|
+
const CODEX_COLLAB_TOOL = "CodexCollab";
|
|
4206
|
+
/** An agent's name is its path's basename: '/root/date_one' → 'date_one'. */
|
|
4207
|
+
function agentName(agentPath) {
|
|
4208
|
+
if (typeof agentPath !== "string") return void 0;
|
|
4209
|
+
return agentPath.split("/").filter(Boolean).at(-1) || void 0;
|
|
4210
|
+
}
|
|
4211
|
+
/** The collab card's input: the verb always, the rich fields only when codex
|
|
4212
|
+
* actually filled them (measured against 0.146.0 they arrive empty — the card
|
|
4213
|
+
* must not render five null columns to say 'wait'). */
|
|
4214
|
+
function collabInput(item) {
|
|
4215
|
+
return {
|
|
4216
|
+
tool: item.tool,
|
|
4217
|
+
...item.receiverThreadIds?.length ? { receiverThreadIds: item.receiverThreadIds } : {},
|
|
4218
|
+
...item.prompt ? { prompt: item.prompt } : {},
|
|
4219
|
+
...item.model ? { model: item.model } : {}
|
|
4220
|
+
};
|
|
4221
|
+
}
|
|
4222
|
+
/** A completed turn's answer, from its summary `items` page — the last
|
|
4223
|
+
* `agentMessage` text. For a sub-agent's thread this is the agent's report,
|
|
4224
|
+
* which is exactly what belongs in the anchor's `tool_result`. */
|
|
4225
|
+
function turnReport(turn) {
|
|
4226
|
+
const items = Array.isArray(turn.items) ? turn.items : [];
|
|
4227
|
+
for (let index = items.length - 1; index >= 0; index--) {
|
|
4228
|
+
const item = items[index];
|
|
4229
|
+
if (item?.type === "agentMessage" && typeof item.text === "string" && item.text) return item.text;
|
|
4230
|
+
}
|
|
4231
|
+
}
|
|
4022
4232
|
/** Longest `result` worth putting in a tool card. The field is free-form and
|
|
4023
4233
|
* undocumented; anything past this is assumed to be an encoded image rather
|
|
4024
4234
|
* than a sentence, and encoded images do not go in the event log. */
|
|
@@ -4362,6 +4572,13 @@ var CodexRunner = class {
|
|
|
4362
4572
|
#events = [];
|
|
4363
4573
|
#subscribers = new SubscriberSet();
|
|
4364
4574
|
#seq = 0;
|
|
4575
|
+
/**
|
|
4576
|
+
* Latest context-window reading, retained from the last `context_usage` this
|
|
4577
|
+
* runner emitted so `GET /sessions` can answer it without an attach — see
|
|
4578
|
+
* `SessionInfo.contextUsage`. Folded in the emit path, so it is by
|
|
4579
|
+
* construction the same number the transcript last drew.
|
|
4580
|
+
*/
|
|
4581
|
+
#contextUsage;
|
|
4365
4582
|
#activityCount = 0;
|
|
4366
4583
|
#status = "starting";
|
|
4367
4584
|
#sdkSessionId;
|
|
@@ -4417,6 +4634,12 @@ var CodexRunner = class {
|
|
|
4417
4634
|
* `mcpServerStatus/list` does not carry a status field at all, so without
|
|
4418
4635
|
* this every server would read as "configured" and never as up or down. */
|
|
4419
4636
|
#mcpStatus = /* @__PURE__ */ new Map();
|
|
4637
|
+
/** The spawned agents, keyed by their thread ids — the attribution table
|
|
4638
|
+
* behind `parentToolUseId` and the rollup behind `info().subagents`. Runner-
|
|
4639
|
+
* level, not per-turn: an agent's thread outlives the root turn that spawned
|
|
4640
|
+
* it, and only the child process dying (or the session closing) ends them
|
|
4641
|
+
* all — see the module doc in `subagents.ts`. */
|
|
4642
|
+
#agents = new CodexAgentTracker();
|
|
4420
4643
|
constructor(config, id = randomUUID()) {
|
|
4421
4644
|
const mode = config.permissionMode ?? "default";
|
|
4422
4645
|
if (!ENGINE_CAPABILITIES.codex.permissionModes.includes(mode)) throw new Error(`permission mode '${mode}' is not supported by the codex engine`);
|
|
@@ -4468,13 +4691,15 @@ var CodexRunner = class {
|
|
|
4468
4691
|
createdAt: this.createdAt,
|
|
4469
4692
|
lastSeq: this.#seq,
|
|
4470
4693
|
activityCount: this.#activityCount,
|
|
4694
|
+
contextUsage: this.#contextUsage,
|
|
4471
4695
|
pendingPermissionCount: this.#approvals.size,
|
|
4472
4696
|
meta: this.#config.meta,
|
|
4473
4697
|
scope: this.#config.scope,
|
|
4474
4698
|
title: this.#title(),
|
|
4475
4699
|
totalCostUsd: this.#totalCostUsd,
|
|
4476
4700
|
numTurns: this.#numTurns || void 0,
|
|
4477
|
-
lastActivityAt: this.#lastActivityAt
|
|
4701
|
+
lastActivityAt: this.#lastActivityAt,
|
|
4702
|
+
subagents: this.#agents.list()
|
|
4478
4703
|
};
|
|
4479
4704
|
}
|
|
4480
4705
|
#title() {
|
|
@@ -4688,6 +4913,7 @@ var CodexRunner = class {
|
|
|
4688
4913
|
}, "policy");
|
|
4689
4914
|
this.#connection?.close();
|
|
4690
4915
|
this.#connection = void 0;
|
|
4916
|
+
this.#agents.sweep();
|
|
4691
4917
|
this.#activeTurn?.reject(/* @__PURE__ */ new Error("session closed"));
|
|
4692
4918
|
if (this.#imageDir) try {
|
|
4693
4919
|
rmSync(this.#imageDir, {
|
|
@@ -4738,6 +4964,7 @@ var CodexRunner = class {
|
|
|
4738
4964
|
behavior: "deny",
|
|
4739
4965
|
message
|
|
4740
4966
|
}, "policy");
|
|
4967
|
+
this.#agents.sweep();
|
|
4741
4968
|
this.#activeTurn?.reject(new Error(message));
|
|
4742
4969
|
});
|
|
4743
4970
|
try {
|
|
@@ -5033,11 +5260,79 @@ var CodexRunner = class {
|
|
|
5033
5260
|
}
|
|
5034
5261
|
#handleNotification(method, params) {
|
|
5035
5262
|
if (this.#closed) return;
|
|
5263
|
+
if (THREAD_SCOPED_NOTIFICATIONS.has(method) && !this.#isRootThread(params)) {
|
|
5264
|
+
if (method === "turn/completed") this.#settleAgentTurn(params);
|
|
5265
|
+
else if (method === "turn/started") {
|
|
5266
|
+
const threadId = this.#threadIdOf(params);
|
|
5267
|
+
const record = threadId ? this.#agents.get(threadId) : void 0;
|
|
5268
|
+
if (record && record.status !== "running") this.#agents.revive(record);
|
|
5269
|
+
}
|
|
5270
|
+
return;
|
|
5271
|
+
}
|
|
5036
5272
|
this.#notifications[method]?.(params);
|
|
5037
5273
|
}
|
|
5274
|
+
/** Whether a notification is about the session's own thread. A notification
|
|
5275
|
+
* with no `threadId` counts as the root's: every thread-scoped method the
|
|
5276
|
+
* schema defines carries one, so an absent id means an older or narrower
|
|
5277
|
+
* shape, not a sub-agent. */
|
|
5278
|
+
#isRootThread(params) {
|
|
5279
|
+
const threadId = this.#threadIdOf(params);
|
|
5280
|
+
if (threadId === void 0) return true;
|
|
5281
|
+
return threadId === this.#sdkSessionId;
|
|
5282
|
+
}
|
|
5283
|
+
#threadIdOf(params) {
|
|
5284
|
+
const threadId = params?.threadId;
|
|
5285
|
+
return typeof threadId === "string" ? threadId : void 0;
|
|
5286
|
+
}
|
|
5287
|
+
/**
|
|
5288
|
+
* The agent behind a notification's `threadId` — the attribution every item
|
|
5289
|
+
* and delta handler asks before emitting, so two agents streaming
|
|
5290
|
+
* concurrently into this one connection come apart again by the id each
|
|
5291
|
+
* frame carries, never by any mutable "current agent".
|
|
5292
|
+
*
|
|
5293
|
+
* A non-root thread with no record still gets one: a thread emitting items on
|
|
5294
|
+
* this connection *is* an agent, whatever announced it (codex runs threads of
|
|
5295
|
+
* its own for review/compact, and a `subAgentActivity` could in principle be
|
|
5296
|
+
* missed) — the claude tracker's nested-event fallback, on a stronger signal.
|
|
5297
|
+
* The minted record is label-less and its anchor is authored here, because an
|
|
5298
|
+
* attributed event whose parent id matches no top-level `tool_use` would
|
|
5299
|
+
* render inline rather than as a frame; a late `started` edge fills the name
|
|
5300
|
+
* in. Root-thread traffic — and, defensively, the pre-thread shapes with no
|
|
5301
|
+
* id at all — stays unattributed (`undefined`).
|
|
5302
|
+
*/
|
|
5303
|
+
#agentFor(params) {
|
|
5304
|
+
const threadId = this.#threadIdOf(params);
|
|
5305
|
+
if (threadId === void 0 || threadId === this.#sdkSessionId) return void 0;
|
|
5306
|
+
const known = this.#agents.get(threadId);
|
|
5307
|
+
if (known) return known;
|
|
5308
|
+
const nonce = this.#activeTurn?.nonce ?? "codex";
|
|
5309
|
+
const record = this.#agents.open(threadId, `${nonce}:agent:${threadId}`, void 0, Date.now());
|
|
5310
|
+
record.anchored = true;
|
|
5311
|
+
this.#emitToolUse(record.toolUseId, CODEX_AGENT_TOOL, { agentThreadId: threadId });
|
|
5312
|
+
return record;
|
|
5313
|
+
}
|
|
5314
|
+
/**
|
|
5315
|
+
* A child thread's `turn/completed` is that AGENT's completion — the one
|
|
5316
|
+
* codex sends (`subAgentActivity` has no 'completed' kind, verified live).
|
|
5317
|
+
* The verdict is the turn's own status, and the report is the completed
|
|
5318
|
+
* turn's final message, delivered as the anchor's `tool_result` so the row
|
|
5319
|
+
* settles exactly the way a claude `Task`'s does. Deliberately not gated on
|
|
5320
|
+
* `#activeTurn`: an agent finishing between root turns still finished.
|
|
5321
|
+
*/
|
|
5322
|
+
#settleAgentTurn(params) {
|
|
5323
|
+
const threadId = this.#threadIdOf(params);
|
|
5324
|
+
const record = threadId ? this.#agents.get(threadId) : void 0;
|
|
5325
|
+
if (!record || record.status !== "running") return;
|
|
5326
|
+
const turn = params?.turn;
|
|
5327
|
+
const status = turn?.status === "completed" ? "done" : "failed";
|
|
5328
|
+
this.#agents.settle(record, status);
|
|
5329
|
+
const report = (turn ? turnReport(turn) : void 0) ?? turn?.error?.message ?? (status === "done" ? "" : turn?.status ?? "failed");
|
|
5330
|
+
this.#emitToolResult(record.toolUseId, report, status === "failed");
|
|
5331
|
+
}
|
|
5038
5332
|
/** Reasoning deltas arrive on two methods that differ only in which section
|
|
5039
5333
|
* counter they advance; the section key carries the method so the two streams
|
|
5040
|
-
* never share a boundary
|
|
5334
|
+
* never share a boundary (and item ids are per-thread, so two agents' streams
|
|
5335
|
+
* never share one either). Section boundaries (a new summary/content entry)
|
|
5041
5336
|
* render as paragraph breaks — the completed item joins sections with '\n\n'. */
|
|
5042
5337
|
#reasoningDelta(method) {
|
|
5043
5338
|
return (params) => {
|
|
@@ -5053,7 +5348,7 @@ var CodexRunner = class {
|
|
|
5053
5348
|
this.#emitDelta({
|
|
5054
5349
|
type: "thinking_delta",
|
|
5055
5350
|
thinking: separator + payload.delta
|
|
5056
|
-
});
|
|
5351
|
+
}, this.#agentFor(params)?.toolUseId ?? null);
|
|
5057
5352
|
};
|
|
5058
5353
|
}
|
|
5059
5354
|
/** One item-progress handler serves `item/started` and `item/updated`. */
|
|
@@ -5061,7 +5356,7 @@ var CodexRunner = class {
|
|
|
5061
5356
|
const active = this.#activeTurn;
|
|
5062
5357
|
if (!active) return;
|
|
5063
5358
|
const item = params?.item;
|
|
5064
|
-
if (item) this.#handleItemProgress(item, active);
|
|
5359
|
+
if (item) this.#handleItemProgress(item, active, this.#agentFor(params));
|
|
5065
5360
|
};
|
|
5066
5361
|
/** The notification dispatch table — every method the child emits that this
|
|
5067
5362
|
* runner maps, in one place. Handlers read `this.#activeTurn` themselves:
|
|
@@ -5079,7 +5374,9 @@ var CodexRunner = class {
|
|
|
5079
5374
|
"turn/completed": (params) => {
|
|
5080
5375
|
const active = this.#activeTurn;
|
|
5081
5376
|
const turn = params?.turn;
|
|
5082
|
-
if (active
|
|
5377
|
+
if (!active || !turn) return;
|
|
5378
|
+
if (active.turnId && turn.id && turn.id !== active.turnId) return;
|
|
5379
|
+
active.resolve(turn);
|
|
5083
5380
|
},
|
|
5084
5381
|
"item/started": this.#itemProgress,
|
|
5085
5382
|
"item/updated": this.#itemProgress,
|
|
@@ -5087,7 +5384,7 @@ var CodexRunner = class {
|
|
|
5087
5384
|
const active = this.#activeTurn;
|
|
5088
5385
|
if (!active) return;
|
|
5089
5386
|
const item = params?.item;
|
|
5090
|
-
if (item) this.#handleItemCompleted(item, active);
|
|
5387
|
+
if (item) this.#handleItemCompleted(item, active, this.#agentFor(params));
|
|
5091
5388
|
},
|
|
5092
5389
|
"item/agentMessage/delta": (params) => {
|
|
5093
5390
|
if (!this.#activeTurn) return;
|
|
@@ -5095,7 +5392,7 @@ var CodexRunner = class {
|
|
|
5095
5392
|
if (typeof delta === "string" && delta) this.#emitDelta({
|
|
5096
5393
|
type: "text_delta",
|
|
5097
5394
|
text: delta
|
|
5098
|
-
});
|
|
5395
|
+
}, this.#agentFor(params)?.toolUseId ?? null);
|
|
5099
5396
|
},
|
|
5100
5397
|
"item/reasoning/textDelta": this.#reasoningDelta("item/reasoning/textDelta"),
|
|
5101
5398
|
"item/reasoning/summaryTextDelta": this.#reasoningDelta("item/reasoning/summaryTextDelta"),
|
|
@@ -5288,30 +5585,40 @@ var CodexRunner = class {
|
|
|
5288
5585
|
if (!this.#closed && this.#approvals.size === 0 && this.#status === "awaiting_approval") this.#setStatus("running");
|
|
5289
5586
|
}
|
|
5290
5587
|
/** Tool calls surface as tool_use when they start; text and reasoning stream
|
|
5291
|
-
* natively via the delta notifications.
|
|
5292
|
-
|
|
5588
|
+
* natively via the delta notifications. `agent` is the sub-agent whose thread
|
|
5589
|
+
* the item arrived on — undefined for the session's own. */
|
|
5590
|
+
#handleItemProgress(item, active, agent) {
|
|
5293
5591
|
const id = `${active.nonce}:${item.id}`;
|
|
5592
|
+
if (item.type === "subAgentActivity") {
|
|
5593
|
+
this.#itemCompleted.subAgentActivity(item, active, id, agent);
|
|
5594
|
+
return;
|
|
5595
|
+
}
|
|
5294
5596
|
if (item.type === "commandExecution" && !active.toolUseEmitted.has(id)) {
|
|
5295
5597
|
active.toolUseEmitted.add(id);
|
|
5296
|
-
this.#emitToolUse(id, "CodexCommand", { command: item.command });
|
|
5598
|
+
this.#emitToolUse(id, "CodexCommand", { command: item.command }, agent);
|
|
5297
5599
|
return;
|
|
5298
5600
|
}
|
|
5299
5601
|
if (item.type === "mcpToolCall" && !active.toolUseEmitted.has(id)) {
|
|
5300
5602
|
active.toolUseEmitted.add(id);
|
|
5301
|
-
this.#emitToolUse(id, `mcp__${item.server}__${item.tool}`, item.arguments);
|
|
5603
|
+
this.#emitToolUse(id, `mcp__${item.server}__${item.tool}`, item.arguments, agent);
|
|
5604
|
+
return;
|
|
5605
|
+
}
|
|
5606
|
+
if (item.type === "collabAgentToolCall" && !active.toolUseEmitted.has(id)) {
|
|
5607
|
+
active.toolUseEmitted.add(id);
|
|
5608
|
+
this.#emitToolUse(id, CODEX_COLLAB_TOOL, collabInput(item), agent);
|
|
5302
5609
|
return;
|
|
5303
5610
|
}
|
|
5304
5611
|
if (item.type === "imageGeneration" && !active.toolUseEmitted.has(id)) {
|
|
5305
5612
|
active.toolUseEmitted.add(id);
|
|
5306
|
-
this.#emitToolUse(id, CODEX_IMAGE_TOOL, imageGenerationInput(item));
|
|
5613
|
+
this.#emitToolUse(id, CODEX_IMAGE_TOOL, imageGenerationInput(item), agent);
|
|
5307
5614
|
if (item.savedPath) this.#emitFileProduced(item.savedPath, id);
|
|
5308
5615
|
}
|
|
5309
5616
|
}
|
|
5310
|
-
#handleItemCompleted(item, active) {
|
|
5617
|
+
#handleItemCompleted(item, active, agent) {
|
|
5311
5618
|
const id = `${active.nonce}:${item.id}`;
|
|
5312
5619
|
const handler = this.#itemCompleted[item.type];
|
|
5313
5620
|
if (handler) {
|
|
5314
|
-
handler(item, active, id);
|
|
5621
|
+
handler(item, active, id, agent);
|
|
5315
5622
|
return;
|
|
5316
5623
|
}
|
|
5317
5624
|
const unknown = item;
|
|
@@ -5332,67 +5639,121 @@ var CodexRunner = class {
|
|
|
5332
5639
|
* union has never heard of; those take the passthrough above.)
|
|
5333
5640
|
*/
|
|
5334
5641
|
#itemCompleted = {
|
|
5335
|
-
userMessage: () => {
|
|
5336
|
-
|
|
5642
|
+
userMessage: (item, active, _id, agent) => {
|
|
5643
|
+
if (!agent) return;
|
|
5644
|
+
const text = historyUserText(item);
|
|
5645
|
+
if (!text) return;
|
|
5646
|
+
this.#emit({
|
|
5647
|
+
type: "user_message",
|
|
5648
|
+
message: {
|
|
5649
|
+
role: "user",
|
|
5650
|
+
content: text
|
|
5651
|
+
},
|
|
5652
|
+
parentToolUseId: agent.toolUseId,
|
|
5653
|
+
uuid: `${active.nonce}:${item.id}`
|
|
5654
|
+
});
|
|
5655
|
+
},
|
|
5656
|
+
agentMessage: (item, active, id, agent) => {
|
|
5337
5657
|
const text = typeof item.text === "string" ? item.text : "";
|
|
5338
5658
|
this.#emitAssistant(id, [{
|
|
5339
5659
|
type: "text",
|
|
5340
5660
|
text
|
|
5341
|
-
}]);
|
|
5342
|
-
active.finalText = text;
|
|
5661
|
+
}], agent?.toolUseId ?? null);
|
|
5662
|
+
if (!agent) active.finalText = text;
|
|
5343
5663
|
},
|
|
5344
|
-
reasoning: (item, _active, id) => {
|
|
5664
|
+
reasoning: (item, _active, id, agent) => {
|
|
5345
5665
|
const summary = Array.isArray(item.summary) ? item.summary.filter(Boolean) : [];
|
|
5346
5666
|
const content = Array.isArray(item.content) ? item.content.filter(Boolean) : [];
|
|
5347
5667
|
const thinking = (summary.length > 0 ? summary : content).join("\n\n");
|
|
5348
5668
|
if (thinking) this.#emitAssistant(id, [{
|
|
5349
5669
|
type: "thinking",
|
|
5350
5670
|
thinking
|
|
5351
|
-
}]);
|
|
5671
|
+
}], agent?.toolUseId ?? null);
|
|
5352
5672
|
},
|
|
5353
|
-
commandExecution: (item, active, id) => {
|
|
5673
|
+
commandExecution: (item, active, id, agent) => {
|
|
5354
5674
|
if (!active.toolUseEmitted.has(id)) {
|
|
5355
5675
|
active.toolUseEmitted.add(id);
|
|
5356
|
-
this.#emitToolUse(id, "CodexCommand", { command: item.command });
|
|
5676
|
+
this.#emitToolUse(id, "CodexCommand", { command: item.command }, agent);
|
|
5357
5677
|
}
|
|
5358
5678
|
const exitCode = item.exitCode ?? void 0;
|
|
5359
5679
|
const failed = item.status === "failed" || item.status === "declined" || exitCode !== void 0 && exitCode !== 0;
|
|
5360
5680
|
const output = (item.aggregatedOutput ?? "") + (exitCode !== void 0 && exitCode !== 0 ? `\n(exit code ${exitCode})` : "");
|
|
5361
|
-
this.#emitToolResult(id, output, failed);
|
|
5681
|
+
this.#emitToolResult(id, output, failed, void 0, agent?.toolUseId ?? null);
|
|
5362
5682
|
},
|
|
5363
|
-
fileChange: (item, _active, id) => {
|
|
5364
|
-
this.#emitToolUse(id, "CodexFileChange", { changes: item.changes });
|
|
5683
|
+
fileChange: (item, _active, id, agent) => {
|
|
5684
|
+
this.#emitToolUse(id, "CodexFileChange", { changes: item.changes }, agent);
|
|
5365
5685
|
const lines = item.changes.map((change) => {
|
|
5366
5686
|
return `${(typeof change.kind === "string" ? change.kind : change.kind?.type) ?? "change"}: ${change.path}`;
|
|
5367
5687
|
});
|
|
5368
5688
|
const only = item.changes.length === 1 ? item.changes[0] : void 0;
|
|
5369
|
-
this.#emitToolResult(id, lines.join("\n") || item.status, item.status === "failed" || item.status === "declined", only?.diff ? parseUnifiedDiff(only.diff, only.path) : void 0);
|
|
5689
|
+
this.#emitToolResult(id, lines.join("\n") || item.status, item.status === "failed" || item.status === "declined", only?.diff ? parseUnifiedDiff(only.diff, only.path) : void 0, agent?.toolUseId ?? null);
|
|
5370
5690
|
},
|
|
5371
|
-
mcpToolCall: (item, active, id) => {
|
|
5691
|
+
mcpToolCall: (item, active, id, agent) => {
|
|
5372
5692
|
if (!active.toolUseEmitted.has(id)) {
|
|
5373
5693
|
active.toolUseEmitted.add(id);
|
|
5374
|
-
this.#emitToolUse(id, `mcp__${item.server}__${item.tool}`, item.arguments);
|
|
5694
|
+
this.#emitToolUse(id, `mcp__${item.server}__${item.tool}`, item.arguments, agent);
|
|
5375
5695
|
}
|
|
5376
5696
|
const isError = item.error !== void 0 && item.error !== null || item.status === "failed";
|
|
5377
|
-
this.#emitToolResult(id, item.error?.message ?? (item.result === void 0 || item.result === null ? "" : JSON.stringify(item.result)), isError);
|
|
5697
|
+
this.#emitToolResult(id, item.error?.message ?? (item.result === void 0 || item.result === null ? "" : JSON.stringify(item.result)), isError, void 0, agent?.toolUseId ?? null);
|
|
5378
5698
|
},
|
|
5379
|
-
webSearch: (item, _active, id) => {
|
|
5380
|
-
this.#emitToolUse(id, "CodexWebSearch", { query: item.query });
|
|
5381
|
-
this.#emitToolResult(id, "", false);
|
|
5699
|
+
webSearch: (item, _active, id, agent) => {
|
|
5700
|
+
this.#emitToolUse(id, "CodexWebSearch", { query: item.query }, agent);
|
|
5701
|
+
this.#emitToolResult(id, "", false, void 0, agent?.toolUseId ?? null);
|
|
5382
5702
|
},
|
|
5383
|
-
imageGeneration: (item, active, id) => {
|
|
5703
|
+
imageGeneration: (item, active, id, agent) => {
|
|
5384
5704
|
active.toolUseEmitted.add(id);
|
|
5385
|
-
this.#emitToolUse(id, CODEX_IMAGE_TOOL, imageGenerationInput(item));
|
|
5705
|
+
this.#emitToolUse(id, CODEX_IMAGE_TOOL, imageGenerationInput(item), agent);
|
|
5386
5706
|
if (item.savedPath) this.#emitFileProduced(item.savedPath, id);
|
|
5387
5707
|
const lines = [item.savedPath ? `Saved to ${item.savedPath}` : "No saved path reported", ...shortResult(item.result) ? [item.result] : []];
|
|
5388
|
-
this.#emitToolResult(id, lines.join("\n"), item.status === "failed");
|
|
5708
|
+
this.#emitToolResult(id, lines.join("\n"), item.status === "failed", void 0, agent?.toolUseId ?? null);
|
|
5389
5709
|
},
|
|
5390
|
-
imageView: (item, _active, id) => {
|
|
5391
|
-
this.#emitToolUse(id, "CodexImageView", { path: item.path });
|
|
5392
|
-
this.#emitToolResult(id, item.path, false);
|
|
5710
|
+
imageView: (item, _active, id, agent) => {
|
|
5711
|
+
this.#emitToolUse(id, "CodexImageView", { path: item.path }, agent);
|
|
5712
|
+
this.#emitToolResult(id, item.path, false, void 0, agent?.toolUseId ?? null);
|
|
5713
|
+
},
|
|
5714
|
+
subAgentActivity: (item, _active, id, agent) => {
|
|
5715
|
+
if (this.#replayingHistory) {
|
|
5716
|
+
if (item.kind !== "started") return;
|
|
5717
|
+
this.#emitToolUse(id, CODEX_AGENT_TOOL, {
|
|
5718
|
+
...agentName(item.agentPath) ? { subagent_type: agentName(item.agentPath) } : {},
|
|
5719
|
+
agentThreadId: item.agentThreadId,
|
|
5720
|
+
...item.agentPath ? { agentPath: item.agentPath } : {}
|
|
5721
|
+
}, agent);
|
|
5722
|
+
this.#emitToolResult(id, "(ran in its own thread — its work is not part of this thread's stored history)", false, void 0, agent?.toolUseId ?? null);
|
|
5723
|
+
return;
|
|
5724
|
+
}
|
|
5725
|
+
const record = this.#agents.get(item.agentThreadId) ?? this.#agents.open(item.agentThreadId, id, void 0, Date.now());
|
|
5726
|
+
const name = agentName(item.agentPath);
|
|
5727
|
+
const relabel = record.agentType === void 0 && name !== void 0;
|
|
5728
|
+
if (relabel) record.agentType = name;
|
|
5729
|
+
if (!record.anchored || relabel) {
|
|
5730
|
+
record.anchored = true;
|
|
5731
|
+
this.#emitToolUse(record.toolUseId, CODEX_AGENT_TOOL, {
|
|
5732
|
+
...record.agentType ? { subagent_type: record.agentType } : {},
|
|
5733
|
+
agentThreadId: item.agentThreadId,
|
|
5734
|
+
...item.agentPath ? { agentPath: item.agentPath } : {}
|
|
5735
|
+
}, agent);
|
|
5736
|
+
}
|
|
5737
|
+
if (item.kind === "interrupted") {
|
|
5738
|
+
if (record.status === "running") {
|
|
5739
|
+
this.#agents.settle(record, "failed");
|
|
5740
|
+
this.#emitToolResult(record.toolUseId, "interrupted", true);
|
|
5741
|
+
}
|
|
5742
|
+
return;
|
|
5743
|
+
}
|
|
5744
|
+
if (item.kind !== "started" && record.status !== "running") this.#agents.revive(record);
|
|
5745
|
+
},
|
|
5746
|
+
collabAgentToolCall: (item, active, id, agent) => {
|
|
5747
|
+
if (!active.toolUseEmitted.has(id)) {
|
|
5748
|
+
active.toolUseEmitted.add(id);
|
|
5749
|
+
this.#emitToolUse(id, CODEX_COLLAB_TOOL, collabInput(item), agent);
|
|
5750
|
+
}
|
|
5751
|
+
if (item.status === "inProgress") return;
|
|
5752
|
+
const failed = item.status === "failed" || item.status === "declined";
|
|
5753
|
+
this.#emitToolResult(id, failed ? item.status : "", failed, void 0, agent?.toolUseId ?? null);
|
|
5393
5754
|
}
|
|
5394
5755
|
};
|
|
5395
|
-
#emitDelta(delta) {
|
|
5756
|
+
#emitDelta(delta, parent) {
|
|
5396
5757
|
if (this.#config.includePartialMessages === false) return;
|
|
5397
5758
|
this.#emit({
|
|
5398
5759
|
type: "stream_delta",
|
|
@@ -5400,11 +5761,11 @@ var CodexRunner = class {
|
|
|
5400
5761
|
type: "content_block_delta",
|
|
5401
5762
|
delta
|
|
5402
5763
|
},
|
|
5403
|
-
parentToolUseId:
|
|
5764
|
+
parentToolUseId: parent,
|
|
5404
5765
|
uuid: randomUUID()
|
|
5405
5766
|
});
|
|
5406
5767
|
}
|
|
5407
|
-
#emitAssistant(uuid, content) {
|
|
5768
|
+
#emitAssistant(uuid, content, parent) {
|
|
5408
5769
|
this.#emit({
|
|
5409
5770
|
type: "assistant_message",
|
|
5410
5771
|
message: {
|
|
@@ -5412,11 +5773,19 @@ var CodexRunner = class {
|
|
|
5412
5773
|
content,
|
|
5413
5774
|
model: this.#model ?? this.#resolvedModel
|
|
5414
5775
|
},
|
|
5415
|
-
parentToolUseId:
|
|
5776
|
+
parentToolUseId: parent,
|
|
5416
5777
|
uuid
|
|
5417
5778
|
});
|
|
5418
5779
|
}
|
|
5419
|
-
|
|
5780
|
+
/** `agent` (rather than a bare parent id) because a nested call is also the
|
|
5781
|
+
* agent's progress reading: `SubagentInfo.toolCount` ticks here, once per
|
|
5782
|
+
* card — the `counted` set is what keeps an upserted re-emission (the
|
|
5783
|
+
* finished imageGeneration input) from counting one picture twice. */
|
|
5784
|
+
#emitToolUse(id, name, input, agent) {
|
|
5785
|
+
if (agent && !agent.counted.has(id)) {
|
|
5786
|
+
agent.counted.add(id);
|
|
5787
|
+
agent.toolCount += 1;
|
|
5788
|
+
}
|
|
5420
5789
|
this.#emit({
|
|
5421
5790
|
type: "assistant_message",
|
|
5422
5791
|
message: {
|
|
@@ -5429,11 +5798,11 @@ var CodexRunner = class {
|
|
|
5429
5798
|
}],
|
|
5430
5799
|
model: this.#model ?? this.#resolvedModel
|
|
5431
5800
|
},
|
|
5432
|
-
parentToolUseId: null,
|
|
5801
|
+
parentToolUseId: agent?.toolUseId ?? null,
|
|
5433
5802
|
uuid: `${id}-use`
|
|
5434
5803
|
});
|
|
5435
5804
|
}
|
|
5436
|
-
#emitToolResult(toolUseId, content, isError, patch) {
|
|
5805
|
+
#emitToolResult(toolUseId, content, isError, patch, parent = null) {
|
|
5437
5806
|
this.#emit({
|
|
5438
5807
|
type: "user_message",
|
|
5439
5808
|
message: {
|
|
@@ -5445,7 +5814,7 @@ var CodexRunner = class {
|
|
|
5445
5814
|
is_error: isError || void 0
|
|
5446
5815
|
}]
|
|
5447
5816
|
},
|
|
5448
|
-
parentToolUseId:
|
|
5817
|
+
parentToolUseId: parent,
|
|
5449
5818
|
synthetic: true,
|
|
5450
5819
|
patch,
|
|
5451
5820
|
uuid: `${toolUseId}-result`
|
|
@@ -5573,6 +5942,8 @@ var CodexRunner = class {
|
|
|
5573
5942
|
};
|
|
5574
5943
|
this.#lastActivityAt = event.ts;
|
|
5575
5944
|
this.#activityCount += transcriptActivity(body);
|
|
5945
|
+
this.#contextUsage = contextReading(body) ?? this.#contextUsage;
|
|
5946
|
+
if (body.type === "conversation_reset") this.#contextUsage = void 0;
|
|
5576
5947
|
this.#events.push(event);
|
|
5577
5948
|
this.#subscribers.emit(event);
|
|
5578
5949
|
}
|