@ordewell/core 0.10.8 → 0.10.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{IFileSystem-C4_mpfze.d.ts → IFileSystem-1PqRCjhu.d.mts} +23 -3
- package/dist/{IFileSystem-DUgeWWQN.d.mts → IFileSystem-lTRL_G9Z.d.ts} +23 -3
- package/dist/{ModeResolver-4qNjxcDs.d.mts → ModeResolver-6TQXVsde.d.mts} +1 -1
- package/dist/{ModeResolver-DGK2rWHE.d.ts → ModeResolver-CoJAJD-f.d.ts} +1 -1
- package/dist/{Task-C0o4i3Nn.d.mts → Task-CfBM0wkV.d.mts} +100 -24
- package/dist/{Task-C0o4i3Nn.d.ts → Task-CfBM0wkV.d.ts} +100 -24
- package/dist/{chunk-H7QX32BH.mjs → chunk-24EL4Z2W.mjs} +2 -2
- package/dist/{chunk-DMG4ISUG.mjs → chunk-ESKK5I4F.mjs} +145 -21
- package/dist/chunk-ESKK5I4F.mjs.map +1 -0
- package/dist/{chunk-LTHKFBKM.mjs → chunk-KWS3VEER.mjs} +3 -3
- package/dist/{chunk-EEE2GBLG.mjs → chunk-PFH7WFIB.mjs} +1 -1
- package/dist/chunk-PFH7WFIB.mjs.map +1 -0
- package/dist/index.d.mts +145 -20
- package/dist/index.d.ts +145 -20
- package/dist/index.js +668 -112
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +537 -96
- package/dist/index.mjs.map +1 -1
- package/dist/parsing.d.mts +2 -2
- package/dist/parsing.d.ts +2 -2
- package/dist/parsing.js.map +1 -1
- package/dist/parsing.mjs +3 -3
- package/dist/plan-utils.d.mts +4 -4
- package/dist/plan-utils.d.ts +4 -4
- package/dist/plan-utils.js +144 -18
- package/dist/plan-utils.js.map +1 -1
- package/dist/plan-utils.mjs +22 -4
- package/dist/{runSettingsText-BgOmLxdw.d.mts → runSettingsText-C1i9wBG1.d.mts} +61 -21
- package/dist/{runSettingsText-BgXvajj2.d.ts → runSettingsText-CWNrmF5U.d.ts} +61 -21
- package/dist/testing.d.mts +7 -2
- package/dist/testing.d.ts +7 -2
- package/dist/testing.js +11 -0
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +12 -1
- package/dist/testing.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-DMG4ISUG.mjs.map +0 -1
- package/dist/chunk-EEE2GBLG.mjs.map +0 -1
- /package/dist/{chunk-H7QX32BH.mjs.map → chunk-24EL4Z2W.mjs.map} +0 -0
- /package/dist/{chunk-LTHKFBKM.mjs.map → chunk-KWS3VEER.mjs.map} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
looksLikePlanAttempt,
|
|
3
3
|
parsePlanJson,
|
|
4
4
|
validatePlanTasks
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-KWS3VEER.mjs";
|
|
6
6
|
import {
|
|
7
7
|
ACT_RISK,
|
|
8
8
|
AUTORUN_RISK,
|
|
@@ -38,7 +38,10 @@ import {
|
|
|
38
38
|
canSplitTask,
|
|
39
39
|
capConflictFiles,
|
|
40
40
|
classifyOutcome,
|
|
41
|
+
contextMeterText,
|
|
42
|
+
contextMeterTitle,
|
|
41
43
|
continuability,
|
|
44
|
+
costText,
|
|
42
45
|
countedSessionCost,
|
|
43
46
|
dependentsNotice,
|
|
44
47
|
diffRows,
|
|
@@ -49,6 +52,8 @@ import {
|
|
|
49
52
|
executionSummary,
|
|
50
53
|
followTaskLog,
|
|
51
54
|
followTurn,
|
|
55
|
+
formatCost,
|
|
56
|
+
formatTokenCount,
|
|
52
57
|
fromTranscript,
|
|
53
58
|
hasHiddenDetail,
|
|
54
59
|
holdPrompt,
|
|
@@ -59,6 +64,7 @@ import {
|
|
|
59
64
|
markRequestFor,
|
|
60
65
|
markdownHtml,
|
|
61
66
|
markdownTokens,
|
|
67
|
+
modelCostTitle,
|
|
62
68
|
namedRepos,
|
|
63
69
|
newTaskFields,
|
|
64
70
|
normalizeAgentArgs,
|
|
@@ -90,6 +96,7 @@ import {
|
|
|
90
96
|
skippedLabel,
|
|
91
97
|
startAttemptUsage,
|
|
92
98
|
stopTurn,
|
|
99
|
+
subagentCostTitle,
|
|
93
100
|
summarizeToolCall,
|
|
94
101
|
supervisorSummary,
|
|
95
102
|
surfacePlan,
|
|
@@ -103,6 +110,7 @@ import {
|
|
|
103
110
|
taskStartedNotice,
|
|
104
111
|
taskStatusKind,
|
|
105
112
|
taskUsageSummary,
|
|
113
|
+
tasksTotalText,
|
|
106
114
|
textHasTaskOps,
|
|
107
115
|
titledRefs,
|
|
108
116
|
toolHeadline,
|
|
@@ -110,8 +118,9 @@ import {
|
|
|
110
118
|
unsendAll,
|
|
111
119
|
unsendLatest,
|
|
112
120
|
usageLine,
|
|
121
|
+
usageText,
|
|
113
122
|
validateTaskEdit
|
|
114
|
-
} from "./chunk-
|
|
123
|
+
} from "./chunk-ESKK5I4F.mjs";
|
|
115
124
|
import {
|
|
116
125
|
PLAN_ENVELOPE_KEY,
|
|
117
126
|
PlanParseError,
|
|
@@ -137,7 +146,7 @@ import {
|
|
|
137
146
|
runnerModesFrom,
|
|
138
147
|
stripTrailingCommas,
|
|
139
148
|
unavailableTaskModeMessage
|
|
140
|
-
} from "./chunk-
|
|
149
|
+
} from "./chunk-24EL4Z2W.mjs";
|
|
141
150
|
import {
|
|
142
151
|
SELF_REPO,
|
|
143
152
|
absorbRemoval,
|
|
@@ -190,7 +199,7 @@ import {
|
|
|
190
199
|
waitsForApproval,
|
|
191
200
|
waitsForReset,
|
|
192
201
|
walkPlan
|
|
193
|
-
} from "./chunk-
|
|
202
|
+
} from "./chunk-PFH7WFIB.mjs";
|
|
194
203
|
|
|
195
204
|
// src/models/TaskLog.ts
|
|
196
205
|
var HEAD_LINES = 60;
|
|
@@ -6028,7 +6037,7 @@ var RunnerApprovals = class {
|
|
|
6028
6037
|
},
|
|
6029
6038
|
stop: (sessionId) => {
|
|
6030
6039
|
this.denySession(sessionId);
|
|
6031
|
-
runner.stop(sessionId);
|
|
6040
|
+
return runner.stop(sessionId);
|
|
6032
6041
|
},
|
|
6033
6042
|
stopAll: () => {
|
|
6034
6043
|
this.approvals.clear(isRunnerApproval);
|
|
@@ -9072,6 +9081,8 @@ var RunnerProcess = class {
|
|
|
9072
9081
|
markEnded;
|
|
9073
9082
|
child = null;
|
|
9074
9083
|
exitStatus = null;
|
|
9084
|
+
/** The process itself reported its end, before its pipes drained; see {@link hasExited}. */
|
|
9085
|
+
exitReported = false;
|
|
9075
9086
|
stderrTail = "";
|
|
9076
9087
|
spawnEnv = {};
|
|
9077
9088
|
/** Stdout readers' end-of-stream flushes, for an end the pipe itself never reports. */
|
|
@@ -9122,6 +9133,7 @@ var RunnerProcess = class {
|
|
|
9122
9133
|
});
|
|
9123
9134
|
child.on("close", (code, signal) => this.end({ code, signal }));
|
|
9124
9135
|
child.once("exit", (code, signal) => {
|
|
9136
|
+
this.exitReported = true;
|
|
9125
9137
|
const grace = setTimeout(() => {
|
|
9126
9138
|
for (const flush of [...this.stdoutEnds]) flush();
|
|
9127
9139
|
this.end({ code, signal });
|
|
@@ -9176,6 +9188,15 @@ ${err.message}`);
|
|
|
9176
9188
|
get exit() {
|
|
9177
9189
|
return this.exitStatus;
|
|
9178
9190
|
}
|
|
9191
|
+
/**
|
|
9192
|
+
* Whether the process has ended, for any reason. Unlike {@link exit} this is
|
|
9193
|
+
* true from its own 'exit', not only once 'close' has drained the pipes: a
|
|
9194
|
+
* runner that has exited can take no further turn even while a descendant
|
|
9195
|
+
* holds its stdout or stderr.
|
|
9196
|
+
*/
|
|
9197
|
+
get hasExited() {
|
|
9198
|
+
return this.exitStatus !== null || this.exitReported;
|
|
9199
|
+
}
|
|
9179
9200
|
/** The exit code a listener is told: -1 for a signal or a spawn failure. */
|
|
9180
9201
|
get exitCode() {
|
|
9181
9202
|
return this.exitStatus?.code ?? -1;
|
|
@@ -9374,6 +9395,9 @@ var StdioAgentAdapter = class {
|
|
|
9374
9395
|
nativeSessionId() {
|
|
9375
9396
|
return this.sessionId;
|
|
9376
9397
|
}
|
|
9398
|
+
get exited() {
|
|
9399
|
+
return this.runner.hasExited;
|
|
9400
|
+
}
|
|
9377
9401
|
onProcessExit(listener) {
|
|
9378
9402
|
const fire = () => listener(this.runner.exitCode, this.exitMessage());
|
|
9379
9403
|
if (this.runner.exit) queueMicrotask(fire);
|
|
@@ -9874,6 +9898,14 @@ function canUseToolAnswer({ input, suggestions }, decision) {
|
|
|
9874
9898
|
}
|
|
9875
9899
|
var MODEL_COMMAND = "model";
|
|
9876
9900
|
var SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["Agent", "Task"]);
|
|
9901
|
+
var VARIANT_SUFFIX = /^(.*?)(\[[^\]]*\])?$/;
|
|
9902
|
+
function sameModel(a, b) {
|
|
9903
|
+
if (a === void 0 || b === void 0) return false;
|
|
9904
|
+
if (a === b) return true;
|
|
9905
|
+
const [, aBase, aVariant] = VARIANT_SUFFIX.exec(a) ?? [];
|
|
9906
|
+
const [, bBase, bVariant] = VARIANT_SUFFIX.exec(b) ?? [];
|
|
9907
|
+
return aBase === bBase && (aVariant === void 0 || bVariant === void 0 || aVariant === bVariant);
|
|
9908
|
+
}
|
|
9877
9909
|
function usageRecord(usage, model, subagentId) {
|
|
9878
9910
|
const record = {
|
|
9879
9911
|
source: "claude-code",
|
|
@@ -9882,6 +9914,7 @@ function usageRecord(usage, model, subagentId) {
|
|
|
9882
9914
|
if (model) record.model = model;
|
|
9883
9915
|
if (usage.output_tokens !== void 0) record.outputTokens = usage.output_tokens;
|
|
9884
9916
|
if (subagentId) record.subagentId = subagentId;
|
|
9917
|
+
else if (record.inputTokens !== void 0) record.contextTokens = record.inputTokens + (record.outputTokens ?? 0);
|
|
9885
9918
|
return record;
|
|
9886
9919
|
}
|
|
9887
9920
|
function subagentFinalCall(result) {
|
|
@@ -9931,16 +9964,27 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
9931
9964
|
replyRuns = new ReplyRuns();
|
|
9932
9965
|
/** The model answering the planner's current message, and the message's id, as its `message_start` named them. */
|
|
9933
9966
|
plannerModel;
|
|
9967
|
+
/**
|
|
9968
|
+
* The id the turn runs as, as `init` names it. A `[1m]` variant is the
|
|
9969
|
+
* selected id, while a message names the API's canonical id without it.
|
|
9970
|
+
*/
|
|
9971
|
+
sessionModel;
|
|
9972
|
+
billing;
|
|
9934
9973
|
streamingMessage;
|
|
9935
9974
|
/** The message the last `assistant` line was part of, and how many of its blocks the lines have carried. */
|
|
9936
9975
|
lineMessage = null;
|
|
9937
9976
|
/**
|
|
9938
|
-
*
|
|
9939
|
-
*
|
|
9940
|
-
*
|
|
9977
|
+
* What has been counted of the session's running cost: a resume starts from
|
|
9978
|
+
* it, and it is undefined without a kept count — the next result then only
|
|
9979
|
+
* sets the baseline, since the CLI restores spend from turns before it. It
|
|
9980
|
+
* grows by what each result is counted for, never by a total that includes
|
|
9981
|
+
* cost the runner marked unknown, so that spend can be counted when its basis
|
|
9982
|
+
* is reported; and it never lowers, since counted dollars are not un-spent.
|
|
9941
9983
|
*/
|
|
9942
9984
|
reportedCostUsd = 0;
|
|
9943
9985
|
reportedModelCostUsd = /* @__PURE__ */ new Map();
|
|
9986
|
+
/** What this turn's streamed calls already reported, so its result only adds what did not stream. */
|
|
9987
|
+
turnCallUsage = { inputTokens: 0, cachedInputTokens: 0, outputTokens: 0 };
|
|
9944
9988
|
/**
|
|
9945
9989
|
* Subagents started and not yet finished, keyed by the `Agent` call's id.
|
|
9946
9990
|
* Kept across turns: a backgrounded one reports after its turn has ended.
|
|
@@ -10027,6 +10071,14 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
10027
10071
|
if (!sessionId || this.reportedCostUsd === void 0) return null;
|
|
10028
10072
|
return { sessionId, usd: this.reportedCostUsd, modelUsd: Object.fromEntries(this.reportedModelCostUsd) };
|
|
10029
10073
|
}
|
|
10074
|
+
/**
|
|
10075
|
+
* A cleared conversation's totals start again from zero, so what follows is
|
|
10076
|
+
* all new spend, even for a resume that had no count kept.
|
|
10077
|
+
*/
|
|
10078
|
+
restartCount() {
|
|
10079
|
+
this.reportedCostUsd = 0;
|
|
10080
|
+
this.reportedModelCostUsd = /* @__PURE__ */ new Map();
|
|
10081
|
+
}
|
|
10030
10082
|
/** A count kept for another session says nothing of this one's. */
|
|
10031
10083
|
countFrom(sessionId, counted) {
|
|
10032
10084
|
const known = counted?.sessionId === sessionId ? counted : void 0;
|
|
@@ -10100,11 +10152,10 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
10100
10152
|
* the alias resolves to now; that id is pinned before the first turn.
|
|
10101
10153
|
*/
|
|
10102
10154
|
async handshake(opts) {
|
|
10103
|
-
if (opts.kind !== "task") return;
|
|
10104
10155
|
const initialized = (await this.control({ subtype: "initialize" }, MODEL_PIN_TIMEOUT_MS))?.response;
|
|
10105
10156
|
this.listedCommands = initialized?.commands ?? [];
|
|
10106
10157
|
this.listedModels = initialized?.models ?? [];
|
|
10107
|
-
if (opts.flags.permissionMode !== "plan" || !opts.model) return;
|
|
10158
|
+
if (opts.kind !== "task" || opts.flags.permissionMode !== "plan" || !opts.model) return;
|
|
10108
10159
|
const resolved = initialized?.models?.find((m) => m.value === opts.model)?.resolvedModel;
|
|
10109
10160
|
if (resolved && resolved !== opts.model) await this.control({ subtype: "set_model", model: resolved }, MODEL_PIN_TIMEOUT_MS);
|
|
10110
10161
|
}
|
|
@@ -10145,6 +10196,15 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
10145
10196
|
this.interruptRequested = true;
|
|
10146
10197
|
return (await this.control({ subtype: "interrupt" }, timeoutMs))?.subtype === "success";
|
|
10147
10198
|
}
|
|
10199
|
+
/**
|
|
10200
|
+
* On stdin's end the CLI exits by itself once its turn has closed, and only
|
|
10201
|
+
* an exit of its own saves the session's running totals for a `--resume` to
|
|
10202
|
+
* restore (checked on 2.1.303: about a second later, code 0, or 1 after an
|
|
10203
|
+
* interrupted turn).
|
|
10204
|
+
*/
|
|
10205
|
+
endInput() {
|
|
10206
|
+
this.process?.stdin?.end();
|
|
10207
|
+
}
|
|
10148
10208
|
/**
|
|
10149
10209
|
* A `user` line written mid-turn is Claude Code's own queue (ADR-0023): the
|
|
10150
10210
|
* CLI attaches it to the next tool result, or runs it as a turn of its own
|
|
@@ -10232,6 +10292,7 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
10232
10292
|
this.resultHeld = false;
|
|
10233
10293
|
this.turnClosed = false;
|
|
10234
10294
|
this.clearFollowOnTimer();
|
|
10295
|
+
this.plannerModel = void 0;
|
|
10235
10296
|
}
|
|
10236
10297
|
/**
|
|
10237
10298
|
* The echo of a steered message: the model has it. After a closed turn it
|
|
@@ -10286,6 +10347,8 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
10286
10347
|
this.permissions.withdraw(msg.request_id, emit);
|
|
10287
10348
|
return;
|
|
10288
10349
|
case "control_response": {
|
|
10350
|
+
const account = msg.response?.response?.account;
|
|
10351
|
+
if (account) this.reportBilling(account.apiKeySource, account.subscriptionType, emit);
|
|
10289
10352
|
const requestId = msg.response?.request_id;
|
|
10290
10353
|
if (requestId) this.pendingControl.get(requestId)?.(msg.response ?? null);
|
|
10291
10354
|
return;
|
|
@@ -10329,12 +10392,24 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
10329
10392
|
emit({ type: "tool_result", id, name: "", output: diff || output, success: block.is_error !== true, subagentId });
|
|
10330
10393
|
}
|
|
10331
10394
|
return;
|
|
10395
|
+
// `/clear` (and `/reset`, `/new`) emit this top-level line before their
|
|
10396
|
+
// own result, and a task's chat can send them. The CLI's totals restart.
|
|
10397
|
+
case "conversation_reset": {
|
|
10398
|
+
if (subagentId) return;
|
|
10399
|
+
this.restartCount();
|
|
10400
|
+
const resetModel = this.sessionModel ?? (this.plannerModel !== "<synthetic>" ? this.plannerModel : void 0);
|
|
10401
|
+
emit({ type: "usage", record: { source: "claude-code", ...resetModel ? { model: resetModel } : {}, compacted: true } });
|
|
10402
|
+
return;
|
|
10403
|
+
}
|
|
10332
10404
|
case "system":
|
|
10405
|
+
if (subagentId && (msg.subtype === "init" || msg.subtype === "compact_boundary")) return;
|
|
10333
10406
|
if (msg.subtype === "init") {
|
|
10334
10407
|
this.clearFollowOnTimer();
|
|
10335
10408
|
if (this.refuseOtherMode(msg, emit)) return;
|
|
10409
|
+
if (msg.model && msg.model !== "<synthetic>") this.sessionModel = msg.model;
|
|
10336
10410
|
this.readLoaded(msg, emit);
|
|
10337
10411
|
this.followModel(msg.model, emit);
|
|
10412
|
+
this.reportBilling(msg.apiKeySource, void 0, emit);
|
|
10338
10413
|
}
|
|
10339
10414
|
if (msg.subtype === "commands_changed" && Array.isArray(msg.commands)) this.listedCommands = msg.commands;
|
|
10340
10415
|
if (msg.subtype === "compact_boundary") {
|
|
@@ -10345,6 +10420,8 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
10345
10420
|
...typeof meta?.pre_tokens === "number" ? { preTokens: meta.pre_tokens } : {},
|
|
10346
10421
|
...typeof meta?.post_tokens === "number" ? { postTokens: meta.post_tokens } : {}
|
|
10347
10422
|
});
|
|
10423
|
+
const model = this.sessionModel ?? (this.plannerModel !== "<synthetic>" ? this.plannerModel : void 0);
|
|
10424
|
+
emit({ type: "usage", record: { source: "claude-code", ...model ? { model } : {}, compacted: true, ...typeof meta?.post_tokens === "number" ? { contextTokens: meta.post_tokens } : {} } });
|
|
10348
10425
|
}
|
|
10349
10426
|
if (msg.subtype === "background_tasks_changed" && Array.isArray(msg.tasks)) {
|
|
10350
10427
|
this.backgroundTaskCount = msg.tasks.length;
|
|
@@ -10356,6 +10433,7 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
10356
10433
|
}
|
|
10357
10434
|
return;
|
|
10358
10435
|
case "result":
|
|
10436
|
+
if (subagentId) return;
|
|
10359
10437
|
this.reportSessionCost(msg, emit);
|
|
10360
10438
|
for (const command of this.modelCommands) command.turnsLeft = Math.max(0, command.turnsLeft - 1);
|
|
10361
10439
|
if (this.interruptRequested && (msg.is_error || msg.subtype !== "success")) {
|
|
@@ -10520,16 +10598,69 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
10520
10598
|
*/
|
|
10521
10599
|
handleStreamEvent(event, emit) {
|
|
10522
10600
|
if (event.type === "message_start") {
|
|
10523
|
-
this.plannerModel = event.message?.model;
|
|
10601
|
+
this.plannerModel = this.modelId(event.message?.model);
|
|
10524
10602
|
this.streamingMessage = event.message?.id;
|
|
10525
10603
|
this.followModel(this.plannerModel, emit);
|
|
10526
|
-
} else if (event.type === "message_delta" && event.usage)
|
|
10527
|
-
|
|
10604
|
+
} else if (event.type === "message_delta" && event.usage) {
|
|
10605
|
+
const record = usageRecord(event.usage, this.plannerModel);
|
|
10606
|
+
this.turnCallUsage.inputTokens += record.inputTokens ?? 0;
|
|
10607
|
+
this.turnCallUsage.cachedInputTokens += record.cachedInputTokens ?? 0;
|
|
10608
|
+
this.turnCallUsage.outputTokens += record.outputTokens ?? 0;
|
|
10609
|
+
emit({ type: "usage", record });
|
|
10610
|
+
} else if (event.type === "content_block_delta" && event.delta?.type === "text_delta" && event.delta.text) {
|
|
10528
10611
|
this.replyRuns.delta(`${this.streamingMessage ?? ""}:${event.index ?? 0}`, event.delta.text, emit);
|
|
10529
10612
|
} else if (event.type === "content_block_delta" && event.delta?.type === "thinking_delta" && event.delta.thinking) {
|
|
10530
10613
|
emit({ type: "thinking_delta", text: event.delta.thinking });
|
|
10531
10614
|
}
|
|
10532
10615
|
}
|
|
10616
|
+
/**
|
|
10617
|
+
* The id a reported model runs as. `init` names the selected value — a
|
|
10618
|
+
* `[1m]` variant included — while a message names the API's canonical id
|
|
10619
|
+
* without it. Keeping the variant is what lets the meter find the window
|
|
10620
|
+
* `modelUsage` keys it by, and keeps that window off the base model.
|
|
10621
|
+
*/
|
|
10622
|
+
modelId(reported) {
|
|
10623
|
+
if (!reported) return void 0;
|
|
10624
|
+
if (!sameModel(this.sessionModel, reported)) return reported;
|
|
10625
|
+
return this.sessionModel?.includes("[") ? this.sessionModel : reported;
|
|
10626
|
+
}
|
|
10627
|
+
reportBilling(apiKeySource, plan, emit) {
|
|
10628
|
+
const type = apiKeySource ? apiKeySource === "none" ? "subscription" : "api-key" : plan ? "subscription" : void 0;
|
|
10629
|
+
if (!type) return;
|
|
10630
|
+
const knownPlan = plan ?? (this.billing?.type === type ? this.billing.plan : void 0);
|
|
10631
|
+
this.billing = { type, ...knownPlan ? { plan: knownPlan } : {} };
|
|
10632
|
+
emit({ type: "usage", record: { source: this.agentId, aggregate: true, sessionBilling: this.billing } });
|
|
10633
|
+
}
|
|
10634
|
+
/**
|
|
10635
|
+
* A turn's `result` states its own token sum. Every call that streamed its
|
|
10636
|
+
* `message_delta` usage has already reported, so only the remainder is added:
|
|
10637
|
+
* all of it when nothing streamed (a recording without partial messages), or
|
|
10638
|
+
* a lost call's share. It is a sum across calls, marked `aggregate` so it
|
|
10639
|
+
* never moves the meter — only a call says what the context was left at.
|
|
10640
|
+
*/
|
|
10641
|
+
reportUnstreamedUsage(msg, emit) {
|
|
10642
|
+
const { inputTokens: streamedInput, cachedInputTokens: streamedCached, outputTokens: streamedOutput } = this.turnCallUsage;
|
|
10643
|
+
this.turnCallUsage = { inputTokens: 0, cachedInputTokens: 0, outputTokens: 0 };
|
|
10644
|
+
if (!msg.usage) return;
|
|
10645
|
+
const models = Object.keys(msg.modelUsage ?? {});
|
|
10646
|
+
const model = this.plannerModel ?? this.mainModel ?? (models.length === 1 ? models[0] : void 0);
|
|
10647
|
+
const total = usageRecord(msg.usage, model);
|
|
10648
|
+
const inputTokens = (total.inputTokens ?? 0) - streamedInput;
|
|
10649
|
+
const cachedInputTokens = (total.cachedInputTokens ?? 0) - streamedCached;
|
|
10650
|
+
const outputTokens = (total.outputTokens ?? 0) - streamedOutput;
|
|
10651
|
+
if (inputTokens <= 0 && outputTokens <= 0) return;
|
|
10652
|
+
emit({
|
|
10653
|
+
type: "usage",
|
|
10654
|
+
record: {
|
|
10655
|
+
source: "claude-code",
|
|
10656
|
+
...model ? { model } : {},
|
|
10657
|
+
aggregate: true,
|
|
10658
|
+
...inputTokens > 0 ? { inputTokens } : {},
|
|
10659
|
+
...inputTokens > 0 && cachedInputTokens > 0 ? { cachedInputTokens } : {},
|
|
10660
|
+
...outputTokens > 0 ? { outputTokens } : {}
|
|
10661
|
+
}
|
|
10662
|
+
});
|
|
10663
|
+
}
|
|
10533
10664
|
/**
|
|
10534
10665
|
* What only the result line knows: the cost, which covers every call the
|
|
10535
10666
|
* session made — subagents included, since none of their lines carries one —
|
|
@@ -10538,24 +10669,54 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
10538
10669
|
* the session before; with no count kept, the first turn only sets the
|
|
10539
10670
|
* baseline: its total includes turns counted before, and a turn's own share
|
|
10540
10671
|
* cannot be told apart from them.
|
|
10672
|
+
*
|
|
10673
|
+
* The running total never lowers: money already reported and counted is not
|
|
10674
|
+
* un-spent because a later or resumed process remembers less of it, and
|
|
10675
|
+
* counting its return would count the same dollars twice. A zero is not a
|
|
10676
|
+
* measurement at all — a crash can end with one — and only `/clear` restarts
|
|
10677
|
+
* the count, through {@link restartCount}. Per-model baselines never lower
|
|
10678
|
+
* either, for the same reason.
|
|
10679
|
+
*
|
|
10680
|
+
* A report without a model breakdown is counted as a session total; one with
|
|
10681
|
+
* a breakdown is counted per model. A breakdown that would count more than
|
|
10682
|
+
* the session total's own growth (the count before it had no model split, or
|
|
10683
|
+
* a lossy report) is counted as that growth alone, so the two shapes
|
|
10684
|
+
* together still add each dollar once.
|
|
10541
10685
|
*/
|
|
10542
10686
|
reportSessionCost(msg, emit) {
|
|
10687
|
+
this.reportUnstreamedUsage(msg, emit);
|
|
10543
10688
|
const total = msg.total_cost_usd;
|
|
10544
10689
|
const canReport = this.reportedCostUsd !== void 0;
|
|
10545
|
-
const modelCosts = Object.entries(msg.modelUsage ?? {}).filter((entry) => typeof entry[1].costUSD === "number");
|
|
10690
|
+
const modelCosts = Object.entries(msg.modelUsage ?? {}).filter((entry) => typeof entry[1].costUSD === "number" && entry[1].costUSD > 0 && entry[1].costBasis !== "unknown");
|
|
10691
|
+
const growth = canReport && typeof total === "number" && total > 0 ? total - this.reportedCostUsd : void 0;
|
|
10692
|
+
let emitted = 0;
|
|
10693
|
+
const emitCost = (record) => {
|
|
10694
|
+
if (!(record.amount > 0)) return;
|
|
10695
|
+
emitted += record.amount;
|
|
10696
|
+
emit({ type: "usage", record: { source: "claude-code", ...record.model ? { model: record.model } : {}, aggregate: true, ...this.billing ? { sessionBilling: this.billing } : {}, reportedCost: { amount: record.amount, currency: "USD", kind: "estimate", ...record.basis ? { basis: record.basis } : {} } } });
|
|
10697
|
+
};
|
|
10546
10698
|
if (modelCosts.length > 0) {
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
|
|
10699
|
+
const modelGrowth = modelCosts.reduce((sum, [model2, usage]) => sum + Math.max(0, usage.costUSD - (this.reportedModelCostUsd.get(model2) ?? 0)), 0);
|
|
10700
|
+
if (canReport) {
|
|
10701
|
+
if (growth !== void 0 && modelGrowth > growth + 1e-9) {
|
|
10702
|
+
emitCost({ amount: growth });
|
|
10703
|
+
} else {
|
|
10704
|
+
for (const [model2, usage] of modelCosts) emitCost({ model: model2, amount: usage.costUSD - (this.reportedModelCostUsd.get(model2) ?? 0), ...usage.costBasis ? { basis: usage.costBasis } : {} });
|
|
10705
|
+
}
|
|
10552
10706
|
}
|
|
10553
|
-
|
|
10554
|
-
|
|
10707
|
+
for (const [model2, usage] of modelCosts) this.reportedModelCostUsd.set(model2, Math.max(this.reportedModelCostUsd.get(model2) ?? 0, usage.costUSD));
|
|
10708
|
+
} else if (!Object.values(msg.modelUsage ?? {}).some((usage) => usage.costBasis === "unknown") && typeof total === "number" && total > 0 && canReport && total >= this.reportedCostUsd) {
|
|
10709
|
+
emitCost({ amount: total - this.reportedCostUsd });
|
|
10555
10710
|
}
|
|
10556
|
-
if (
|
|
10557
|
-
|
|
10558
|
-
|
|
10711
|
+
if (!canReport) {
|
|
10712
|
+
if (typeof total === "number" && total > 0) this.reportedCostUsd = total;
|
|
10713
|
+
else if (modelCosts.length > 0) this.reportedCostUsd = modelCosts.reduce((sum, [, usage]) => sum + usage.costUSD, 0);
|
|
10714
|
+
} else {
|
|
10715
|
+
this.reportedCostUsd = (this.reportedCostUsd ?? 0) + emitted;
|
|
10716
|
+
}
|
|
10717
|
+
const model = this.plannerModel && this.plannerModel !== "<synthetic>" ? this.plannerModel : this.sessionModel;
|
|
10718
|
+
const contextWindow = model ? msg.modelUsage?.[model]?.contextWindow : void 0;
|
|
10719
|
+
if (typeof contextWindow === "number" && contextWindow > 0) emit({ type: "usage", record: { source: "claude-code", model, contextWindow } });
|
|
10559
10720
|
}
|
|
10560
10721
|
};
|
|
10561
10722
|
|
|
@@ -11190,9 +11351,13 @@ function mergeCodexRateLimits(known, params) {
|
|
|
11190
11351
|
const id = snapshot.limitId ?? "";
|
|
11191
11352
|
const before = known.get(id);
|
|
11192
11353
|
const next = new Map(known);
|
|
11193
|
-
next.set(id, { limitId: id, primary: snapshot.primary ?? before?.primary, secondary: snapshot.secondary ?? before?.secondary });
|
|
11354
|
+
next.set(id, { limitId: id, planType: snapshot.planType ?? before?.planType, primary: snapshot.primary ?? before?.primary, secondary: snapshot.secondary ?? before?.secondary });
|
|
11194
11355
|
return next;
|
|
11195
11356
|
}
|
|
11357
|
+
function codexSessionBilling(limits) {
|
|
11358
|
+
const plan = [...limits.values()].find((snapshot) => snapshot.planType && snapshot.planType !== "unknown")?.planType;
|
|
11359
|
+
return plan ? { type: "subscription", plan } : void 0;
|
|
11360
|
+
}
|
|
11196
11361
|
function isCodexUsageLimit(error) {
|
|
11197
11362
|
const info = error?.codexErrorInfo;
|
|
11198
11363
|
if (info === void 0 || info === null || info === "other") return void 0;
|
|
@@ -11751,8 +11916,21 @@ ${this.exitMessage()}`);
|
|
|
11751
11916
|
case "thread/tokenUsage/updated":
|
|
11752
11917
|
this.emitUsage(msg.params, emit);
|
|
11753
11918
|
return;
|
|
11919
|
+
case "account/updated": {
|
|
11920
|
+
const account = msg.params;
|
|
11921
|
+
if (account?.authMode === "apikey") emit({ type: "usage", record: { source: this.agentId, aggregate: true, sessionBilling: { type: "api-key" } } });
|
|
11922
|
+
else if (account?.authMode === "chatgpt" || account?.authMode === "chatgptAuthTokens") {
|
|
11923
|
+
const plan = account.planType;
|
|
11924
|
+
emit({ type: "usage", record: { source: this.agentId, aggregate: true, sessionBilling: { type: "subscription", ...plan && plan !== "unknown" ? { plan } : {} } } });
|
|
11925
|
+
}
|
|
11926
|
+
return;
|
|
11927
|
+
}
|
|
11754
11928
|
case "account/rateLimits/updated":
|
|
11755
11929
|
this.rateLimits = mergeCodexRateLimits(this.rateLimits, msg.params);
|
|
11930
|
+
{
|
|
11931
|
+
const sessionBilling = codexSessionBilling(this.rateLimits);
|
|
11932
|
+
if (sessionBilling) emit({ type: "usage", record: { source: this.agentId, aggregate: true, sessionBilling } });
|
|
11933
|
+
}
|
|
11756
11934
|
return;
|
|
11757
11935
|
case "turn/started": {
|
|
11758
11936
|
if (this.subagentOf(msg.params?.threadId)) return;
|
|
@@ -11881,6 +12059,13 @@ ${this.exitMessage()}`);
|
|
|
11881
12059
|
* `last.outputTokens` already includes `reasoningOutputTokens` — the thread
|
|
11882
12060
|
* total is `inputTokens + outputTokens`, not a sum of three — so reasoning is
|
|
11883
12061
|
* not added a second time. Codex reports no price.
|
|
12062
|
+
*
|
|
12063
|
+
* The main thread's context is what Codex itself shows as in its window:
|
|
12064
|
+
* `TokenUsage::tokens_in_context_window`, which is `total_tokens` over
|
|
12065
|
+
* `last` as codex-rs defines it. `total_tokens` is input plus output, and
|
|
12066
|
+
* reasoning is part of output, so this is not `input + output − reasoning`
|
|
12067
|
+
* (checked against codex-rs and codex-cli 0.163.0's token_count events,
|
|
12068
|
+
* where `total_tokens` is `input_tokens + output_tokens`).
|
|
11884
12069
|
*/
|
|
11885
12070
|
emitUsage(params, emit) {
|
|
11886
12071
|
const last = params?.tokenUsage?.last;
|
|
@@ -11908,6 +12093,8 @@ ${this.exitMessage()}`);
|
|
|
11908
12093
|
} else {
|
|
11909
12094
|
const window = params?.tokenUsage?.modelContextWindow;
|
|
11910
12095
|
if (typeof window === "number" && window > 0) record.contextWindow = window;
|
|
12096
|
+
const inContext = last.totalTokens !== void 0 && last.totalTokens > 0 ? last.totalTokens : last.inputTokens !== void 0 ? last.inputTokens + (last.outputTokens ?? 0) : void 0;
|
|
12097
|
+
if (inContext !== void 0) record.contextTokens = Math.max(0, inContext);
|
|
11911
12098
|
}
|
|
11912
12099
|
emit({ type: "usage", record });
|
|
11913
12100
|
}
|
|
@@ -12069,7 +12256,9 @@ ${this.exitMessage()}`);
|
|
|
12069
12256
|
}
|
|
12070
12257
|
switch (item.type) {
|
|
12071
12258
|
case "contextCompaction":
|
|
12072
|
-
if (
|
|
12259
|
+
if (subagentId) return;
|
|
12260
|
+
emit({ type: "compaction", trigger: this.command === "compact" ? "manual" : "auto" });
|
|
12261
|
+
emit({ type: "usage", record: { source: this.agentId, compacted: true } });
|
|
12073
12262
|
return;
|
|
12074
12263
|
// Codex announces the item started and completed; whichever lands first delivers.
|
|
12075
12264
|
case "userMessage":
|
|
@@ -12365,8 +12554,9 @@ function usageRecord2(tokens, call) {
|
|
|
12365
12554
|
if ((prompt.inputTokens ?? 0) + outputTokens === 0) return null;
|
|
12366
12555
|
const record = { source: "opencode", ...prompt, outputTokens };
|
|
12367
12556
|
if (call.model) record.model = call.model;
|
|
12368
|
-
if (typeof call.cost === "number" && call.cost > 0) record.reportedCost = { amount: call.cost, currency: "USD" };
|
|
12557
|
+
if (typeof call.cost === "number" && call.cost > 0) record.reportedCost = { amount: call.cost, currency: "USD", kind: "estimate" };
|
|
12369
12558
|
if (call.subagentId) record.subagentId = call.subagentId;
|
|
12559
|
+
else record.contextTokens = (prompt.inputTokens ?? 0) + outputTokens;
|
|
12370
12560
|
return record;
|
|
12371
12561
|
}
|
|
12372
12562
|
function permissionReply(decision, key) {
|
|
@@ -12929,6 +13119,7 @@ var OpenCodeV2 = class {
|
|
|
12929
13119
|
}
|
|
12930
13120
|
if (frame.type === "session.compaction.ended") {
|
|
12931
13121
|
onEvent({ type: "compaction", trigger: data.reason === "manual" ? "manual" : "auto" });
|
|
13122
|
+
onEvent({ type: "usage", record: { source: "opencode", compacted: true } });
|
|
12932
13123
|
return;
|
|
12933
13124
|
}
|
|
12934
13125
|
if (frame.type === "session.execution.started") {
|
|
@@ -13187,7 +13378,7 @@ var OpenCodeAdapter = class {
|
|
|
13187
13378
|
return this.runner.ended;
|
|
13188
13379
|
}
|
|
13189
13380
|
get exited() {
|
|
13190
|
-
return this.runner.
|
|
13381
|
+
return this.runner.hasExited;
|
|
13191
13382
|
}
|
|
13192
13383
|
async start(opts) {
|
|
13193
13384
|
try {
|
|
@@ -13794,6 +13985,7 @@ var OpenCodeAdapter = class {
|
|
|
13794
13985
|
if (session && session !== this.sessionId && !turn.children.follows(session)) return;
|
|
13795
13986
|
if (frame.type === "session.compacted" && session === this.sessionId) {
|
|
13796
13987
|
onEvent({ type: "compaction", trigger: this.manualCompaction ? "manual" : "auto" });
|
|
13988
|
+
onEvent({ type: "usage", record: { source: "opencode", compacted: true } });
|
|
13797
13989
|
return;
|
|
13798
13990
|
}
|
|
13799
13991
|
if (session === this.sessionId && this.taskTurn) this.followTaskTurn(frame, this.taskTurn, onEvent);
|
|
@@ -17348,6 +17540,7 @@ var PlannerConversation = class {
|
|
|
17348
17540
|
const request = summaryRequest(this.currentPlanLines());
|
|
17349
17541
|
const onProgress = (p) => {
|
|
17350
17542
|
if (p.type === "liveness") this.host.onProgress(p);
|
|
17543
|
+
else if (p.type === "usage" && p.record) this.host.onProgress({ ...p, record: { ...p.record, aggregate: true } });
|
|
17351
17544
|
};
|
|
17352
17545
|
let turn;
|
|
17353
17546
|
if (this.canContinueLive()) {
|
|
@@ -19891,10 +20084,21 @@ var GitWorktreeIsolation = class {
|
|
|
19891
20084
|
const entry = record.repos[repo.path];
|
|
19892
20085
|
if (!entry?.changed) continue;
|
|
19893
20086
|
const merged = await this.tryGit(repo.root, ["merge-base", "--is-ancestor", entry.landedTip ?? repo.integrationBranch, "HEAD"]);
|
|
19894
|
-
if (!merged.ok) return false;
|
|
20087
|
+
if (!merged.ok && !await this.integrationGone(repo)) return false;
|
|
19895
20088
|
}
|
|
19896
20089
|
return true;
|
|
19897
20090
|
}
|
|
20091
|
+
/**
|
|
20092
|
+
* The repo's integration branch is gone — deleted by hand, or by a squash
|
|
20093
|
+
* merge's clean-up — in a repo git can read. Merge all merges that branch by
|
|
20094
|
+
* name, so no merge can bring the work in again, and it is not offered as
|
|
20095
|
+
* work to merge. A repo git cannot read at all (moved, or git failing)
|
|
20096
|
+
* proves nothing either way and counts as not merged.
|
|
20097
|
+
*/
|
|
20098
|
+
async integrationGone(repo) {
|
|
20099
|
+
if (!(await this.tryGit(repo.root, ["rev-parse", "--verify", "-q", "HEAD"])).ok) return false;
|
|
20100
|
+
return !(await this.tryGit(repo.root, ["rev-parse", "--verify", "-q", `refs/heads/${repo.integrationBranch}`])).ok;
|
|
20101
|
+
}
|
|
19898
20102
|
async snapshotTree(workspaceRoot, exclude) {
|
|
19899
20103
|
const scan = await this.scanGroup(workspaceRoot);
|
|
19900
20104
|
if ("refused" in scan) return null;
|
|
@@ -20953,6 +21157,9 @@ function nonPromptingFs(fs18) {
|
|
|
20953
21157
|
}
|
|
20954
21158
|
};
|
|
20955
21159
|
}
|
|
21160
|
+
function withoutContext({ contextTokens: _context, ...record }) {
|
|
21161
|
+
return record;
|
|
21162
|
+
}
|
|
20956
21163
|
async function runLoop(prompt, deps) {
|
|
20957
21164
|
if (deps.signal?.aborted) return "[research agent aborted before starting]";
|
|
20958
21165
|
const fs18 = nonPromptingFs(deps.fs);
|
|
@@ -20960,7 +21167,8 @@ async function runLoop(prompt, deps) {
|
|
|
20960
21167
|
(delta) => deps.onProgress?.({ type: "thinking", text: delta, subagentId: deps.subagentId }),
|
|
20961
21168
|
// The record says who made the call: unstamped, the ledger would read the
|
|
20962
21169
|
// subagent's prompt as the planner's own and measure its context by it.
|
|
20963
|
-
|
|
21170
|
+
// The chat states the context its call leaves; a subagent's is no meter's.
|
|
21171
|
+
(record) => deps.onProgress?.({ type: "usage", record: deps.subagentId ? { ...withoutContext(record), subagentId: deps.subagentId } : record })
|
|
20964
21172
|
);
|
|
20965
21173
|
let turn = await chat.sendMessage(prompt, deps.signal);
|
|
20966
21174
|
for (let step = 0; turn.hasToolCalls && step < SUBAGENT_LIMITS.maxSteps; step++) {
|
|
@@ -21470,7 +21678,8 @@ function usageRecordFrom(usage, model) {
|
|
|
21470
21678
|
model,
|
|
21471
21679
|
...inputTokens !== void 0 ? { inputTokens } : {},
|
|
21472
21680
|
...reportedOutput.length > 0 ? { outputTokens: reportedOutput.reduce((a, b) => a + b, 0) } : {},
|
|
21473
|
-
...tokenCount(usage.cachedContentTokenCount) !== void 0 ? { cachedInputTokens: tokenCount(usage.cachedContentTokenCount) } : {}
|
|
21681
|
+
...tokenCount(usage.cachedContentTokenCount) !== void 0 ? { cachedInputTokens: tokenCount(usage.cachedContentTokenCount) } : {},
|
|
21682
|
+
...inputTokens !== void 0 ? { contextTokens: inputTokens + (answerTokens ?? 0) } : {}
|
|
21474
21683
|
};
|
|
21475
21684
|
const anyReported = record.inputTokens !== void 0 || record.outputTokens !== void 0 || record.cachedInputTokens !== void 0;
|
|
21476
21685
|
return anyReported ? record : void 0;
|
|
@@ -21873,7 +22082,9 @@ var OpenAiResearchChat = class {
|
|
|
21873
22082
|
/**
|
|
21874
22083
|
* The one record for this API call, built only from what the provider
|
|
21875
22084
|
* reported. OpenRouter's `cost` is in USD (its credits are dollar-pegged);
|
|
21876
|
-
* nothing is estimated. A field the provider left out stays absent.
|
|
22085
|
+
* nothing is estimated. A field the provider left out stays absent. The
|
|
22086
|
+
* context the call leaves is its prompt and its completion: the API's own
|
|
22087
|
+
* `total_tokens`, and the reply the next call's prompt carries.
|
|
21877
22088
|
*/
|
|
21878
22089
|
usageRecord(reported) {
|
|
21879
22090
|
if (!reported) return void 0;
|
|
@@ -21892,9 +22103,10 @@ var OpenAiResearchChat = class {
|
|
|
21892
22103
|
hasMeasure = true;
|
|
21893
22104
|
}
|
|
21894
22105
|
if (reported.cost !== void 0) {
|
|
21895
|
-
record.reportedCost = { amount: reported.cost, currency: "USD" };
|
|
22106
|
+
record.reportedCost = { amount: reported.cost, currency: "USD", ...record.source === "openrouter" ? { kind: "billed" } : {} };
|
|
21896
22107
|
hasMeasure = true;
|
|
21897
22108
|
}
|
|
22109
|
+
if (record.inputTokens !== void 0) record.contextTokens = record.inputTokens + (record.outputTokens ?? 0);
|
|
21898
22110
|
return hasMeasure ? record : void 0;
|
|
21899
22111
|
}
|
|
21900
22112
|
};
|
|
@@ -23042,6 +23254,8 @@ var IsolationRunController = class {
|
|
|
23042
23254
|
listener;
|
|
23043
23255
|
liveTasks;
|
|
23044
23256
|
run = null;
|
|
23257
|
+
/** An adoption stays scoped even while cleanup temporarily leaves no run record. */
|
|
23258
|
+
adoptions = 0;
|
|
23045
23259
|
/** Copied paths already reported for the current run: every task gets the same copies. */
|
|
23046
23260
|
reportedCopies = /* @__PURE__ */ new Set();
|
|
23047
23261
|
reportedSetupFailures = /* @__PURE__ */ new Set();
|
|
@@ -23117,18 +23331,27 @@ var IsolationRunController = class {
|
|
|
23117
23331
|
* Take over a plan's persisted isolation, or none for a plan that has not
|
|
23118
23332
|
* isolated yet. Whatever a crashed process left behind for the run — a
|
|
23119
23333
|
* worktree still marked active, a directory no record owns — is pruned,
|
|
23120
|
-
* while kept, failed and conflicted worktrees stay for the user.
|
|
23334
|
+
* while kept, failed and conflicted worktrees stay for the user. Then git is
|
|
23335
|
+
* asked which landed work the user has merged since the record was saved:
|
|
23336
|
+
* a settled run whose landed work is all in HEAD has nothing left to hand
|
|
23337
|
+
* over, so it is cleared up as Merge all would have, unless it still holds
|
|
23338
|
+
* work that never landed (ADR-0013).
|
|
23121
23339
|
*
|
|
23122
|
-
* Deliberately not reported
|
|
23123
|
-
*
|
|
23124
|
-
* would otherwise write a new session file on every reload.
|
|
23340
|
+
* Deliberately not reported to the listener: adopting is not a change of the
|
|
23341
|
+
* session's own, and a host that adopts without persisting (VS Code's
|
|
23342
|
+
* restore) would otherwise write a new session file on every reload. What
|
|
23343
|
+
* git changed is returned instead, for the caller to save where it can and
|
|
23344
|
+
* announce. A plan loaded meanwhile supersedes this adoption, which then
|
|
23345
|
+
* touches nothing and resolves `unchanged`.
|
|
23125
23346
|
*/
|
|
23126
23347
|
async adopt(state) {
|
|
23348
|
+
const adoption = ++this.adoptions;
|
|
23127
23349
|
this.run = state?.run ?? null;
|
|
23128
23350
|
this.resolvers = { ...state?.resolvers ?? {} };
|
|
23129
|
-
|
|
23351
|
+
const run = this.run;
|
|
23352
|
+
if (!run) return "unchanged";
|
|
23130
23353
|
try {
|
|
23131
|
-
const pruned = await this.isolation.pruneOrphans(
|
|
23354
|
+
const pruned = await this.isolation.pruneOrphans(run);
|
|
23132
23355
|
for (const task of pruned.kept) {
|
|
23133
23356
|
this.tell("warn", `Task "${task.title}" was still holding unlanded work when this plan was re-opened, so its worktree and branch were kept. Retry it to land the work, or review it by hand.`);
|
|
23134
23357
|
}
|
|
@@ -23136,6 +23359,24 @@ var IsolationRunController = class {
|
|
|
23136
23359
|
} catch (err) {
|
|
23137
23360
|
this.tell("warn", `Could not prune leftover worktrees: ${err instanceof Error ? err.message : String(err)}`);
|
|
23138
23361
|
}
|
|
23362
|
+
if (this.run !== run) return "unchanged";
|
|
23363
|
+
const found = await this.findLanded(run);
|
|
23364
|
+
if (this.run !== run) return "unchanged";
|
|
23365
|
+
const outcome = found ? "changed" : "unchanged";
|
|
23366
|
+
if (this.isOpen || !this.allMerged(run) || this.holdouts(run).length > 0) return outcome;
|
|
23367
|
+
const resolvers = this.resolvers;
|
|
23368
|
+
this.run = null;
|
|
23369
|
+
this.resolvers = {};
|
|
23370
|
+
const discarded = await this.discardMerged(run);
|
|
23371
|
+
if (this.adoptions !== adoption) return "unchanged";
|
|
23372
|
+
if (!discarded) {
|
|
23373
|
+
if (this.run === null) {
|
|
23374
|
+
this.run = run;
|
|
23375
|
+
this.resolvers = resolvers;
|
|
23376
|
+
}
|
|
23377
|
+
return outcome;
|
|
23378
|
+
}
|
|
23379
|
+
return this.isOpen ? "changed" : "cleared";
|
|
23139
23380
|
}
|
|
23140
23381
|
/** Open a run if none is, leaving whether it isolates to its first change task. */
|
|
23141
23382
|
open() {
|
|
@@ -23292,8 +23533,7 @@ var IsolationRunController = class {
|
|
|
23292
23533
|
const run = this.run;
|
|
23293
23534
|
if (mode !== "isolated" || !run) return;
|
|
23294
23535
|
await this.refreshInHead();
|
|
23295
|
-
|
|
23296
|
-
if (records.some((r) => r.status === "merged") && handoffOf(run).landed.length === 0) {
|
|
23536
|
+
if (this.allMerged(run)) {
|
|
23297
23537
|
this.tell("info", "Everything this run landed is merged into your branch already.");
|
|
23298
23538
|
await this.clearMerged(run);
|
|
23299
23539
|
return;
|
|
@@ -23320,11 +23560,21 @@ var IsolationRunController = class {
|
|
|
23320
23560
|
*/
|
|
23321
23561
|
async refreshInHead() {
|
|
23322
23562
|
const run = this.run;
|
|
23323
|
-
if (!run
|
|
23563
|
+
if (!run) return false;
|
|
23564
|
+
const found = await this.findLanded(run);
|
|
23565
|
+
if (found) this.listener.changed();
|
|
23566
|
+
return found;
|
|
23567
|
+
}
|
|
23568
|
+
/** Mark the run's landed work git finds in HEAD; true when some was found. */
|
|
23569
|
+
async findLanded(run) {
|
|
23570
|
+
if (!Object.values(run.tasks).some((r) => r.status === "merged" && !r.inHead)) return false;
|
|
23324
23571
|
const found = await this.isolation.findInHead(run).catch(() => []);
|
|
23325
|
-
if (found.length > 0) this.listener.changed();
|
|
23326
23572
|
return found.length > 0;
|
|
23327
23573
|
}
|
|
23574
|
+
/** The run landed something and every landing is in the user's HEAD. */
|
|
23575
|
+
allMerged(run) {
|
|
23576
|
+
return Object.values(run.tasks).some((r) => r.status === "merged") && handoffOf(run).landed.length === 0;
|
|
23577
|
+
}
|
|
23328
23578
|
/**
|
|
23329
23579
|
* The tracked state of the workspace as an attempt whose tree is checked
|
|
23330
23580
|
* starts (see `attemptKind.checksTree`), for {@link filesChangedSince}
|
|
@@ -23446,7 +23696,7 @@ var IsolationRunController = class {
|
|
|
23446
23696
|
*/
|
|
23447
23697
|
async clearMerged(run) {
|
|
23448
23698
|
const live = this.liveRecords(run);
|
|
23449
|
-
const unlanded =
|
|
23699
|
+
const unlanded = this.holdouts(run).filter((r) => !live.includes(r));
|
|
23450
23700
|
if (live.length > 0 || unlanded.length > 0) {
|
|
23451
23701
|
const why = [
|
|
23452
23702
|
...live.length > 0 ? [`${tasksNamed(live)} ${live.length === 1 ? "is" : "are"} still running or waiting on you`] : [],
|
|
@@ -23456,13 +23706,22 @@ var IsolationRunController = class {
|
|
|
23456
23706
|
this.tell("info", `The run is not cleared up: ${why.join(", and ")}, so ${one ? "its worktree stays" : "their worktrees stay"}, and so do the run's branches.`);
|
|
23457
23707
|
return;
|
|
23458
23708
|
}
|
|
23709
|
+
if (await this.discardMerged(run)) this.forget();
|
|
23710
|
+
}
|
|
23711
|
+
/** The records that keep a settled run from being cleared up: live, or holding work that never landed. */
|
|
23712
|
+
holdouts(run) {
|
|
23713
|
+
const live = this.liveTasks();
|
|
23714
|
+
return Object.values(run.tasks).filter((r) => live.has(r.taskId) || r.status !== "merged");
|
|
23715
|
+
}
|
|
23716
|
+
/** The run's worktrees and merged branches go; false, said, when git would not let them. */
|
|
23717
|
+
async discardMerged(run) {
|
|
23459
23718
|
try {
|
|
23460
23719
|
this.report(await this.isolation.discard(run, { integration: "delete-merged" }));
|
|
23720
|
+
return true;
|
|
23461
23721
|
} catch (err) {
|
|
23462
23722
|
this.tell("warn", `Merged, but could not clean up the run's worktrees and branches: ${err instanceof Error ? err.message : String(err)}`);
|
|
23463
|
-
return;
|
|
23723
|
+
return false;
|
|
23464
23724
|
}
|
|
23465
|
-
this.forget();
|
|
23466
23725
|
}
|
|
23467
23726
|
/** Say what a removal kept back instead of deleting: where the work went, or which worktree stayed. */
|
|
23468
23727
|
report(removal) {
|
|
@@ -24018,6 +24277,10 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
24018
24277
|
store;
|
|
24019
24278
|
output;
|
|
24020
24279
|
attempts = /* @__PURE__ */ new Map();
|
|
24280
|
+
/** Bumped by every plan load: an ended attempt's wind-down belongs to the scope before it. */
|
|
24281
|
+
planScope = 0;
|
|
24282
|
+
/** Each task's runners still winding down once stopped: a continue waits for them (ADR-0018, L1). */
|
|
24283
|
+
windDowns = /* @__PURE__ */ new Map();
|
|
24021
24284
|
verifier = new VerdictEngine();
|
|
24022
24285
|
/** Changed only by start, stop, a plan load, a usage-limit pause, a move off the limit, and {@link settleRun}. */
|
|
24023
24286
|
runState = "idle";
|
|
@@ -24566,8 +24829,9 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
24566
24829
|
return blockingFailures(this.store.planTasks).get(taskId) ?? [];
|
|
24567
24830
|
}
|
|
24568
24831
|
loadPlan(tasks, planRunners = [...DEFAULT_RUNNERS]) {
|
|
24569
|
-
this.store.load(tasks, planRunners);
|
|
24570
24832
|
const repairs = this.endAllAttempts("load").filter((a) => a.kind.kind === "repair" && a.worktree);
|
|
24833
|
+
this.planScope += 1;
|
|
24834
|
+
this.store.load(tasks, planRunners);
|
|
24571
24835
|
for (const a of repairs) this.unawaited(this.runs.release(a.taskId, { keep: true }, a.integration), `Could not keep the worktree of task "${this.store.get(a.taskId)?.title ?? a.taskId}"`);
|
|
24572
24836
|
this.runs.interrupt({ keepOpen: this.isScheduling });
|
|
24573
24837
|
if (this.runState !== "scheduling") this.runState = "idle";
|
|
@@ -24653,6 +24917,7 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
24653
24917
|
if (reason === "user") this.stopGeneration++;
|
|
24654
24918
|
this.runState = "idle";
|
|
24655
24919
|
this.verifier.reset();
|
|
24920
|
+
for (const attempt of this.attempts.values()) this.stopRunner(attempt);
|
|
24656
24921
|
this.runner.stopAll();
|
|
24657
24922
|
const stopped = [];
|
|
24658
24923
|
for (const a of this.endAllAttempts("stop")) {
|
|
@@ -24766,6 +25031,7 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
24766
25031
|
if (!task || !attempt) return;
|
|
24767
25032
|
attempt.decided = true;
|
|
24768
25033
|
const stoppedMidTurn = endedByExit(verdict) ? saidInFlight(attempt) : null;
|
|
25034
|
+
this.stopRunner(attempt);
|
|
24769
25035
|
console.error(`[TaskOrchestrator] Task ${taskRefOf(this.store.planTasks, task.id) ?? `#${task.order}`} "${task.title}" verdict=${verdict.outcome}`);
|
|
24770
25036
|
console.error(`[TaskOrchestrator] Runner: ${task.assignedRunner}, Model: ${task.assignedModel?.modelId ?? "default"}`);
|
|
24771
25037
|
console.error(`[TaskOrchestrator] Prompt preview: ${(task.prompt ?? "").slice(0, 200)}`);
|
|
@@ -25677,7 +25943,9 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
25677
25943
|
autoResumed: opts.autoResumed === true,
|
|
25678
25944
|
turnError: null,
|
|
25679
25945
|
turnOutputFrom: 0,
|
|
25680
|
-
reminded: false
|
|
25946
|
+
reminded: false,
|
|
25947
|
+
runnerStopped: false,
|
|
25948
|
+
planScope: this.planScope
|
|
25681
25949
|
};
|
|
25682
25950
|
this.spawnCounts.set(task.id, attempt.attempt);
|
|
25683
25951
|
this.attempts.set(task.id, attempt);
|
|
@@ -25714,6 +25982,7 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
25714
25982
|
const manifest = this.registry?.get(attempt.runner)?.manifest;
|
|
25715
25983
|
const unavailable = manifest && unavailableTaskModeMessage(manifest, task.taskMode);
|
|
25716
25984
|
if (unavailable) throw new Error(unavailable);
|
|
25985
|
+
if (kind.kind === "continuation") await this.windDowns.get(task.id);
|
|
25717
25986
|
const { cwd, worktree, previousWork } = await attemptCwd(kind, task, this.runs);
|
|
25718
25987
|
attempt.cwd = cwd;
|
|
25719
25988
|
attempt.worktree = worktree;
|
|
@@ -25769,10 +26038,11 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
25769
26038
|
}
|
|
25770
26039
|
if (takesSkills(kind)) this.store.setTaskAttemptSkills(task.id, [...attempt.skills]);
|
|
25771
26040
|
session.onEvent((event) => {
|
|
25772
|
-
if (event.type !== "usage" || this.attempts.get(task.id) !== attempt) return;
|
|
26041
|
+
if (event.type !== "usage" || attempt.planScope !== this.planScope || this.attempts.get(task.id) !== attempt && !attempt.runnerStopped) return;
|
|
25773
26042
|
this.store.recordAttemptUsage(task.id, attempt.attempt, event.record, (/* @__PURE__ */ new Date()).toISOString());
|
|
25774
26043
|
this.emit("onUsageChanged");
|
|
25775
26044
|
});
|
|
26045
|
+
session.onExit(() => this.woundDown(attempt, session));
|
|
25776
26046
|
this.output.attach(task.id, session);
|
|
25777
26047
|
this.verifier.watch(task, session);
|
|
25778
26048
|
session.onEvent((event) => {
|
|
@@ -25941,10 +26211,35 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
25941
26211
|
if (stopsRunner(reason)) {
|
|
25942
26212
|
const task = this.store.get(taskId);
|
|
25943
26213
|
if (task) this.verifier.clear(task);
|
|
25944
|
-
if (
|
|
26214
|
+
if (attempt) this.stopRunner(attempt);
|
|
25945
26215
|
}
|
|
25946
26216
|
return attempt;
|
|
25947
26217
|
}
|
|
26218
|
+
/**
|
|
26219
|
+
* Let the attempt's runner go the way it ends a turn (ADR-0018, L1): it
|
|
26220
|
+
* winds down on its own time, bounded by its interrupt grace, and nothing
|
|
26221
|
+
* here waits on it but a continue of the same task.
|
|
26222
|
+
*/
|
|
26223
|
+
stopRunner(attempt) {
|
|
26224
|
+
const session = attempt.session;
|
|
26225
|
+
if (!session || attempt.runnerStopped) return;
|
|
26226
|
+
attempt.runnerStopped = true;
|
|
26227
|
+
const { taskId } = attempt;
|
|
26228
|
+
const windDown = Promise.all([this.windDowns.get(taskId), this.runner.stop(session.id)]).catch(() => void 0).then(() => {
|
|
26229
|
+
if (this.windDowns.get(taskId) === windDown) this.windDowns.delete(taskId);
|
|
26230
|
+
});
|
|
26231
|
+
this.windDowns.set(taskId, windDown);
|
|
26232
|
+
}
|
|
26233
|
+
/**
|
|
26234
|
+
* An ended attempt's runner is gone: the count its session ended on is the
|
|
26235
|
+
* one a continue of that session counts from. One still current is ended
|
|
26236
|
+
* with that count instead.
|
|
26237
|
+
*/
|
|
26238
|
+
woundDown(attempt, session) {
|
|
26239
|
+
if (attempt.planScope !== this.planScope || !attempt.runnerStopped || this.attempts.get(attempt.taskId) === attempt) return;
|
|
26240
|
+
this.store.endAttemptUsage(attempt.taskId, attempt.attempt, (/* @__PURE__ */ new Date()).toISOString(), session.sessionCost?.() ?? void 0, session.nativeSessionId() ?? void 0);
|
|
26241
|
+
this.emit("onUsageChanged");
|
|
26242
|
+
}
|
|
25948
26243
|
/** Kept on the task once the attempt ends, so a continue can resume it after a reload (ADR-0018, K1). */
|
|
25949
26244
|
saveNativeSession(taskId, nativeSessionId, runner) {
|
|
25950
26245
|
if (nativeSessionId) this.store.setTaskRunnerSessionId(taskId, nativeSessionId, runner);
|
|
@@ -27117,17 +27412,37 @@ var CliAgentAiService = class {
|
|
|
27117
27412
|
* `startConversation` reseeds from Ordewell's transcript, which is the same
|
|
27118
27413
|
* degradation `restoreChat` already performs on every surface.
|
|
27119
27414
|
*
|
|
27415
|
+
* A process can end while idle without Ordewell stopping it — the OS, the
|
|
27416
|
+
* CLI itself, a sleeping machine, a signal sent to its group — and every
|
|
27417
|
+
* path of ours that kills one also drops its adapter. So an adapter still
|
|
27418
|
+
* held whose process has ended died on its own, and is restarted here
|
|
27419
|
+
* rather than answering the next message with its exit code.
|
|
27420
|
+
*
|
|
27120
27421
|
* Also restarted when the policy's decision on the runner's own search has
|
|
27121
27422
|
* changed since the process was spawned: the running thread would otherwise
|
|
27122
27423
|
* keep searching unasked after Full turned Guarded. Codex 0.162 applies
|
|
27123
27424
|
* `thread/resume`'s `config.web_search` either way (checked live).
|
|
27124
27425
|
*/
|
|
27125
27426
|
async ensureAdapter(session) {
|
|
27126
|
-
|
|
27427
|
+
const live = session.adapter;
|
|
27428
|
+
if (live && !live.exited && session.nativeSearch === this.nativeSearch(session)) return live;
|
|
27127
27429
|
session.stop();
|
|
27128
27430
|
const start = session.start;
|
|
27129
27431
|
if (!start) throw new Error("No active planner conversation.");
|
|
27130
|
-
|
|
27432
|
+
const resumeSessionId = session.nativeSessionId ?? void 0;
|
|
27433
|
+
try {
|
|
27434
|
+
const adapter = await this.startAdapter({ ...start.startOptions, resumeSessionId, resumeCost: session.sessionCost ?? void 0 }, start.tools, session);
|
|
27435
|
+
const resumed = adapter.nativeSessionId();
|
|
27436
|
+
if (resumeSessionId && resumed && resumed !== resumeSessionId) {
|
|
27437
|
+
throw new Error("The runner started a different session instead.");
|
|
27438
|
+
}
|
|
27439
|
+
return adapter;
|
|
27440
|
+
} catch (err) {
|
|
27441
|
+
if (!resumeSessionId || session.discarded.aborted) throw err;
|
|
27442
|
+
if (this.session === session) this.session = null;
|
|
27443
|
+
session.discard();
|
|
27444
|
+
throw new Error(`The ${this.runner} planner's session could not be resumed (${err instanceof Error ? err.message : String(err)}). Send your message again: the planner picks the conversation up from its transcript.`);
|
|
27445
|
+
}
|
|
27131
27446
|
}
|
|
27132
27447
|
/**
|
|
27133
27448
|
* What the planner gets of the repository's config, its runner being
|
|
@@ -28659,19 +28974,50 @@ var TaskLogRecorder = class {
|
|
|
28659
28974
|
};
|
|
28660
28975
|
|
|
28661
28976
|
// src/services/ContextWindowFill.ts
|
|
28662
|
-
var
|
|
28663
|
-
constructor(catalog
|
|
28977
|
+
var SessionWindows = class {
|
|
28978
|
+
constructor(catalog = () => void 0) {
|
|
28664
28979
|
this.catalog = catalog;
|
|
28665
|
-
if (last.contextWindow !== void 0) this.known.set(modelKey(last.source, last.model), last.contextWindow);
|
|
28666
28980
|
}
|
|
28667
28981
|
catalog;
|
|
28668
|
-
|
|
28982
|
+
reported = /* @__PURE__ */ new Map();
|
|
28983
|
+
report(source, model, contextWindow) {
|
|
28984
|
+
if (contextWindow > 0) this.reported.set(modelKey(source, model), { source, model, contextWindow });
|
|
28985
|
+
}
|
|
28986
|
+
get(source, model) {
|
|
28987
|
+
return this.reported.get(modelKey(source, model))?.contextWindow;
|
|
28988
|
+
}
|
|
28989
|
+
snapshot() {
|
|
28990
|
+
return [...this.reported.values()];
|
|
28991
|
+
}
|
|
28992
|
+
restore(saved) {
|
|
28993
|
+
this.reported.clear();
|
|
28994
|
+
for (const { source, model, contextWindow } of saved ?? []) this.report(source, model, contextWindow);
|
|
28995
|
+
}
|
|
28996
|
+
clear() {
|
|
28997
|
+
this.reported.clear();
|
|
28998
|
+
}
|
|
28999
|
+
};
|
|
29000
|
+
var ContextWindowFill = class {
|
|
29001
|
+
constructor(windows, last = {}) {
|
|
29002
|
+
this.windows = windows;
|
|
29003
|
+
if (last.contextWindow !== void 0) this.known.set(modelKey(last.source, last.model), last.contextWindow);
|
|
29004
|
+
}
|
|
29005
|
+
windows;
|
|
29006
|
+
/** The window this stream used for each of its models, by reporter and model. */
|
|
28669
29007
|
known = /* @__PURE__ */ new Map();
|
|
28670
29008
|
fill(record) {
|
|
28671
29009
|
if (record.subagentId || record.aggregate) return record;
|
|
28672
29010
|
const key = modelKey(record.source, record.model);
|
|
29011
|
+
if (record.compacted) {
|
|
29012
|
+
if (!record.model) return record;
|
|
29013
|
+
const known = this.windows.get(record.source, record.model) ?? this.known.get(key) ?? positive(this.windows.catalog(record.source, record.model));
|
|
29014
|
+
if (known === void 0) return record;
|
|
29015
|
+
this.known.set(key, known);
|
|
29016
|
+
return { ...record, contextWindow: known };
|
|
29017
|
+
}
|
|
28673
29018
|
const reported = record.contextWindow !== void 0 && record.contextWindow > 0 ? record.contextWindow : void 0;
|
|
28674
|
-
|
|
29019
|
+
if (reported !== void 0 && record.model) this.windows.report(record.source, record.model, reported);
|
|
29020
|
+
const window = reported ?? (record.model ? this.windows.get(record.source, record.model) : void 0) ?? this.known.get(key) ?? (record.model ? positive(this.windows.catalog(record.source, record.model)) : void 0);
|
|
28675
29021
|
if (window !== void 0) this.known.set(key, window);
|
|
28676
29022
|
return window === void 0 || window === record.contextWindow ? record : { ...record, contextWindow: window };
|
|
28677
29023
|
}
|
|
@@ -28679,9 +29025,9 @@ var ContextWindowFill = class {
|
|
|
28679
29025
|
function modelKey(source, model) {
|
|
28680
29026
|
return JSON.stringify([source ?? null, model ?? null]);
|
|
28681
29027
|
}
|
|
28682
|
-
function withContextWindows(runner,
|
|
29028
|
+
function withContextWindows(runner, windows) {
|
|
28683
29029
|
return {
|
|
28684
|
-
spawn: async (opts) => windowedSession(await runner.spawn(opts), new ContextWindowFill(
|
|
29030
|
+
spawn: async (opts) => windowedSession(await runner.spawn(opts), new ContextWindowFill(windows, opts.resumeWindow)),
|
|
28685
29031
|
stop: (sessionId) => runner.stop(sessionId),
|
|
28686
29032
|
stopAll: () => runner.stopAll(),
|
|
28687
29033
|
get activeCount() {
|
|
@@ -28712,12 +29058,12 @@ function positive(n) {
|
|
|
28712
29058
|
|
|
28713
29059
|
// src/services/PlannerUsage.ts
|
|
28714
29060
|
var PlannerUsageLedger = class {
|
|
28715
|
-
/** `
|
|
28716
|
-
constructor(
|
|
28717
|
-
this.
|
|
28718
|
-
this.windows = new ContextWindowFill(
|
|
29061
|
+
/** `sessionWindows` serves the window of a planner model whose backend reports none. */
|
|
29062
|
+
constructor(sessionWindows = new SessionWindows()) {
|
|
29063
|
+
this.sessionWindows = sessionWindows;
|
|
29064
|
+
this.windows = new ContextWindowFill(sessionWindows);
|
|
28719
29065
|
}
|
|
28720
|
-
|
|
29066
|
+
sessionWindows;
|
|
28721
29067
|
usage = { totals: {} };
|
|
28722
29068
|
windows;
|
|
28723
29069
|
/** Fold one model call's record into the totals; returns the running ledger. */
|
|
@@ -28728,16 +29074,16 @@ var PlannerUsageLedger = class {
|
|
|
28728
29074
|
/** Adopt a persisted ledger (a reopened session) or start from zero. */
|
|
28729
29075
|
restore(usage) {
|
|
28730
29076
|
this.usage = usage ?? { totals: {} };
|
|
28731
|
-
this.windows = new ContextWindowFill(this.
|
|
29077
|
+
this.windows = new ContextWindowFill(this.sessionWindows, this.usage);
|
|
28732
29078
|
}
|
|
28733
29079
|
/** Start a fresh session's ledger from zero. */
|
|
28734
29080
|
clear() {
|
|
28735
29081
|
this.usage = { totals: {} };
|
|
28736
|
-
this.windows = new ContextWindowFill(this.
|
|
29082
|
+
this.windows = new ContextWindowFill(this.sessionWindows);
|
|
28737
29083
|
}
|
|
28738
|
-
/** Whether
|
|
29084
|
+
/** Whether the ledger has measures or session billing facts to broadcast. */
|
|
28739
29085
|
get hasUsage() {
|
|
28740
|
-
return isMeasured(this.usage.totals);
|
|
29086
|
+
return isMeasured(this.usage.totals) || this.usage.sessionBilling !== void 0;
|
|
28741
29087
|
}
|
|
28742
29088
|
/** The value persisted onto the plan state. */
|
|
28743
29089
|
snapshot() {
|
|
@@ -30186,7 +30532,7 @@ function createSession(deps) {
|
|
|
30186
30532
|
const backend = new PlannerBackend(deps.aiService ? { pinned: deps.aiService, busy } : { provider: () => deps.config.aiProvider, create: () => createAiService(deps.config, { workspaceRoot: deps.workspaceRoot, mcpServer }), busy });
|
|
30187
30533
|
const aiService = () => backend.current();
|
|
30188
30534
|
const store = new PlanStore();
|
|
30189
|
-
const
|
|
30535
|
+
const windows = new SessionWindows((source, model) => deps.modelResolver.contextWindowFor?.(source, model));
|
|
30190
30536
|
const taskLogs = new TaskLogRecorder({
|
|
30191
30537
|
broadcast: deps.broadcast,
|
|
30192
30538
|
// Read through the Session, whose id and workspace are the ones it saves
|
|
@@ -30219,7 +30565,7 @@ function createSession(deps) {
|
|
|
30219
30565
|
const orchestrator = TaskOrchestrator.compose({
|
|
30220
30566
|
config: deps.config,
|
|
30221
30567
|
notifications: deps.notifications,
|
|
30222
|
-
runner: taskLogs.wrap(runnerApprovals.wrap(withContextWindows(deps.runner,
|
|
30568
|
+
runner: taskLogs.wrap(runnerApprovals.wrap(withContextWindows(deps.runner, windows))),
|
|
30223
30569
|
store,
|
|
30224
30570
|
output: deps.taskOutput,
|
|
30225
30571
|
isolation: deps.isolation,
|
|
@@ -30231,7 +30577,7 @@ function createSession(deps) {
|
|
|
30231
30577
|
runSettings: () => session.planState?.runSettings,
|
|
30232
30578
|
isApproved: (taskId) => session.isApproved(taskId)
|
|
30233
30579
|
});
|
|
30234
|
-
const usage = new PlannerUsageLedger(
|
|
30580
|
+
const usage = new PlannerUsageLedger(windows);
|
|
30235
30581
|
const events = new SessionEventRelay({
|
|
30236
30582
|
broadcast: deps.broadcast,
|
|
30237
30583
|
onNotice: deps.onNotice,
|
|
@@ -30301,6 +30647,7 @@ function createSession(deps) {
|
|
|
30301
30647
|
runs: orchestrator.runs,
|
|
30302
30648
|
events,
|
|
30303
30649
|
usage,
|
|
30650
|
+
windows,
|
|
30304
30651
|
approvals,
|
|
30305
30652
|
approvalPolicy,
|
|
30306
30653
|
taskLogs,
|
|
@@ -30328,6 +30675,7 @@ function configSecrets(config) {
|
|
|
30328
30675
|
var Session = class {
|
|
30329
30676
|
backend;
|
|
30330
30677
|
usage;
|
|
30678
|
+
windows;
|
|
30331
30679
|
events;
|
|
30332
30680
|
planner;
|
|
30333
30681
|
orchestrator;
|
|
@@ -30433,6 +30781,7 @@ var Session = class {
|
|
|
30433
30781
|
this.runs = parts.runs;
|
|
30434
30782
|
this.events = parts.events;
|
|
30435
30783
|
this.usage = parts.usage;
|
|
30784
|
+
this.windows = parts.windows;
|
|
30436
30785
|
this.approvals = parts.approvals;
|
|
30437
30786
|
this.approvalPolicy = parts.approvalPolicy;
|
|
30438
30787
|
this.taskLogs = parts.taskLogs;
|
|
@@ -30911,6 +31260,7 @@ var Session = class {
|
|
|
30911
31260
|
this.syncPlanTasks();
|
|
30912
31261
|
this.plan.isolation = this.runs.planIsolation ?? void 0;
|
|
30913
31262
|
this.plan.plannerUsage = this.usage.snapshot();
|
|
31263
|
+
this.plan.contextWindows = this.windows.snapshot();
|
|
30914
31264
|
this.plan.supervision = this.supervision.snapshot();
|
|
30915
31265
|
this.plan.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
30916
31266
|
this.save(this.plan, this.goal, this.workspace, this.currentSessionId);
|
|
@@ -30940,6 +31290,7 @@ var Session = class {
|
|
|
30940
31290
|
this.owingEffects.clear();
|
|
30941
31291
|
this.conversation.reset();
|
|
30942
31292
|
this.usage.clear();
|
|
31293
|
+
this.windows.clear();
|
|
30943
31294
|
this.approvals.clear(isPlannerApproval);
|
|
30944
31295
|
if (!keepGrants) this.approvalPolicy.reset();
|
|
30945
31296
|
this.store.clearLog();
|
|
@@ -31280,7 +31631,18 @@ var Session = class {
|
|
|
31280
31631
|
if (!this.plan) throw new ConversationEditError("No planning conversation to condense");
|
|
31281
31632
|
const opening = this.plannerTurnMayOpen(signal);
|
|
31282
31633
|
if (opening) await opening;
|
|
31283
|
-
|
|
31634
|
+
const compaction = await this.conversation.compact(signal);
|
|
31635
|
+
const usage = this.usage.snapshot();
|
|
31636
|
+
if (usage.lastPromptTokens !== void 0) {
|
|
31637
|
+
this.usage.record({
|
|
31638
|
+
source: usage.source ?? this.config.aiProvider,
|
|
31639
|
+
...usage.model ? { model: usage.model } : {},
|
|
31640
|
+
compacted: true
|
|
31641
|
+
});
|
|
31642
|
+
this.persist();
|
|
31643
|
+
this.broadcast(this.usage.message());
|
|
31644
|
+
}
|
|
31645
|
+
return compaction;
|
|
31284
31646
|
}
|
|
31285
31647
|
rewindTargets() {
|
|
31286
31648
|
return this.conversation.rewindTargets();
|
|
@@ -31590,9 +31952,9 @@ var Session = class {
|
|
|
31590
31952
|
runSnapshot() {
|
|
31591
31953
|
if (!this.plan) return EMPTY_RUN;
|
|
31592
31954
|
const planned = reduceRun(EMPTY_RUN, { type: "plan_snapshot", plan: surfacePlan({ ...this.plan, tasks: this.store.snapshot() }) });
|
|
31593
|
-
const
|
|
31594
|
-
const
|
|
31595
|
-
return this.
|
|
31955
|
+
const handed = this.sentHandoff ? reduceRun(planned, { type: "isolation_handoff", ...this.sentHandoff }) : planned;
|
|
31956
|
+
const view = reduceRun(handed, this.events.statusMessage());
|
|
31957
|
+
return this.sentHeld ? reduceRun(view, { type: "plan_edit_held", held: this.sentHeld }) : view;
|
|
31596
31958
|
}
|
|
31597
31959
|
/** The ancestors a task waits on at its merge gate (ADR-0020); empty when it waits for no Merge all. */
|
|
31598
31960
|
mergeGate(taskId) {
|
|
@@ -31768,7 +32130,10 @@ var Session = class {
|
|
|
31768
32130
|
if (!plan.approvedTasks && plan.approved) {
|
|
31769
32131
|
plan.approvedTasks = flattenTasks(plan.tasks).map((t) => t.id);
|
|
31770
32132
|
}
|
|
31771
|
-
if (adopting)
|
|
32133
|
+
if (adopting) {
|
|
32134
|
+
this.usage.restore(plan.plannerUsage);
|
|
32135
|
+
this.windows.restore(plan.contextWindows);
|
|
32136
|
+
}
|
|
31772
32137
|
if (opts?.sessionId) this.currentSessionId = opts.sessionId;
|
|
31773
32138
|
if (adopting) {
|
|
31774
32139
|
for (const task of flattenTasks(plan.tasks)) delete task.resumeAt;
|
|
@@ -31782,12 +32147,31 @@ var Session = class {
|
|
|
31782
32147
|
}
|
|
31783
32148
|
carryOverQueuedEdits(plan);
|
|
31784
32149
|
if (adopting) {
|
|
31785
|
-
void this.
|
|
32150
|
+
void this.adoptRun(plan, opts?.persist !== false || opts?.sessionId !== void 0);
|
|
31786
32151
|
this.sentHandoff = this.owedHandoff();
|
|
31787
32152
|
}
|
|
31788
32153
|
if (opts?.persist !== false) this.persist();
|
|
31789
32154
|
if (this.usage.hasUsage) this.broadcast(this.usage.message());
|
|
31790
32155
|
}
|
|
32156
|
+
/**
|
|
32157
|
+
* Take the loaded plan's run over, then correct what the surfaces were told
|
|
32158
|
+
* by what git found: the saved record cannot know what the user merged
|
|
32159
|
+
* since, and its handoff would offer Merge all for work already in their
|
|
32160
|
+
* branch. The status update says what landed work is still unmerged, which
|
|
32161
|
+
* a handoff folds to (see `reduceRun`), and `run_closed` that the run was
|
|
32162
|
+
* cleared up. Saved only where the load itself may save (`persists`), so a
|
|
32163
|
+
* restore with no session file to write keeps the correction in memory for
|
|
32164
|
+
* its next save. A plan loaded meanwhile is not this one's to correct.
|
|
32165
|
+
* Starts nothing (ADR-0044).
|
|
32166
|
+
*/
|
|
32167
|
+
async adoptRun(plan, persists) {
|
|
32168
|
+
const outcome = await this.runs.adopt(plan.isolation ?? null);
|
|
32169
|
+
if (this.closed || this.plan !== plan || outcome === "unchanged") return;
|
|
32170
|
+
plan.isolation = this.runs.planIsolation ?? void 0;
|
|
32171
|
+
if (persists) this.persist();
|
|
32172
|
+
this.events.status(plan);
|
|
32173
|
+
if (outcome === "cleared") this.broadcast({ type: "run_closed" });
|
|
32174
|
+
}
|
|
31791
32175
|
destroy() {
|
|
31792
32176
|
this.closed = true;
|
|
31793
32177
|
this.dropPlanScope();
|
|
@@ -31836,17 +32220,12 @@ var AbstractRunner = class {
|
|
|
31836
32220
|
get activeCount() {
|
|
31837
32221
|
return this.sessions.size;
|
|
31838
32222
|
}
|
|
32223
|
+
/** A session winding down stays registered until it exits, so a second stop gets the same wind-down. */
|
|
31839
32224
|
stop(sessionId) {
|
|
31840
|
-
|
|
31841
|
-
if (!session) return;
|
|
31842
|
-
session.kill();
|
|
31843
|
-
this.sessions.delete(sessionId);
|
|
32225
|
+
return this.sessions.get(sessionId)?.stop() ?? Promise.resolve();
|
|
31844
32226
|
}
|
|
31845
32227
|
stopAll() {
|
|
31846
|
-
for (const
|
|
31847
|
-
session.kill();
|
|
31848
|
-
}
|
|
31849
|
-
this.sessions.clear();
|
|
32228
|
+
for (const session of this.sessions.values()) void session.stop();
|
|
31850
32229
|
}
|
|
31851
32230
|
registerSession(id, session) {
|
|
31852
32231
|
this.sessions.set(id, session);
|
|
@@ -32004,6 +32383,8 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
32004
32383
|
queue = [];
|
|
32005
32384
|
messageCount = 0;
|
|
32006
32385
|
interrupting = null;
|
|
32386
|
+
/** The wind-down once {@link stop} was asked for: the same for every caller, and nothing new is sent meanwhile. */
|
|
32387
|
+
stopping = null;
|
|
32007
32388
|
lastSessionId = null;
|
|
32008
32389
|
lastSessionCost = null;
|
|
32009
32390
|
structuredEmitter = new EventEmitter2();
|
|
@@ -32029,7 +32410,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
32029
32410
|
await this.startAttached(this.launch.startOptions);
|
|
32030
32411
|
this.state = "working";
|
|
32031
32412
|
setImmediate(() => {
|
|
32032
|
-
if (!this.exited) this.deliver(prompt);
|
|
32413
|
+
if (!this.exited && !this.stopping) this.deliver(prompt);
|
|
32033
32414
|
});
|
|
32034
32415
|
}
|
|
32035
32416
|
getOutput() {
|
|
@@ -32109,6 +32490,53 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
32109
32490
|
this.withdrawPermissions();
|
|
32110
32491
|
this.baseHandleExit(-1);
|
|
32111
32492
|
}
|
|
32493
|
+
stop() {
|
|
32494
|
+
if (this.stopping) return this.stopping;
|
|
32495
|
+
if (this.exited) return Promise.resolve();
|
|
32496
|
+
let woundDown;
|
|
32497
|
+
this.stopping = new Promise((resolve9) => {
|
|
32498
|
+
woundDown = resolve9;
|
|
32499
|
+
});
|
|
32500
|
+
void this.windDown().catch(() => this.kill()).then(woundDown);
|
|
32501
|
+
return this.stopping;
|
|
32502
|
+
}
|
|
32503
|
+
/**
|
|
32504
|
+
* The attempt is over (ADR-0018, L1), so what is still owed goes undelivered
|
|
32505
|
+
* and every tool request unanswered at once. A turn still open is
|
|
32506
|
+
* interrupted, and its close — where Claude Code reports the turn's cost and
|
|
32507
|
+
* its model's window — is waited for; then the runner is told nothing more
|
|
32508
|
+
* is coming and left to exit by itself. All of it within the interrupt
|
|
32509
|
+
* grace: a runner still there once it runs out is killed.
|
|
32510
|
+
*/
|
|
32511
|
+
async windDown() {
|
|
32512
|
+
const adapter = this.adapterAttached ? this.adapter : null;
|
|
32513
|
+
const deadline = Date.now() + this.launch.interruptGraceMs;
|
|
32514
|
+
this.revokeTools();
|
|
32515
|
+
this.undeliverQueued();
|
|
32516
|
+
this.withdrawPermissions();
|
|
32517
|
+
const open = this.turn && !this.turn.ended ? this.turn : null;
|
|
32518
|
+
if (adapter && (!open || await this.turnClosed(open, adapter, deadline)) && adapter.endInput && !this.exited) {
|
|
32519
|
+
adapter.endInput();
|
|
32520
|
+
await this.within(this.gone(), deadline);
|
|
32521
|
+
}
|
|
32522
|
+
if (this.exited) adapter?.dispose();
|
|
32523
|
+
else this.kill();
|
|
32524
|
+
}
|
|
32525
|
+
/** Whether `turn` closed within the deadline once interrupted. */
|
|
32526
|
+
async turnClosed(turn, adapter, deadline) {
|
|
32527
|
+
const closed = new Promise((resolve9) => this.structuredEmitter.once("turnEnd", () => resolve9()));
|
|
32528
|
+
adapter.interrupt(Math.max(0, deadline - Date.now())).catch(() => false);
|
|
32529
|
+
return await this.within(closed, deadline) && turn.ended;
|
|
32530
|
+
}
|
|
32531
|
+
/** Whether `done` settled before the deadline, or the session ended meanwhile. */
|
|
32532
|
+
within(done, deadline) {
|
|
32533
|
+
return settleWithin(done.then(() => true), { timeoutMs: Math.max(0, deadline - Date.now()), onTimeout: () => false, ended: this.gone(), onEnded: () => true });
|
|
32534
|
+
}
|
|
32535
|
+
gone() {
|
|
32536
|
+
const { signal } = this.ended;
|
|
32537
|
+
if (signal.aborted) return Promise.resolve();
|
|
32538
|
+
return new Promise((resolve9) => signal.addEventListener("abort", () => resolve9(), { once: true }));
|
|
32539
|
+
}
|
|
32112
32540
|
turnState() {
|
|
32113
32541
|
return this.state;
|
|
32114
32542
|
}
|
|
@@ -32134,7 +32562,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
32134
32562
|
this.messageCount += 1;
|
|
32135
32563
|
const id = `msg-${this.messageCount}`;
|
|
32136
32564
|
const sender2 = opening.from ? { from: opening.from } : {};
|
|
32137
|
-
if (this.exited) this.emitEvent({ type: "message_undelivered", messageId: id, text, ...sender2 });
|
|
32565
|
+
if (this.exited || this.stopping) this.emitEvent({ type: "message_undelivered", messageId: id, text, ...sender2 });
|
|
32138
32566
|
else if (this.state === "idle" && this.adapterStarted && !this.sending) this.deliver(text, opening);
|
|
32139
32567
|
else {
|
|
32140
32568
|
this.state = "working";
|
|
@@ -32189,7 +32617,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
32189
32617
|
});
|
|
32190
32618
|
}
|
|
32191
32619
|
runningTurn() {
|
|
32192
|
-
return !this.exited && this.adapter !== null && this.turn !== null && !this.turn.ended;
|
|
32620
|
+
return !this.exited && !this.stopping && this.adapter !== null && this.turn !== null && !this.turn.ended;
|
|
32193
32621
|
}
|
|
32194
32622
|
/** No runner can recall a message once offered to it, so only a queued one comes back. */
|
|
32195
32623
|
removeQueued(id) {
|
|
@@ -32250,7 +32678,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
32250
32678
|
if (this.interrupting) return this.interrupting;
|
|
32251
32679
|
const turn = this.turn;
|
|
32252
32680
|
const adapter = this.adapter;
|
|
32253
|
-
if (!turn || turn.ended || !adapter) return Promise.resolve();
|
|
32681
|
+
if (!turn || turn.ended || !adapter || this.stopping) return Promise.resolve();
|
|
32254
32682
|
this.interrupting = this.interruptTurn(turn, adapter).finally(() => {
|
|
32255
32683
|
this.interrupting = null;
|
|
32256
32684
|
this.offerNext();
|
|
@@ -32264,7 +32692,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
32264
32692
|
const settled = await settleWithin(ended.then(() => true), { timeoutMs: grace, onTimeout: () => false });
|
|
32265
32693
|
if (settled) return;
|
|
32266
32694
|
}
|
|
32267
|
-
if (turn.ended || this.exited) return;
|
|
32695
|
+
if (turn.ended || this.exited || this.stopping) return;
|
|
32268
32696
|
await this.restartInterrupted(turn);
|
|
32269
32697
|
}
|
|
32270
32698
|
/**
|
|
@@ -32368,7 +32796,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
32368
32796
|
offerNext() {
|
|
32369
32797
|
const adapter = this.adapter;
|
|
32370
32798
|
const turn = this.turn;
|
|
32371
|
-
if (!adapter?.steer || !turn || turn.ended || turn.steerRefused || this.interrupting || this.exited) return;
|
|
32799
|
+
if (!adapter?.steer || !turn || turn.ended || turn.steerRefused || this.interrupting || this.exited || this.stopping) return;
|
|
32372
32800
|
if (this.queue.some((m) => m.stage === "steering")) return;
|
|
32373
32801
|
const next = this.queue.find((m) => m.stage === "queued");
|
|
32374
32802
|
if (!next || next.command) return;
|
|
@@ -32443,7 +32871,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
32443
32871
|
}
|
|
32444
32872
|
return;
|
|
32445
32873
|
}
|
|
32446
|
-
if (!turn && TURN_OPENING_EVENTS.has(event.type) && !("subagentId" in event && event.subagentId)) {
|
|
32874
|
+
if (!turn && !this.stopping && TURN_OPENING_EVENTS.has(event.type) && !("subagentId" in event && event.subagentId)) {
|
|
32447
32875
|
turn = this.openTurn("");
|
|
32448
32876
|
this.offerNext();
|
|
32449
32877
|
}
|
|
@@ -32514,7 +32942,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
32514
32942
|
* back in Ordewell's hands: while it is on offer or owed, nothing overtakes it.
|
|
32515
32943
|
*/
|
|
32516
32944
|
deliverNext() {
|
|
32517
|
-
if (this.exited || this.sending || this.turn && !this.turn.ended) return;
|
|
32945
|
+
if (this.exited || this.stopping || this.sending || this.turn && !this.turn.ended) return;
|
|
32518
32946
|
const head = this.queue[0];
|
|
32519
32947
|
if (!head || this.queue.some((message) => message.stage !== "queued")) return;
|
|
32520
32948
|
this.queue.shift();
|
|
@@ -32683,11 +33111,13 @@ var STATE_FILE = "state.json";
|
|
|
32683
33111
|
function getStatePath(baseDir) {
|
|
32684
33112
|
return path33.join(getStateDir(baseDir), STATE_FILE);
|
|
32685
33113
|
}
|
|
32686
|
-
|
|
33114
|
+
var SESSION_ID_KEY = "sessionId";
|
|
33115
|
+
function saveState(plan, baseDir, sessionId) {
|
|
32687
33116
|
const statePath = getStatePath(baseDir ?? process.cwd());
|
|
32688
33117
|
ensureDir(path33.dirname(statePath));
|
|
32689
33118
|
plan.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
32690
|
-
|
|
33119
|
+
const file = sessionId ? { ...plan, [SESSION_ID_KEY]: sessionId } : plan;
|
|
33120
|
+
fs17.writeFileSync(statePath, JSON.stringify(file, null, 2));
|
|
32691
33121
|
}
|
|
32692
33122
|
function loadState(baseDir, logger = defaultLogger) {
|
|
32693
33123
|
const dir = baseDir ?? process.cwd();
|
|
@@ -32700,10 +33130,12 @@ function loadState(baseDir, logger = defaultLogger) {
|
|
|
32700
33130
|
logger.warn("stateStore", `rejecting state from ${statePath}: old format (scalar "runner" instead of "runners" array). Please re-plan.`);
|
|
32701
33131
|
return null;
|
|
32702
33132
|
}
|
|
33133
|
+
const sessionId = parsed[SESSION_ID_KEY];
|
|
33134
|
+
delete parsed[SESSION_ID_KEY];
|
|
32703
33135
|
const plan = parsed;
|
|
32704
33136
|
migratePlanStateIsolation(plan);
|
|
32705
33137
|
migrateLoadedTasks(plan.tasks);
|
|
32706
|
-
return plan;
|
|
33138
|
+
return typeof sessionId === "string" && sessionId ? { plan, sessionId } : { plan };
|
|
32707
33139
|
} catch (err) {
|
|
32708
33140
|
logger.warn("stateStore", `failed to load state from ${statePath}; falling back to empty state`, err);
|
|
32709
33141
|
return null;
|
|
@@ -32959,7 +33391,10 @@ export {
|
|
|
32959
33391
|
collectProviderCredentials,
|
|
32960
33392
|
commandAllowedBy,
|
|
32961
33393
|
configuredProviders,
|
|
33394
|
+
contextMeterText,
|
|
33395
|
+
contextMeterTitle,
|
|
32962
33396
|
continuability,
|
|
33397
|
+
costText,
|
|
32963
33398
|
countedSessionCost,
|
|
32964
33399
|
createAiService,
|
|
32965
33400
|
createEmptyPlan,
|
|
@@ -33002,7 +33437,9 @@ export {
|
|
|
33002
33437
|
flattenTasksWithParents,
|
|
33003
33438
|
followTaskLog,
|
|
33004
33439
|
followTurn,
|
|
33440
|
+
formatCost,
|
|
33005
33441
|
formatSearchOutput,
|
|
33442
|
+
formatTokenCount,
|
|
33006
33443
|
fromTranscript,
|
|
33007
33444
|
getLatestSession,
|
|
33008
33445
|
getProviderMeta,
|
|
@@ -33054,6 +33491,7 @@ export {
|
|
|
33054
33491
|
migratePlanState,
|
|
33055
33492
|
mintDaemonToken,
|
|
33056
33493
|
mintSessionId,
|
|
33494
|
+
modelCostTitle,
|
|
33057
33495
|
namedRepos,
|
|
33058
33496
|
newTaskFields,
|
|
33059
33497
|
normalizeAgentArgs,
|
|
@@ -33139,6 +33577,7 @@ export {
|
|
|
33139
33577
|
stopTurn,
|
|
33140
33578
|
stripAnsi,
|
|
33141
33579
|
stripModelPrefix,
|
|
33580
|
+
subagentCostTitle,
|
|
33142
33581
|
subtreeIds,
|
|
33143
33582
|
summarizeToolCall,
|
|
33144
33583
|
supervisorSummary,
|
|
@@ -33157,6 +33596,7 @@ export {
|
|
|
33157
33596
|
taskStartedNotice,
|
|
33158
33597
|
taskStatusKind,
|
|
33159
33598
|
taskUsageSummary,
|
|
33599
|
+
tasksTotalText,
|
|
33160
33600
|
textHasTaskOps,
|
|
33161
33601
|
textHasTaskQuery,
|
|
33162
33602
|
titledRefs,
|
|
@@ -33174,6 +33614,7 @@ export {
|
|
|
33174
33614
|
unsendAll,
|
|
33175
33615
|
unsendLatest,
|
|
33176
33616
|
usageLine,
|
|
33617
|
+
usageText,
|
|
33177
33618
|
waitsForApproval,
|
|
33178
33619
|
walkPlan,
|
|
33179
33620
|
wellKnownBinDirs,
|