@ordewell/core 0.10.9 → 0.10.11
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-C9W5mRYA.d.mts → IFileSystem-1PqRCjhu.d.mts} +21 -3
- package/dist/{IFileSystem-B8qRNC_6.d.ts → IFileSystem-lTRL_G9Z.d.ts} +21 -3
- package/dist/{ModeResolver-C1PN5FYG.d.mts → ModeResolver-6TQXVsde.d.mts} +1 -1
- package/dist/{ModeResolver-CaPnoU8N.d.ts → ModeResolver-CoJAJD-f.d.ts} +1 -1
- package/dist/{Task-hFfRS9rN.d.mts → Task-CfBM0wkV.d.mts} +94 -21
- package/dist/{Task-hFfRS9rN.d.ts → Task-CfBM0wkV.d.ts} +94 -21
- package/dist/{chunk-H7QX32BH.mjs → chunk-24EL4Z2W.mjs} +2 -2
- package/dist/{chunk-O6LQU3W6.mjs → chunk-ESKK5I4F.mjs} +138 -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 +129 -20
- package/dist/index.d.ts +129 -20
- package/dist/index.js +556 -92
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +432 -76
- 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-CGKX3aeV.d.ts → runSettingsText-C5uv2axI.d.ts} +29 -18
- package/dist/{runSettingsText-rLoYQlUH.d.mts → runSettingsText-Jb_efnI4.d.mts} +29 -18
- package/dist/testing.d.mts +12 -3
- package/dist/testing.d.ts +12 -3
- package/dist/testing.js +16 -1
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +17 -2
- package/dist/testing.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-EEE2GBLG.mjs.map +0 -1
- package/dist/chunk-O6LQU3W6.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.js
CHANGED
|
@@ -203,7 +203,10 @@ __export(index_exports, {
|
|
|
203
203
|
collectProviderCredentials: () => collectProviderCredentials,
|
|
204
204
|
commandAllowedBy: () => commandAllowedBy,
|
|
205
205
|
configuredProviders: () => configuredProviders,
|
|
206
|
+
contextMeterText: () => contextMeterText,
|
|
207
|
+
contextMeterTitle: () => contextMeterTitle,
|
|
206
208
|
continuability: () => continuability,
|
|
209
|
+
costText: () => costText,
|
|
207
210
|
countedSessionCost: () => countedSessionCost,
|
|
208
211
|
createAiService: () => createAiService,
|
|
209
212
|
createEmptyPlan: () => createEmptyPlan,
|
|
@@ -246,7 +249,9 @@ __export(index_exports, {
|
|
|
246
249
|
flattenTasksWithParents: () => flattenTasksWithParents,
|
|
247
250
|
followTaskLog: () => followTaskLog,
|
|
248
251
|
followTurn: () => followTurn,
|
|
252
|
+
formatCost: () => formatCost,
|
|
249
253
|
formatSearchOutput: () => formatSearchOutput,
|
|
254
|
+
formatTokenCount: () => formatTokenCount,
|
|
250
255
|
fromTranscript: () => fromTranscript,
|
|
251
256
|
getLatestSession: () => getLatestSession,
|
|
252
257
|
getProviderMeta: () => getProviderMeta,
|
|
@@ -298,6 +303,7 @@ __export(index_exports, {
|
|
|
298
303
|
migratePlanState: () => migratePlanState,
|
|
299
304
|
mintDaemonToken: () => mintDaemonToken,
|
|
300
305
|
mintSessionId: () => mintSessionId,
|
|
306
|
+
modelCostTitle: () => modelCostTitle,
|
|
301
307
|
namedRepos: () => namedRepos,
|
|
302
308
|
newTaskFields: () => newTaskFields,
|
|
303
309
|
normalizeAgentArgs: () => normalizeAgentArgs,
|
|
@@ -383,6 +389,7 @@ __export(index_exports, {
|
|
|
383
389
|
stopTurn: () => stopTurn,
|
|
384
390
|
stripAnsi: () => stripAnsi,
|
|
385
391
|
stripModelPrefix: () => stripModelPrefix,
|
|
392
|
+
subagentCostTitle: () => subagentCostTitle,
|
|
386
393
|
subtreeIds: () => subtreeIds,
|
|
387
394
|
summarizeToolCall: () => summarizeToolCall,
|
|
388
395
|
supervisorSummary: () => supervisorSummary,
|
|
@@ -401,6 +408,7 @@ __export(index_exports, {
|
|
|
401
408
|
taskStartedNotice: () => taskStartedNotice,
|
|
402
409
|
taskStatusKind: () => taskStatusKind,
|
|
403
410
|
taskUsageSummary: () => taskUsageSummary,
|
|
411
|
+
tasksTotalText: () => tasksTotalText,
|
|
404
412
|
textHasTaskOps: () => textHasTaskOps,
|
|
405
413
|
textHasTaskQuery: () => textHasTaskQuery,
|
|
406
414
|
titledRefs: () => titledRefs,
|
|
@@ -418,6 +426,7 @@ __export(index_exports, {
|
|
|
418
426
|
unsendAll: () => unsendAll,
|
|
419
427
|
unsendLatest: () => unsendLatest,
|
|
420
428
|
usageLine: () => usageLine,
|
|
429
|
+
usageText: () => usageText,
|
|
421
430
|
waitsForApproval: () => waitsForApproval,
|
|
422
431
|
walkPlan: () => walkPlan,
|
|
423
432
|
wellKnownBinDirs: () => wellKnownBinDirs,
|
|
@@ -880,25 +889,66 @@ function addUsage(totals, record) {
|
|
|
880
889
|
}
|
|
881
890
|
if (record.reportedCost) {
|
|
882
891
|
const { amount, currency } = record.reportedCost;
|
|
883
|
-
|
|
892
|
+
const provenance = record.reportedCost.kind || record.sessionBilling ? {
|
|
893
|
+
kind: record.reportedCost.kind ?? "unknown",
|
|
894
|
+
billing: record.sessionBilling?.type ?? "unknown",
|
|
895
|
+
...record.reportedCost.basis ? { basis: record.reportedCost.basis } : {}
|
|
896
|
+
} : void 0;
|
|
897
|
+
addCost(next, currency, amount, provenance);
|
|
884
898
|
}
|
|
885
899
|
return next;
|
|
886
900
|
}
|
|
901
|
+
function addCost(totals, currency, amount, provenance) {
|
|
902
|
+
const hadCost = totals.reportedCost?.[currency] !== void 0;
|
|
903
|
+
const previous = totals.costProvenance?.[currency];
|
|
904
|
+
totals.reportedCost = { ...totals.reportedCost, [currency]: (totals.reportedCost?.[currency] ?? 0) + amount };
|
|
905
|
+
if (!previous && !provenance) return;
|
|
906
|
+
const unknown = { kind: "unknown", billing: "unknown" };
|
|
907
|
+
const a = previous ?? unknown;
|
|
908
|
+
const b = provenance ?? unknown;
|
|
909
|
+
const merged = !hadCost ? b : {
|
|
910
|
+
kind: a.kind === "estimate" || b.kind === "estimate" ? "estimate" : a.kind === "billed" && b.kind === "billed" ? "billed" : "unknown",
|
|
911
|
+
billing: a.billing === b.billing ? a.billing : "mixed",
|
|
912
|
+
...a.basis && a.basis === b.basis ? { basis: a.basis } : {}
|
|
913
|
+
};
|
|
914
|
+
totals.costProvenance = { ...totals.costProvenance, [currency]: merged };
|
|
915
|
+
}
|
|
887
916
|
function addPlannerUsage(usage, record) {
|
|
888
|
-
|
|
917
|
+
if (record.compacted) {
|
|
918
|
+
if (record.subagentId || record.aggregate) return usage;
|
|
919
|
+
const next2 = { ...usage };
|
|
920
|
+
if (record.source !== void 0) next2.source = record.source;
|
|
921
|
+
if (record.model !== void 0) {
|
|
922
|
+
if (record.source !== usage.source || record.model !== usage.model) delete next2.contextWindow;
|
|
923
|
+
next2.model = record.model;
|
|
924
|
+
}
|
|
925
|
+
if (record.contextTokens !== void 0) next2.lastPromptTokens = record.contextTokens;
|
|
926
|
+
else delete next2.lastPromptTokens;
|
|
927
|
+
if (record.contextWindow !== void 0) next2.contextWindow = record.contextWindow > 0 ? record.contextWindow : void 0;
|
|
928
|
+
return next2;
|
|
929
|
+
}
|
|
930
|
+
if (record.sessionBilling && !record.reportedCost && record.inputTokens === void 0 && record.outputTokens === void 0 && record.cachedInputTokens === void 0 && record.contextWindow === void 0) {
|
|
931
|
+
return record.subagentId ? usage : { ...usage, sessionBilling: record.sessionBilling, source: record.source };
|
|
932
|
+
}
|
|
933
|
+
record = { ...record, sessionBilling: record.sessionBilling ?? (usage.source === void 0 || record.source === usage.source ? usage.sessionBilling : void 0) };
|
|
934
|
+
const next = { ...usage, ...!record.subagentId && record.sessionBilling ? { sessionBilling: record.sessionBilling } : {}, totals: addUsage(usage.totals, record), byModel: addModelUsage(usage.byModel ?? [], record) };
|
|
889
935
|
if (record.subagentId) {
|
|
890
936
|
next.bySubagent = { ...usage.bySubagent, [record.subagentId]: addUsage(usage.bySubagent?.[record.subagentId] ?? {}, record) };
|
|
891
937
|
return next;
|
|
892
938
|
}
|
|
893
|
-
if (
|
|
894
|
-
if (record.source !== usage.source
|
|
895
|
-
|
|
896
|
-
delete next.contextWindow;
|
|
897
|
-
}
|
|
898
|
-
next.source = record.source;
|
|
899
|
-
next.model = record.model;
|
|
939
|
+
if (record.aggregate) {
|
|
940
|
+
if (record.source !== usage.source && !record.sessionBilling) delete next.sessionBilling;
|
|
941
|
+
return next;
|
|
900
942
|
}
|
|
901
|
-
if (record.
|
|
943
|
+
if (record.source !== usage.source || record.model !== usage.model) {
|
|
944
|
+
delete next.lastPromptTokens;
|
|
945
|
+
delete next.contextWindow;
|
|
946
|
+
}
|
|
947
|
+
if (record.source !== usage.source && !record.sessionBilling) delete next.sessionBilling;
|
|
948
|
+
next.source = record.source;
|
|
949
|
+
next.model = record.model;
|
|
950
|
+
const used = record.contextTokens ?? record.inputTokens;
|
|
951
|
+
if (used !== void 0) next.lastPromptTokens = used;
|
|
902
952
|
if (record.contextWindow !== void 0) next.contextWindow = record.contextWindow > 0 ? record.contextWindow : void 0;
|
|
903
953
|
return next;
|
|
904
954
|
}
|
|
@@ -909,6 +959,7 @@ function usageLine(usage) {
|
|
|
909
959
|
const contextFill = plannerContextFill(usage);
|
|
910
960
|
return {
|
|
911
961
|
totals: usage.totals,
|
|
962
|
+
...usage.sessionBilling ? { sessionBilling: usage.sessionBilling } : {},
|
|
912
963
|
...usage.byModel ? { byModel: usage.byModel } : {},
|
|
913
964
|
...usage.model ? { model: usage.model } : {},
|
|
914
965
|
...usage.bySubagent ? { bySubagent: usage.bySubagent } : {},
|
|
@@ -916,8 +967,8 @@ function usageLine(usage) {
|
|
|
916
967
|
};
|
|
917
968
|
}
|
|
918
969
|
function plannerContextFill(usage) {
|
|
919
|
-
if (usage.lastPromptTokens === void 0
|
|
920
|
-
return { usedTokens: usage.lastPromptTokens, windowTokens: usage.contextWindow };
|
|
970
|
+
if (usage.lastPromptTokens === void 0) return void 0;
|
|
971
|
+
return { usedTokens: usage.lastPromptTokens, ...usage.contextWindow !== void 0 && usage.contextWindow > 0 ? { windowTokens: usage.contextWindow } : {} };
|
|
921
972
|
}
|
|
922
973
|
function addModelUsage(models, record) {
|
|
923
974
|
const index = models.findIndex((m) => m.source === record.source && m.model === record.model);
|
|
@@ -929,13 +980,14 @@ function startAttemptUsage(attempt, runner, startedAt) {
|
|
|
929
980
|
}
|
|
930
981
|
function addAttemptUsage(usage, record, now) {
|
|
931
982
|
const folded = addPlannerUsage(usage, record);
|
|
932
|
-
return { ...usage, ...folded, lastPromptTokens: folded.lastPromptTokens, contextWindow: folded.contextWindow, wallTimeMs: Math.max(0, Date.parse(now) - Date.parse(usage.startedAt)) };
|
|
983
|
+
return { ...usage, ...folded, lastPromptTokens: folded.lastPromptTokens, contextWindow: folded.contextWindow, wallTimeMs: Math.max(0, Date.parse(usage.endedAt ?? now) - Date.parse(usage.startedAt)) };
|
|
933
984
|
}
|
|
934
985
|
function endAttemptUsage(usage, now, runnerSessionCost, sessionId) {
|
|
986
|
+
const endedAt = usage.endedAt ?? now;
|
|
935
987
|
return {
|
|
936
988
|
...usage,
|
|
937
|
-
endedAt
|
|
938
|
-
wallTimeMs: Math.max(0, Date.parse(
|
|
989
|
+
endedAt,
|
|
990
|
+
wallTimeMs: Math.max(0, Date.parse(endedAt) - Date.parse(usage.startedAt)),
|
|
939
991
|
...sessionId ? { sessionId } : {},
|
|
940
992
|
...runnerSessionCost ? { runnerSessionCost } : {}
|
|
941
993
|
};
|
|
@@ -948,8 +1000,8 @@ function resumedSessionWindow(attempts, runner, attempt, sessionId) {
|
|
|
948
1000
|
return last ? { source: last.source, model: last.model, contextWindow: last.contextWindow } : void 0;
|
|
949
1001
|
}
|
|
950
1002
|
function addUsageTotals(totals, addition) {
|
|
951
|
-
|
|
952
|
-
for (const [currency, amount] of Object.entries(addition.reportedCost ?? {}))
|
|
1003
|
+
const next = addUsage(totals, { source: "", inputTokens: addition.inputTokens, outputTokens: addition.outputTokens, cachedInputTokens: addition.cachedInputTokens });
|
|
1004
|
+
for (const [currency, amount] of Object.entries(addition.reportedCost ?? {})) addCost(next, currency, amount, addition.costProvenance?.[currency]);
|
|
953
1005
|
return next;
|
|
954
1006
|
}
|
|
955
1007
|
function planUsage(tasks) {
|
|
@@ -984,6 +1036,71 @@ function taskUsageSummary(task) {
|
|
|
984
1036
|
...contextFill ? { contextFill } : {}
|
|
985
1037
|
};
|
|
986
1038
|
}
|
|
1039
|
+
function formatTokenCount(n) {
|
|
1040
|
+
if (n < 1e3) return String(n);
|
|
1041
|
+
if (n < 1e4) return `${(n / 1e3).toFixed(1).replace(/\.0$/, "")}k`;
|
|
1042
|
+
if (n < 999500) return `${Math.round(n / 1e3)}k`;
|
|
1043
|
+
return `${(n / 1e6).toFixed(1).replace(/\.0$/, "")}M`;
|
|
1044
|
+
}
|
|
1045
|
+
function formatCost(currency, amount) {
|
|
1046
|
+
const value = amount >= 1 ? amount.toFixed(2) : (([whole, frac = ""]) => `${whole}.${frac.padEnd(2, "0")}`)(amount.toFixed(4).replace(/0+$/, "").split("."));
|
|
1047
|
+
return currency.toLowerCase() === "usd" ? `$${value}` : `${value} ${currency.toUpperCase()}`;
|
|
1048
|
+
}
|
|
1049
|
+
function contextMeterText(fill) {
|
|
1050
|
+
if (!fill) return null;
|
|
1051
|
+
const used = `ctx ${formatTokenCount(fill.usedTokens)}`;
|
|
1052
|
+
if (!fill.windowTokens) return used;
|
|
1053
|
+
const share = fill.usedTokens / fill.windowTokens * 100;
|
|
1054
|
+
return `${used} (${share < 1 ? "<1" : Math.round(share)}%)`;
|
|
1055
|
+
}
|
|
1056
|
+
function contextMeterTitle(fill) {
|
|
1057
|
+
const used = fill.usedTokens.toLocaleString("en-US");
|
|
1058
|
+
return fill.windowTokens ? `${used} of ${fill.windowTokens.toLocaleString("en-US")} tokens` : `${used} tokens`;
|
|
1059
|
+
}
|
|
1060
|
+
function costParts(totals) {
|
|
1061
|
+
const rank = (currency) => currency.toLowerCase() === "usd" ? "" : currency.toUpperCase();
|
|
1062
|
+
return Object.entries(totals.reportedCost ?? {}).sort(([a], [b]) => rank(a).localeCompare(rank(b))).map(([currency, amount]) => {
|
|
1063
|
+
const provenance = totals.costProvenance?.[currency];
|
|
1064
|
+
const equivalent = provenance?.billing === "subscription";
|
|
1065
|
+
return `${provenance?.kind === "estimate" || equivalent ? "\u2248" : ""}${formatCost(currency, amount)}${equivalent ? " API-equivalent" : ""}`;
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
function costText(totals) {
|
|
1069
|
+
const parts = costParts(totals);
|
|
1070
|
+
return parts.length > 0 ? parts.join(" \xB7 ") : null;
|
|
1071
|
+
}
|
|
1072
|
+
function usageText(usage) {
|
|
1073
|
+
const parts = [contextMeterText(usage.contextFill), costText(usage.totals)].filter((p) => p !== null);
|
|
1074
|
+
return parts.length > 0 ? parts.join(" \xB7 ") : null;
|
|
1075
|
+
}
|
|
1076
|
+
function modelCostTitle(byModel) {
|
|
1077
|
+
const lines2 = byModel.flatMap((m) => {
|
|
1078
|
+
const cost = costParts(m.totals);
|
|
1079
|
+
const descriptions = [...new Set(Object.values(m.totals.costProvenance ?? {}).map((p) => {
|
|
1080
|
+
const source = m.source === "claude-code" ? "Claude Code" : m.source === "opencode" ? "OpenCode" : m.source === "openrouter" ? "OpenRouter" : m.source;
|
|
1081
|
+
const basis = p.basis === "list" ? " at list price" : p.basis === "managed" ? " at managed price" : "";
|
|
1082
|
+
const kind = p.kind === "estimate" ? `${source}'s estimate${basis}` : p.kind === "billed" ? `Amount billed by ${source}` : "Reported cost";
|
|
1083
|
+
return `${kind}${p.billing === "subscription" ? "; not billed on a subscription" : ""}`;
|
|
1084
|
+
}))];
|
|
1085
|
+
return cost.length > 0 ? [`${m.source} / ${m.model ?? "unattributed"}: ${cost.join(" \xB7 ")}${descriptions.length ? ` \u2014 ${descriptions.join("; ")}` : ""}`] : [];
|
|
1086
|
+
});
|
|
1087
|
+
return lines2.length > 0 ? lines2.join("\n") : void 0;
|
|
1088
|
+
}
|
|
1089
|
+
function subagentCostTitle(bySubagent) {
|
|
1090
|
+
return Object.entries(bySubagent).map(([id, totals]) => `${id}: ${costParts(totals).join(" \xB7 ") || "reported no cost"}`).join("\n");
|
|
1091
|
+
}
|
|
1092
|
+
function runnersOf(tasks) {
|
|
1093
|
+
return tasks.flatMap((task) => [...task.attemptUsage ?? [], ...runnersOf(task.subtasks ?? [])]);
|
|
1094
|
+
}
|
|
1095
|
+
function tasksTotalText(tasks) {
|
|
1096
|
+
const attempts = runnersOf(tasks);
|
|
1097
|
+
if (attempts.length === 0) return null;
|
|
1098
|
+
const costed = (a) => Object.keys(a.totals.reportedCost ?? {}).length > 0;
|
|
1099
|
+
const costless = [...new Set(attempts.filter((a) => a.endedAt !== void 0 && isMeasured(a.totals)).map((a) => a.runner))].filter((runner) => !attempts.some((a) => a.runner === runner && costed(a)));
|
|
1100
|
+
const note = costless.length === 0 ? [] : [`${costless.length === 1 ? costless[0] : `${costless.slice(0, -1).join(", ")} and ${costless.at(-1)}`} ${costless.length === 1 ? "reports" : "report"} no cost`];
|
|
1101
|
+
const parts = [...costParts(planUsage(tasks).totals), ...note];
|
|
1102
|
+
return parts.length > 0 ? `Tasks total: ${parts.join(" \xB7 ")}` : null;
|
|
1103
|
+
}
|
|
987
1104
|
|
|
988
1105
|
// src/models/TaskLog.ts
|
|
989
1106
|
var HEAD_LINES = 60;
|
|
@@ -7523,7 +7640,7 @@ var RunnerApprovals = class {
|
|
|
7523
7640
|
},
|
|
7524
7641
|
stop: (sessionId) => {
|
|
7525
7642
|
this.denySession(sessionId);
|
|
7526
|
-
runner.stop(sessionId);
|
|
7643
|
+
return runner.stop(sessionId);
|
|
7527
7644
|
},
|
|
7528
7645
|
stopAll: () => {
|
|
7529
7646
|
this.approvals.clear(isRunnerApproval);
|
|
@@ -11628,6 +11745,14 @@ function canUseToolAnswer({ input, suggestions }, decision) {
|
|
|
11628
11745
|
}
|
|
11629
11746
|
var MODEL_COMMAND = "model";
|
|
11630
11747
|
var SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["Agent", "Task"]);
|
|
11748
|
+
var VARIANT_SUFFIX = /^(.*?)(\[[^\]]*\])?$/;
|
|
11749
|
+
function sameModel(a, b) {
|
|
11750
|
+
if (a === void 0 || b === void 0) return false;
|
|
11751
|
+
if (a === b) return true;
|
|
11752
|
+
const [, aBase, aVariant] = VARIANT_SUFFIX.exec(a) ?? [];
|
|
11753
|
+
const [, bBase, bVariant] = VARIANT_SUFFIX.exec(b) ?? [];
|
|
11754
|
+
return aBase === bBase && (aVariant === void 0 || bVariant === void 0 || aVariant === bVariant);
|
|
11755
|
+
}
|
|
11631
11756
|
function usageRecord(usage, model, subagentId) {
|
|
11632
11757
|
const record = {
|
|
11633
11758
|
source: "claude-code",
|
|
@@ -11636,6 +11761,7 @@ function usageRecord(usage, model, subagentId) {
|
|
|
11636
11761
|
if (model) record.model = model;
|
|
11637
11762
|
if (usage.output_tokens !== void 0) record.outputTokens = usage.output_tokens;
|
|
11638
11763
|
if (subagentId) record.subagentId = subagentId;
|
|
11764
|
+
else if (record.inputTokens !== void 0) record.contextTokens = record.inputTokens + (record.outputTokens ?? 0);
|
|
11639
11765
|
return record;
|
|
11640
11766
|
}
|
|
11641
11767
|
function subagentFinalCall(result) {
|
|
@@ -11685,16 +11811,27 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
11685
11811
|
replyRuns = new ReplyRuns();
|
|
11686
11812
|
/** The model answering the planner's current message, and the message's id, as its `message_start` named them. */
|
|
11687
11813
|
plannerModel;
|
|
11814
|
+
/**
|
|
11815
|
+
* The id the turn runs as, as `init` names it. A `[1m]` variant is the
|
|
11816
|
+
* selected id, while a message names the API's canonical id without it.
|
|
11817
|
+
*/
|
|
11818
|
+
sessionModel;
|
|
11819
|
+
billing;
|
|
11688
11820
|
streamingMessage;
|
|
11689
11821
|
/** The message the last `assistant` line was part of, and how many of its blocks the lines have carried. */
|
|
11690
11822
|
lineMessage = null;
|
|
11691
11823
|
/**
|
|
11692
|
-
*
|
|
11693
|
-
*
|
|
11694
|
-
*
|
|
11824
|
+
* What has been counted of the session's running cost: a resume starts from
|
|
11825
|
+
* it, and it is undefined without a kept count — the next result then only
|
|
11826
|
+
* sets the baseline, since the CLI restores spend from turns before it. It
|
|
11827
|
+
* grows by what each result is counted for, never by a total that includes
|
|
11828
|
+
* cost the runner marked unknown, so that spend can be counted when its basis
|
|
11829
|
+
* is reported; and it never lowers, since counted dollars are not un-spent.
|
|
11695
11830
|
*/
|
|
11696
11831
|
reportedCostUsd = 0;
|
|
11697
11832
|
reportedModelCostUsd = /* @__PURE__ */ new Map();
|
|
11833
|
+
/** What this turn's streamed calls already reported, so its result only adds what did not stream. */
|
|
11834
|
+
turnCallUsage = { inputTokens: 0, cachedInputTokens: 0, outputTokens: 0 };
|
|
11698
11835
|
/**
|
|
11699
11836
|
* Subagents started and not yet finished, keyed by the `Agent` call's id.
|
|
11700
11837
|
* Kept across turns: a backgrounded one reports after its turn has ended.
|
|
@@ -11781,6 +11918,14 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
11781
11918
|
if (!sessionId || this.reportedCostUsd === void 0) return null;
|
|
11782
11919
|
return { sessionId, usd: this.reportedCostUsd, modelUsd: Object.fromEntries(this.reportedModelCostUsd) };
|
|
11783
11920
|
}
|
|
11921
|
+
/**
|
|
11922
|
+
* A cleared conversation's totals start again from zero, so what follows is
|
|
11923
|
+
* all new spend, even for a resume that had no count kept.
|
|
11924
|
+
*/
|
|
11925
|
+
restartCount() {
|
|
11926
|
+
this.reportedCostUsd = 0;
|
|
11927
|
+
this.reportedModelCostUsd = /* @__PURE__ */ new Map();
|
|
11928
|
+
}
|
|
11784
11929
|
/** A count kept for another session says nothing of this one's. */
|
|
11785
11930
|
countFrom(sessionId, counted) {
|
|
11786
11931
|
const known = counted?.sessionId === sessionId ? counted : void 0;
|
|
@@ -11854,11 +11999,10 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
11854
11999
|
* the alias resolves to now; that id is pinned before the first turn.
|
|
11855
12000
|
*/
|
|
11856
12001
|
async handshake(opts) {
|
|
11857
|
-
if (opts.kind !== "task") return;
|
|
11858
12002
|
const initialized = (await this.control({ subtype: "initialize" }, MODEL_PIN_TIMEOUT_MS))?.response;
|
|
11859
12003
|
this.listedCommands = initialized?.commands ?? [];
|
|
11860
12004
|
this.listedModels = initialized?.models ?? [];
|
|
11861
|
-
if (opts.flags.permissionMode !== "plan" || !opts.model) return;
|
|
12005
|
+
if (opts.kind !== "task" || opts.flags.permissionMode !== "plan" || !opts.model) return;
|
|
11862
12006
|
const resolved = initialized?.models?.find((m) => m.value === opts.model)?.resolvedModel;
|
|
11863
12007
|
if (resolved && resolved !== opts.model) await this.control({ subtype: "set_model", model: resolved }, MODEL_PIN_TIMEOUT_MS);
|
|
11864
12008
|
}
|
|
@@ -11899,6 +12043,15 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
11899
12043
|
this.interruptRequested = true;
|
|
11900
12044
|
return (await this.control({ subtype: "interrupt" }, timeoutMs))?.subtype === "success";
|
|
11901
12045
|
}
|
|
12046
|
+
/**
|
|
12047
|
+
* On stdin's end the CLI exits by itself once its turn has closed, and only
|
|
12048
|
+
* an exit of its own saves the session's running totals for a `--resume` to
|
|
12049
|
+
* restore (checked on 2.1.303: about a second later, code 0, or 1 after an
|
|
12050
|
+
* interrupted turn).
|
|
12051
|
+
*/
|
|
12052
|
+
endInput() {
|
|
12053
|
+
this.process?.stdin?.end();
|
|
12054
|
+
}
|
|
11902
12055
|
/**
|
|
11903
12056
|
* A `user` line written mid-turn is Claude Code's own queue (ADR-0023): the
|
|
11904
12057
|
* CLI attaches it to the next tool result, or runs it as a turn of its own
|
|
@@ -11986,6 +12139,7 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
11986
12139
|
this.resultHeld = false;
|
|
11987
12140
|
this.turnClosed = false;
|
|
11988
12141
|
this.clearFollowOnTimer();
|
|
12142
|
+
this.plannerModel = void 0;
|
|
11989
12143
|
}
|
|
11990
12144
|
/**
|
|
11991
12145
|
* The echo of a steered message: the model has it. After a closed turn it
|
|
@@ -12040,6 +12194,8 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
12040
12194
|
this.permissions.withdraw(msg.request_id, emit);
|
|
12041
12195
|
return;
|
|
12042
12196
|
case "control_response": {
|
|
12197
|
+
const account = msg.response?.response?.account;
|
|
12198
|
+
if (account) this.reportBilling(account.apiKeySource, account.subscriptionType, emit);
|
|
12043
12199
|
const requestId = msg.response?.request_id;
|
|
12044
12200
|
if (requestId) this.pendingControl.get(requestId)?.(msg.response ?? null);
|
|
12045
12201
|
return;
|
|
@@ -12083,12 +12239,24 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
12083
12239
|
emit({ type: "tool_result", id, name: "", output: diff || output, success: block.is_error !== true, subagentId });
|
|
12084
12240
|
}
|
|
12085
12241
|
return;
|
|
12242
|
+
// `/clear` (and `/reset`, `/new`) emit this top-level line before their
|
|
12243
|
+
// own result, and a task's chat can send them. The CLI's totals restart.
|
|
12244
|
+
case "conversation_reset": {
|
|
12245
|
+
if (subagentId) return;
|
|
12246
|
+
this.restartCount();
|
|
12247
|
+
const resetModel = this.sessionModel ?? (this.plannerModel !== "<synthetic>" ? this.plannerModel : void 0);
|
|
12248
|
+
emit({ type: "usage", record: { source: "claude-code", ...resetModel ? { model: resetModel } : {}, compacted: true } });
|
|
12249
|
+
return;
|
|
12250
|
+
}
|
|
12086
12251
|
case "system":
|
|
12252
|
+
if (subagentId && (msg.subtype === "init" || msg.subtype === "compact_boundary")) return;
|
|
12087
12253
|
if (msg.subtype === "init") {
|
|
12088
12254
|
this.clearFollowOnTimer();
|
|
12089
12255
|
if (this.refuseOtherMode(msg, emit)) return;
|
|
12256
|
+
if (msg.model && msg.model !== "<synthetic>") this.sessionModel = msg.model;
|
|
12090
12257
|
this.readLoaded(msg, emit);
|
|
12091
12258
|
this.followModel(msg.model, emit);
|
|
12259
|
+
this.reportBilling(msg.apiKeySource, void 0, emit);
|
|
12092
12260
|
}
|
|
12093
12261
|
if (msg.subtype === "commands_changed" && Array.isArray(msg.commands)) this.listedCommands = msg.commands;
|
|
12094
12262
|
if (msg.subtype === "compact_boundary") {
|
|
@@ -12099,6 +12267,8 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
12099
12267
|
...typeof meta?.pre_tokens === "number" ? { preTokens: meta.pre_tokens } : {},
|
|
12100
12268
|
...typeof meta?.post_tokens === "number" ? { postTokens: meta.post_tokens } : {}
|
|
12101
12269
|
});
|
|
12270
|
+
const model = this.sessionModel ?? (this.plannerModel !== "<synthetic>" ? this.plannerModel : void 0);
|
|
12271
|
+
emit({ type: "usage", record: { source: "claude-code", ...model ? { model } : {}, compacted: true, ...typeof meta?.post_tokens === "number" ? { contextTokens: meta.post_tokens } : {} } });
|
|
12102
12272
|
}
|
|
12103
12273
|
if (msg.subtype === "background_tasks_changed" && Array.isArray(msg.tasks)) {
|
|
12104
12274
|
this.backgroundTaskCount = msg.tasks.length;
|
|
@@ -12110,6 +12280,7 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
12110
12280
|
}
|
|
12111
12281
|
return;
|
|
12112
12282
|
case "result":
|
|
12283
|
+
if (subagentId) return;
|
|
12113
12284
|
this.reportSessionCost(msg, emit);
|
|
12114
12285
|
for (const command of this.modelCommands) command.turnsLeft = Math.max(0, command.turnsLeft - 1);
|
|
12115
12286
|
if (this.interruptRequested && (msg.is_error || msg.subtype !== "success")) {
|
|
@@ -12274,16 +12445,69 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
12274
12445
|
*/
|
|
12275
12446
|
handleStreamEvent(event, emit) {
|
|
12276
12447
|
if (event.type === "message_start") {
|
|
12277
|
-
this.plannerModel = event.message?.model;
|
|
12448
|
+
this.plannerModel = this.modelId(event.message?.model);
|
|
12278
12449
|
this.streamingMessage = event.message?.id;
|
|
12279
12450
|
this.followModel(this.plannerModel, emit);
|
|
12280
|
-
} else if (event.type === "message_delta" && event.usage)
|
|
12281
|
-
|
|
12451
|
+
} else if (event.type === "message_delta" && event.usage) {
|
|
12452
|
+
const record = usageRecord(event.usage, this.plannerModel);
|
|
12453
|
+
this.turnCallUsage.inputTokens += record.inputTokens ?? 0;
|
|
12454
|
+
this.turnCallUsage.cachedInputTokens += record.cachedInputTokens ?? 0;
|
|
12455
|
+
this.turnCallUsage.outputTokens += record.outputTokens ?? 0;
|
|
12456
|
+
emit({ type: "usage", record });
|
|
12457
|
+
} else if (event.type === "content_block_delta" && event.delta?.type === "text_delta" && event.delta.text) {
|
|
12282
12458
|
this.replyRuns.delta(`${this.streamingMessage ?? ""}:${event.index ?? 0}`, event.delta.text, emit);
|
|
12283
12459
|
} else if (event.type === "content_block_delta" && event.delta?.type === "thinking_delta" && event.delta.thinking) {
|
|
12284
12460
|
emit({ type: "thinking_delta", text: event.delta.thinking });
|
|
12285
12461
|
}
|
|
12286
12462
|
}
|
|
12463
|
+
/**
|
|
12464
|
+
* The id a reported model runs as. `init` names the selected value — a
|
|
12465
|
+
* `[1m]` variant included — while a message names the API's canonical id
|
|
12466
|
+
* without it. Keeping the variant is what lets the meter find the window
|
|
12467
|
+
* `modelUsage` keys it by, and keeps that window off the base model.
|
|
12468
|
+
*/
|
|
12469
|
+
modelId(reported) {
|
|
12470
|
+
if (!reported) return void 0;
|
|
12471
|
+
if (!sameModel(this.sessionModel, reported)) return reported;
|
|
12472
|
+
return this.sessionModel?.includes("[") ? this.sessionModel : reported;
|
|
12473
|
+
}
|
|
12474
|
+
reportBilling(apiKeySource, plan, emit) {
|
|
12475
|
+
const type = apiKeySource ? apiKeySource === "none" ? "subscription" : "api-key" : plan ? "subscription" : void 0;
|
|
12476
|
+
if (!type) return;
|
|
12477
|
+
const knownPlan = plan ?? (this.billing?.type === type ? this.billing.plan : void 0);
|
|
12478
|
+
this.billing = { type, ...knownPlan ? { plan: knownPlan } : {} };
|
|
12479
|
+
emit({ type: "usage", record: { source: this.agentId, aggregate: true, sessionBilling: this.billing } });
|
|
12480
|
+
}
|
|
12481
|
+
/**
|
|
12482
|
+
* A turn's `result` states its own token sum. Every call that streamed its
|
|
12483
|
+
* `message_delta` usage has already reported, so only the remainder is added:
|
|
12484
|
+
* all of it when nothing streamed (a recording without partial messages), or
|
|
12485
|
+
* a lost call's share. It is a sum across calls, marked `aggregate` so it
|
|
12486
|
+
* never moves the meter — only a call says what the context was left at.
|
|
12487
|
+
*/
|
|
12488
|
+
reportUnstreamedUsage(msg, emit) {
|
|
12489
|
+
const { inputTokens: streamedInput, cachedInputTokens: streamedCached, outputTokens: streamedOutput } = this.turnCallUsage;
|
|
12490
|
+
this.turnCallUsage = { inputTokens: 0, cachedInputTokens: 0, outputTokens: 0 };
|
|
12491
|
+
if (!msg.usage) return;
|
|
12492
|
+
const models = Object.keys(msg.modelUsage ?? {});
|
|
12493
|
+
const model = this.plannerModel ?? this.mainModel ?? (models.length === 1 ? models[0] : void 0);
|
|
12494
|
+
const total = usageRecord(msg.usage, model);
|
|
12495
|
+
const inputTokens = (total.inputTokens ?? 0) - streamedInput;
|
|
12496
|
+
const cachedInputTokens = (total.cachedInputTokens ?? 0) - streamedCached;
|
|
12497
|
+
const outputTokens = (total.outputTokens ?? 0) - streamedOutput;
|
|
12498
|
+
if (inputTokens <= 0 && outputTokens <= 0) return;
|
|
12499
|
+
emit({
|
|
12500
|
+
type: "usage",
|
|
12501
|
+
record: {
|
|
12502
|
+
source: "claude-code",
|
|
12503
|
+
...model ? { model } : {},
|
|
12504
|
+
aggregate: true,
|
|
12505
|
+
...inputTokens > 0 ? { inputTokens } : {},
|
|
12506
|
+
...inputTokens > 0 && cachedInputTokens > 0 ? { cachedInputTokens } : {},
|
|
12507
|
+
...outputTokens > 0 ? { outputTokens } : {}
|
|
12508
|
+
}
|
|
12509
|
+
});
|
|
12510
|
+
}
|
|
12287
12511
|
/**
|
|
12288
12512
|
* What only the result line knows: the cost, which covers every call the
|
|
12289
12513
|
* session made — subagents included, since none of their lines carries one —
|
|
@@ -12292,24 +12516,54 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
12292
12516
|
* the session before; with no count kept, the first turn only sets the
|
|
12293
12517
|
* baseline: its total includes turns counted before, and a turn's own share
|
|
12294
12518
|
* cannot be told apart from them.
|
|
12519
|
+
*
|
|
12520
|
+
* The running total never lowers: money already reported and counted is not
|
|
12521
|
+
* un-spent because a later or resumed process remembers less of it, and
|
|
12522
|
+
* counting its return would count the same dollars twice. A zero is not a
|
|
12523
|
+
* measurement at all — a crash can end with one — and only `/clear` restarts
|
|
12524
|
+
* the count, through {@link restartCount}. Per-model baselines never lower
|
|
12525
|
+
* either, for the same reason.
|
|
12526
|
+
*
|
|
12527
|
+
* A report without a model breakdown is counted as a session total; one with
|
|
12528
|
+
* a breakdown is counted per model. A breakdown that would count more than
|
|
12529
|
+
* the session total's own growth (the count before it had no model split, or
|
|
12530
|
+
* a lossy report) is counted as that growth alone, so the two shapes
|
|
12531
|
+
* together still add each dollar once.
|
|
12295
12532
|
*/
|
|
12296
12533
|
reportSessionCost(msg, emit) {
|
|
12534
|
+
this.reportUnstreamedUsage(msg, emit);
|
|
12297
12535
|
const total = msg.total_cost_usd;
|
|
12298
12536
|
const canReport = this.reportedCostUsd !== void 0;
|
|
12299
|
-
const modelCosts = Object.entries(msg.modelUsage ?? {}).filter((entry) => typeof entry[1].costUSD === "number");
|
|
12537
|
+
const modelCosts = Object.entries(msg.modelUsage ?? {}).filter((entry) => typeof entry[1].costUSD === "number" && entry[1].costUSD > 0 && entry[1].costBasis !== "unknown");
|
|
12538
|
+
const growth = canReport && typeof total === "number" && total > 0 ? total - this.reportedCostUsd : void 0;
|
|
12539
|
+
let emitted = 0;
|
|
12540
|
+
const emitCost = (record) => {
|
|
12541
|
+
if (!(record.amount > 0)) return;
|
|
12542
|
+
emitted += record.amount;
|
|
12543
|
+
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 } : {} } } });
|
|
12544
|
+
};
|
|
12300
12545
|
if (modelCosts.length > 0) {
|
|
12301
|
-
|
|
12302
|
-
|
|
12303
|
-
|
|
12304
|
-
|
|
12305
|
-
|
|
12546
|
+
const modelGrowth = modelCosts.reduce((sum, [model2, usage]) => sum + Math.max(0, usage.costUSD - (this.reportedModelCostUsd.get(model2) ?? 0)), 0);
|
|
12547
|
+
if (canReport) {
|
|
12548
|
+
if (growth !== void 0 && modelGrowth > growth + 1e-9) {
|
|
12549
|
+
emitCost({ amount: growth });
|
|
12550
|
+
} else {
|
|
12551
|
+
for (const [model2, usage] of modelCosts) emitCost({ model: model2, amount: usage.costUSD - (this.reportedModelCostUsd.get(model2) ?? 0), ...usage.costBasis ? { basis: usage.costBasis } : {} });
|
|
12552
|
+
}
|
|
12306
12553
|
}
|
|
12307
|
-
|
|
12308
|
-
|
|
12554
|
+
for (const [model2, usage] of modelCosts) this.reportedModelCostUsd.set(model2, Math.max(this.reportedModelCostUsd.get(model2) ?? 0, usage.costUSD));
|
|
12555
|
+
} else if (!Object.values(msg.modelUsage ?? {}).some((usage) => usage.costBasis === "unknown") && typeof total === "number" && total > 0 && canReport && total >= this.reportedCostUsd) {
|
|
12556
|
+
emitCost({ amount: total - this.reportedCostUsd });
|
|
12557
|
+
}
|
|
12558
|
+
if (!canReport) {
|
|
12559
|
+
if (typeof total === "number" && total > 0) this.reportedCostUsd = total;
|
|
12560
|
+
else if (modelCosts.length > 0) this.reportedCostUsd = modelCosts.reduce((sum, [, usage]) => sum + usage.costUSD, 0);
|
|
12561
|
+
} else {
|
|
12562
|
+
this.reportedCostUsd = (this.reportedCostUsd ?? 0) + emitted;
|
|
12309
12563
|
}
|
|
12310
|
-
|
|
12311
|
-
const contextWindow =
|
|
12312
|
-
if (contextWindow
|
|
12564
|
+
const model = this.plannerModel && this.plannerModel !== "<synthetic>" ? this.plannerModel : this.sessionModel;
|
|
12565
|
+
const contextWindow = model ? msg.modelUsage?.[model]?.contextWindow : void 0;
|
|
12566
|
+
if (typeof contextWindow === "number" && contextWindow > 0) emit({ type: "usage", record: { source: "claude-code", model, contextWindow } });
|
|
12313
12567
|
}
|
|
12314
12568
|
};
|
|
12315
12569
|
|
|
@@ -12944,9 +13198,13 @@ function mergeCodexRateLimits(known, params) {
|
|
|
12944
13198
|
const id = snapshot.limitId ?? "";
|
|
12945
13199
|
const before = known.get(id);
|
|
12946
13200
|
const next = new Map(known);
|
|
12947
|
-
next.set(id, { limitId: id, primary: snapshot.primary ?? before?.primary, secondary: snapshot.secondary ?? before?.secondary });
|
|
13201
|
+
next.set(id, { limitId: id, planType: snapshot.planType ?? before?.planType, primary: snapshot.primary ?? before?.primary, secondary: snapshot.secondary ?? before?.secondary });
|
|
12948
13202
|
return next;
|
|
12949
13203
|
}
|
|
13204
|
+
function codexSessionBilling(limits) {
|
|
13205
|
+
const plan = [...limits.values()].find((snapshot) => snapshot.planType && snapshot.planType !== "unknown")?.planType;
|
|
13206
|
+
return plan ? { type: "subscription", plan } : void 0;
|
|
13207
|
+
}
|
|
12950
13208
|
function isCodexUsageLimit(error) {
|
|
12951
13209
|
const info = error?.codexErrorInfo;
|
|
12952
13210
|
if (info === void 0 || info === null || info === "other") return void 0;
|
|
@@ -13505,8 +13763,21 @@ ${this.exitMessage()}`);
|
|
|
13505
13763
|
case "thread/tokenUsage/updated":
|
|
13506
13764
|
this.emitUsage(msg.params, emit);
|
|
13507
13765
|
return;
|
|
13766
|
+
case "account/updated": {
|
|
13767
|
+
const account = msg.params;
|
|
13768
|
+
if (account?.authMode === "apikey") emit({ type: "usage", record: { source: this.agentId, aggregate: true, sessionBilling: { type: "api-key" } } });
|
|
13769
|
+
else if (account?.authMode === "chatgpt" || account?.authMode === "chatgptAuthTokens") {
|
|
13770
|
+
const plan = account.planType;
|
|
13771
|
+
emit({ type: "usage", record: { source: this.agentId, aggregate: true, sessionBilling: { type: "subscription", ...plan && plan !== "unknown" ? { plan } : {} } } });
|
|
13772
|
+
}
|
|
13773
|
+
return;
|
|
13774
|
+
}
|
|
13508
13775
|
case "account/rateLimits/updated":
|
|
13509
13776
|
this.rateLimits = mergeCodexRateLimits(this.rateLimits, msg.params);
|
|
13777
|
+
{
|
|
13778
|
+
const sessionBilling = codexSessionBilling(this.rateLimits);
|
|
13779
|
+
if (sessionBilling) emit({ type: "usage", record: { source: this.agentId, aggregate: true, sessionBilling } });
|
|
13780
|
+
}
|
|
13510
13781
|
return;
|
|
13511
13782
|
case "turn/started": {
|
|
13512
13783
|
if (this.subagentOf(msg.params?.threadId)) return;
|
|
@@ -13635,6 +13906,13 @@ ${this.exitMessage()}`);
|
|
|
13635
13906
|
* `last.outputTokens` already includes `reasoningOutputTokens` — the thread
|
|
13636
13907
|
* total is `inputTokens + outputTokens`, not a sum of three — so reasoning is
|
|
13637
13908
|
* not added a second time. Codex reports no price.
|
|
13909
|
+
*
|
|
13910
|
+
* The main thread's context is what Codex itself shows as in its window:
|
|
13911
|
+
* `TokenUsage::tokens_in_context_window`, which is `total_tokens` over
|
|
13912
|
+
* `last` as codex-rs defines it. `total_tokens` is input plus output, and
|
|
13913
|
+
* reasoning is part of output, so this is not `input + output − reasoning`
|
|
13914
|
+
* (checked against codex-rs and codex-cli 0.163.0's token_count events,
|
|
13915
|
+
* where `total_tokens` is `input_tokens + output_tokens`).
|
|
13638
13916
|
*/
|
|
13639
13917
|
emitUsage(params, emit) {
|
|
13640
13918
|
const last = params?.tokenUsage?.last;
|
|
@@ -13662,6 +13940,8 @@ ${this.exitMessage()}`);
|
|
|
13662
13940
|
} else {
|
|
13663
13941
|
const window = params?.tokenUsage?.modelContextWindow;
|
|
13664
13942
|
if (typeof window === "number" && window > 0) record.contextWindow = window;
|
|
13943
|
+
const inContext = last.totalTokens !== void 0 && last.totalTokens > 0 ? last.totalTokens : last.inputTokens !== void 0 ? last.inputTokens + (last.outputTokens ?? 0) : void 0;
|
|
13944
|
+
if (inContext !== void 0) record.contextTokens = Math.max(0, inContext);
|
|
13665
13945
|
}
|
|
13666
13946
|
emit({ type: "usage", record });
|
|
13667
13947
|
}
|
|
@@ -13823,7 +14103,9 @@ ${this.exitMessage()}`);
|
|
|
13823
14103
|
}
|
|
13824
14104
|
switch (item.type) {
|
|
13825
14105
|
case "contextCompaction":
|
|
13826
|
-
if (
|
|
14106
|
+
if (subagentId) return;
|
|
14107
|
+
emit({ type: "compaction", trigger: this.command === "compact" ? "manual" : "auto" });
|
|
14108
|
+
emit({ type: "usage", record: { source: this.agentId, compacted: true } });
|
|
13827
14109
|
return;
|
|
13828
14110
|
// Codex announces the item started and completed; whichever lands first delivers.
|
|
13829
14111
|
case "userMessage":
|
|
@@ -14119,8 +14401,9 @@ function usageRecord2(tokens, call) {
|
|
|
14119
14401
|
if ((prompt.inputTokens ?? 0) + outputTokens === 0) return null;
|
|
14120
14402
|
const record = { source: "opencode", ...prompt, outputTokens };
|
|
14121
14403
|
if (call.model) record.model = call.model;
|
|
14122
|
-
if (typeof call.cost === "number" && call.cost > 0) record.reportedCost = { amount: call.cost, currency: "USD" };
|
|
14404
|
+
if (typeof call.cost === "number" && call.cost > 0) record.reportedCost = { amount: call.cost, currency: "USD", kind: "estimate" };
|
|
14123
14405
|
if (call.subagentId) record.subagentId = call.subagentId;
|
|
14406
|
+
else record.contextTokens = (prompt.inputTokens ?? 0) + outputTokens;
|
|
14124
14407
|
return record;
|
|
14125
14408
|
}
|
|
14126
14409
|
function permissionReply(decision, key) {
|
|
@@ -14683,6 +14966,7 @@ var OpenCodeV2 = class {
|
|
|
14683
14966
|
}
|
|
14684
14967
|
if (frame.type === "session.compaction.ended") {
|
|
14685
14968
|
onEvent({ type: "compaction", trigger: data.reason === "manual" ? "manual" : "auto" });
|
|
14969
|
+
onEvent({ type: "usage", record: { source: "opencode", compacted: true } });
|
|
14686
14970
|
return;
|
|
14687
14971
|
}
|
|
14688
14972
|
if (frame.type === "session.execution.started") {
|
|
@@ -15548,6 +15832,7 @@ var OpenCodeAdapter = class {
|
|
|
15548
15832
|
if (session && session !== this.sessionId && !turn.children.follows(session)) return;
|
|
15549
15833
|
if (frame.type === "session.compacted" && session === this.sessionId) {
|
|
15550
15834
|
onEvent({ type: "compaction", trigger: this.manualCompaction ? "manual" : "auto" });
|
|
15835
|
+
onEvent({ type: "usage", record: { source: "opencode", compacted: true } });
|
|
15551
15836
|
return;
|
|
15552
15837
|
}
|
|
15553
15838
|
if (session === this.sessionId && this.taskTurn) this.followTaskTurn(frame, this.taskTurn, onEvent);
|
|
@@ -20570,6 +20855,7 @@ var PlannerConversation = class {
|
|
|
20570
20855
|
const request = summaryRequest(this.currentPlanLines());
|
|
20571
20856
|
const onProgress = (p) => {
|
|
20572
20857
|
if (p.type === "liveness") this.host.onProgress(p);
|
|
20858
|
+
else if (p.type === "usage" && p.record) this.host.onProgress({ ...p, record: { ...p.record, aggregate: true } });
|
|
20573
20859
|
};
|
|
20574
20860
|
let turn;
|
|
20575
20861
|
if (this.canContinueLive()) {
|
|
@@ -24260,6 +24546,9 @@ function nonPromptingFs(fs18) {
|
|
|
24260
24546
|
}
|
|
24261
24547
|
};
|
|
24262
24548
|
}
|
|
24549
|
+
function withoutContext({ contextTokens: _context, ...record }) {
|
|
24550
|
+
return record;
|
|
24551
|
+
}
|
|
24263
24552
|
async function runLoop(prompt, deps) {
|
|
24264
24553
|
if (deps.signal?.aborted) return "[research agent aborted before starting]";
|
|
24265
24554
|
const fs18 = nonPromptingFs(deps.fs);
|
|
@@ -24267,7 +24556,8 @@ async function runLoop(prompt, deps) {
|
|
|
24267
24556
|
(delta) => deps.onProgress?.({ type: "thinking", text: delta, subagentId: deps.subagentId }),
|
|
24268
24557
|
// The record says who made the call: unstamped, the ledger would read the
|
|
24269
24558
|
// subagent's prompt as the planner's own and measure its context by it.
|
|
24270
|
-
|
|
24559
|
+
// The chat states the context its call leaves; a subagent's is no meter's.
|
|
24560
|
+
(record) => deps.onProgress?.({ type: "usage", record: deps.subagentId ? { ...withoutContext(record), subagentId: deps.subagentId } : record })
|
|
24271
24561
|
);
|
|
24272
24562
|
let turn = await chat.sendMessage(prompt, deps.signal);
|
|
24273
24563
|
for (let step = 0; turn.hasToolCalls && step < SUBAGENT_LIMITS.maxSteps; step++) {
|
|
@@ -24725,9 +25015,11 @@ function plannerNotReadyFor(config) {
|
|
|
24725
25015
|
var TERMINAL_UI_COMMANDS = {
|
|
24726
25016
|
pickPlanner: "/planner",
|
|
24727
25017
|
setKey: () => "/key",
|
|
24728
|
-
|
|
25018
|
+
// `/planner` walks harness, model and effort in one flow; there is no
|
|
25019
|
+
// separate `/model` (or `/planner-effort`) to point at.
|
|
25020
|
+
setModel: "/planner"
|
|
24729
25021
|
};
|
|
24730
|
-
var CHAT_COMMANDS = { ...TERMINAL_UI_COMMANDS
|
|
25022
|
+
var CHAT_COMMANDS = { ...TERMINAL_UI_COMMANDS };
|
|
24731
25023
|
var HEADLESS_CLI_COMMANDS = {
|
|
24732
25024
|
pickPlanner: "ordewell planner <id>",
|
|
24733
25025
|
setKey: (provider) => `ordewell key set ${provider} <key>`,
|
|
@@ -24741,7 +25033,7 @@ function harnessPlannerNames() {
|
|
|
24741
25033
|
var PLANNER_ESCAPE_HINT = `or pick a coding agent with ${TERMINAL_UI_COMMANDS.pickPlanner}: it plans with no API key`;
|
|
24742
25034
|
function describePlannerNotReady(reason, commands) {
|
|
24743
25035
|
if (reason.code === "no_model") {
|
|
24744
|
-
return `No planner model selected for ${reason.label}. Pick one with ${commands.setModel}, or switch planner with ${commands.pickPlanner}.`;
|
|
25036
|
+
return commands.setModel === commands.pickPlanner ? `No planner model selected for ${reason.label}. Pick one with ${commands.setModel}.` : `No planner model selected for ${reason.label}. Pick one with ${commands.setModel}, or switch planner with ${commands.pickPlanner}.`;
|
|
24745
25037
|
}
|
|
24746
25038
|
const lead = `No planner is ready. Pick one with ${commands.pickPlanner}: ${harnessPlannerNames()} plan with no API key.`;
|
|
24747
25039
|
if (reason.code === "no_base_url") {
|
|
@@ -24775,7 +25067,8 @@ function usageRecordFrom(usage, model) {
|
|
|
24775
25067
|
model,
|
|
24776
25068
|
...inputTokens !== void 0 ? { inputTokens } : {},
|
|
24777
25069
|
...reportedOutput.length > 0 ? { outputTokens: reportedOutput.reduce((a, b) => a + b, 0) } : {},
|
|
24778
|
-
...tokenCount(usage.cachedContentTokenCount) !== void 0 ? { cachedInputTokens: tokenCount(usage.cachedContentTokenCount) } : {}
|
|
25070
|
+
...tokenCount(usage.cachedContentTokenCount) !== void 0 ? { cachedInputTokens: tokenCount(usage.cachedContentTokenCount) } : {},
|
|
25071
|
+
...inputTokens !== void 0 ? { contextTokens: inputTokens + (answerTokens ?? 0) } : {}
|
|
24779
25072
|
};
|
|
24780
25073
|
const anyReported = record.inputTokens !== void 0 || record.outputTokens !== void 0 || record.cachedInputTokens !== void 0;
|
|
24781
25074
|
return anyReported ? record : void 0;
|
|
@@ -25178,7 +25471,9 @@ var OpenAiResearchChat = class {
|
|
|
25178
25471
|
/**
|
|
25179
25472
|
* The one record for this API call, built only from what the provider
|
|
25180
25473
|
* reported. OpenRouter's `cost` is in USD (its credits are dollar-pegged);
|
|
25181
|
-
* nothing is estimated. A field the provider left out stays absent.
|
|
25474
|
+
* nothing is estimated. A field the provider left out stays absent. The
|
|
25475
|
+
* context the call leaves is its prompt and its completion: the API's own
|
|
25476
|
+
* `total_tokens`, and the reply the next call's prompt carries.
|
|
25182
25477
|
*/
|
|
25183
25478
|
usageRecord(reported) {
|
|
25184
25479
|
if (!reported) return void 0;
|
|
@@ -25197,9 +25492,10 @@ var OpenAiResearchChat = class {
|
|
|
25197
25492
|
hasMeasure = true;
|
|
25198
25493
|
}
|
|
25199
25494
|
if (reported.cost !== void 0) {
|
|
25200
|
-
record.reportedCost = { amount: reported.cost, currency: "USD" };
|
|
25495
|
+
record.reportedCost = { amount: reported.cost, currency: "USD", ...record.source === "openrouter" ? { kind: "billed" } : {} };
|
|
25201
25496
|
hasMeasure = true;
|
|
25202
25497
|
}
|
|
25498
|
+
if (record.inputTokens !== void 0) record.contextTokens = record.inputTokens + (record.outputTokens ?? 0);
|
|
25203
25499
|
return hasMeasure ? record : void 0;
|
|
25204
25500
|
}
|
|
25205
25501
|
};
|
|
@@ -27370,6 +27666,10 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
27370
27666
|
store;
|
|
27371
27667
|
output;
|
|
27372
27668
|
attempts = /* @__PURE__ */ new Map();
|
|
27669
|
+
/** Bumped by every plan load: an ended attempt's wind-down belongs to the scope before it. */
|
|
27670
|
+
planScope = 0;
|
|
27671
|
+
/** Each task's runners still winding down once stopped: a continue waits for them (ADR-0018, L1). */
|
|
27672
|
+
windDowns = /* @__PURE__ */ new Map();
|
|
27373
27673
|
verifier = new VerdictEngine();
|
|
27374
27674
|
/** Changed only by start, stop, a plan load, a usage-limit pause, a move off the limit, and {@link settleRun}. */
|
|
27375
27675
|
runState = "idle";
|
|
@@ -27918,8 +28218,9 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
27918
28218
|
return blockingFailures(this.store.planTasks).get(taskId) ?? [];
|
|
27919
28219
|
}
|
|
27920
28220
|
loadPlan(tasks, planRunners = [...DEFAULT_RUNNERS]) {
|
|
27921
|
-
this.store.load(tasks, planRunners);
|
|
27922
28221
|
const repairs = this.endAllAttempts("load").filter((a) => a.kind.kind === "repair" && a.worktree);
|
|
28222
|
+
this.planScope += 1;
|
|
28223
|
+
this.store.load(tasks, planRunners);
|
|
27923
28224
|
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}"`);
|
|
27924
28225
|
this.runs.interrupt({ keepOpen: this.isScheduling });
|
|
27925
28226
|
if (this.runState !== "scheduling") this.runState = "idle";
|
|
@@ -28005,6 +28306,7 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
28005
28306
|
if (reason === "user") this.stopGeneration++;
|
|
28006
28307
|
this.runState = "idle";
|
|
28007
28308
|
this.verifier.reset();
|
|
28309
|
+
for (const attempt of this.attempts.values()) this.stopRunner(attempt);
|
|
28008
28310
|
this.runner.stopAll();
|
|
28009
28311
|
const stopped = [];
|
|
28010
28312
|
for (const a of this.endAllAttempts("stop")) {
|
|
@@ -28118,6 +28420,7 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
28118
28420
|
if (!task || !attempt) return;
|
|
28119
28421
|
attempt.decided = true;
|
|
28120
28422
|
const stoppedMidTurn = endedByExit(verdict) ? saidInFlight(attempt) : null;
|
|
28423
|
+
this.stopRunner(attempt);
|
|
28121
28424
|
console.error(`[TaskOrchestrator] Task ${taskRefOf(this.store.planTasks, task.id) ?? `#${task.order}`} "${task.title}" verdict=${verdict.outcome}`);
|
|
28122
28425
|
console.error(`[TaskOrchestrator] Runner: ${task.assignedRunner}, Model: ${task.assignedModel?.modelId ?? "default"}`);
|
|
28123
28426
|
console.error(`[TaskOrchestrator] Prompt preview: ${(task.prompt ?? "").slice(0, 200)}`);
|
|
@@ -29029,7 +29332,9 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
29029
29332
|
autoResumed: opts.autoResumed === true,
|
|
29030
29333
|
turnError: null,
|
|
29031
29334
|
turnOutputFrom: 0,
|
|
29032
|
-
reminded: false
|
|
29335
|
+
reminded: false,
|
|
29336
|
+
runnerStopped: false,
|
|
29337
|
+
planScope: this.planScope
|
|
29033
29338
|
};
|
|
29034
29339
|
this.spawnCounts.set(task.id, attempt.attempt);
|
|
29035
29340
|
this.attempts.set(task.id, attempt);
|
|
@@ -29066,6 +29371,7 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
29066
29371
|
const manifest = this.registry?.get(attempt.runner)?.manifest;
|
|
29067
29372
|
const unavailable = manifest && unavailableTaskModeMessage(manifest, task.taskMode);
|
|
29068
29373
|
if (unavailable) throw new Error(unavailable);
|
|
29374
|
+
if (kind.kind === "continuation") await this.windDowns.get(task.id);
|
|
29069
29375
|
const { cwd, worktree, previousWork } = await attemptCwd(kind, task, this.runs);
|
|
29070
29376
|
attempt.cwd = cwd;
|
|
29071
29377
|
attempt.worktree = worktree;
|
|
@@ -29121,10 +29427,11 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
29121
29427
|
}
|
|
29122
29428
|
if (takesSkills(kind)) this.store.setTaskAttemptSkills(task.id, [...attempt.skills]);
|
|
29123
29429
|
session.onEvent((event) => {
|
|
29124
|
-
if (event.type !== "usage" || this.attempts.get(task.id) !== attempt) return;
|
|
29430
|
+
if (event.type !== "usage" || attempt.planScope !== this.planScope || this.attempts.get(task.id) !== attempt && !attempt.runnerStopped) return;
|
|
29125
29431
|
this.store.recordAttemptUsage(task.id, attempt.attempt, event.record, (/* @__PURE__ */ new Date()).toISOString());
|
|
29126
29432
|
this.emit("onUsageChanged");
|
|
29127
29433
|
});
|
|
29434
|
+
session.onExit(() => this.woundDown(attempt, session));
|
|
29128
29435
|
this.output.attach(task.id, session);
|
|
29129
29436
|
this.verifier.watch(task, session);
|
|
29130
29437
|
session.onEvent((event) => {
|
|
@@ -29293,10 +29600,35 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
29293
29600
|
if (stopsRunner(reason)) {
|
|
29294
29601
|
const task = this.store.get(taskId);
|
|
29295
29602
|
if (task) this.verifier.clear(task);
|
|
29296
|
-
if (
|
|
29603
|
+
if (attempt) this.stopRunner(attempt);
|
|
29297
29604
|
}
|
|
29298
29605
|
return attempt;
|
|
29299
29606
|
}
|
|
29607
|
+
/**
|
|
29608
|
+
* Let the attempt's runner go the way it ends a turn (ADR-0018, L1): it
|
|
29609
|
+
* winds down on its own time, bounded by its interrupt grace, and nothing
|
|
29610
|
+
* here waits on it but a continue of the same task.
|
|
29611
|
+
*/
|
|
29612
|
+
stopRunner(attempt) {
|
|
29613
|
+
const session = attempt.session;
|
|
29614
|
+
if (!session || attempt.runnerStopped) return;
|
|
29615
|
+
attempt.runnerStopped = true;
|
|
29616
|
+
const { taskId } = attempt;
|
|
29617
|
+
const windDown = Promise.all([this.windDowns.get(taskId), this.runner.stop(session.id)]).catch(() => void 0).then(() => {
|
|
29618
|
+
if (this.windDowns.get(taskId) === windDown) this.windDowns.delete(taskId);
|
|
29619
|
+
});
|
|
29620
|
+
this.windDowns.set(taskId, windDown);
|
|
29621
|
+
}
|
|
29622
|
+
/**
|
|
29623
|
+
* An ended attempt's runner is gone: the count its session ended on is the
|
|
29624
|
+
* one a continue of that session counts from. One still current is ended
|
|
29625
|
+
* with that count instead.
|
|
29626
|
+
*/
|
|
29627
|
+
woundDown(attempt, session) {
|
|
29628
|
+
if (attempt.planScope !== this.planScope || !attempt.runnerStopped || this.attempts.get(attempt.taskId) === attempt) return;
|
|
29629
|
+
this.store.endAttemptUsage(attempt.taskId, attempt.attempt, (/* @__PURE__ */ new Date()).toISOString(), session.sessionCost?.() ?? void 0, session.nativeSessionId() ?? void 0);
|
|
29630
|
+
this.emit("onUsageChanged");
|
|
29631
|
+
}
|
|
29300
29632
|
/** Kept on the task once the attempt ends, so a continue can resume it after a reload (ADR-0018, K1). */
|
|
29301
29633
|
saveNativeSession(taskId, nativeSessionId, runner) {
|
|
29302
29634
|
if (nativeSessionId) this.store.setTaskRunnerSessionId(taskId, nativeSessionId, runner);
|
|
@@ -29654,7 +29986,7 @@ function taskReadPage(text3, { lines: lines2, since }) {
|
|
|
29654
29986
|
|
|
29655
29987
|
// src/services/plannerTools.ts
|
|
29656
29988
|
var PLANNER_TURN_ENDED = "The planning turn this call was made in has ended, so nothing was submitted.";
|
|
29657
|
-
function
|
|
29989
|
+
function runnersOf2(tasks) {
|
|
29658
29990
|
return [...new Set(flattenTasks(tasks).filter((t) => t.type !== "user").map((t) => t.assignedRunner))];
|
|
29659
29991
|
}
|
|
29660
29992
|
function plannerToolHandler(host) {
|
|
@@ -32204,19 +32536,50 @@ var TaskLogRecorder = class {
|
|
|
32204
32536
|
};
|
|
32205
32537
|
|
|
32206
32538
|
// src/services/ContextWindowFill.ts
|
|
32207
|
-
var
|
|
32208
|
-
constructor(catalog
|
|
32539
|
+
var SessionWindows = class {
|
|
32540
|
+
constructor(catalog = () => void 0) {
|
|
32209
32541
|
this.catalog = catalog;
|
|
32210
|
-
if (last.contextWindow !== void 0) this.known.set(modelKey(last.source, last.model), last.contextWindow);
|
|
32211
32542
|
}
|
|
32212
32543
|
catalog;
|
|
32213
|
-
|
|
32544
|
+
reported = /* @__PURE__ */ new Map();
|
|
32545
|
+
report(source, model, contextWindow) {
|
|
32546
|
+
if (contextWindow > 0) this.reported.set(modelKey(source, model), { source, model, contextWindow });
|
|
32547
|
+
}
|
|
32548
|
+
get(source, model) {
|
|
32549
|
+
return this.reported.get(modelKey(source, model))?.contextWindow;
|
|
32550
|
+
}
|
|
32551
|
+
snapshot() {
|
|
32552
|
+
return [...this.reported.values()];
|
|
32553
|
+
}
|
|
32554
|
+
restore(saved) {
|
|
32555
|
+
this.reported.clear();
|
|
32556
|
+
for (const { source, model, contextWindow } of saved ?? []) this.report(source, model, contextWindow);
|
|
32557
|
+
}
|
|
32558
|
+
clear() {
|
|
32559
|
+
this.reported.clear();
|
|
32560
|
+
}
|
|
32561
|
+
};
|
|
32562
|
+
var ContextWindowFill = class {
|
|
32563
|
+
constructor(windows, last = {}) {
|
|
32564
|
+
this.windows = windows;
|
|
32565
|
+
if (last.contextWindow !== void 0) this.known.set(modelKey(last.source, last.model), last.contextWindow);
|
|
32566
|
+
}
|
|
32567
|
+
windows;
|
|
32568
|
+
/** The window this stream used for each of its models, by reporter and model. */
|
|
32214
32569
|
known = /* @__PURE__ */ new Map();
|
|
32215
32570
|
fill(record) {
|
|
32216
32571
|
if (record.subagentId || record.aggregate) return record;
|
|
32217
32572
|
const key = modelKey(record.source, record.model);
|
|
32573
|
+
if (record.compacted) {
|
|
32574
|
+
if (!record.model) return record;
|
|
32575
|
+
const known = this.windows.get(record.source, record.model) ?? this.known.get(key) ?? positive(this.windows.catalog(record.source, record.model));
|
|
32576
|
+
if (known === void 0) return record;
|
|
32577
|
+
this.known.set(key, known);
|
|
32578
|
+
return { ...record, contextWindow: known };
|
|
32579
|
+
}
|
|
32218
32580
|
const reported = record.contextWindow !== void 0 && record.contextWindow > 0 ? record.contextWindow : void 0;
|
|
32219
|
-
|
|
32581
|
+
if (reported !== void 0 && record.model) this.windows.report(record.source, record.model, reported);
|
|
32582
|
+
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);
|
|
32220
32583
|
if (window !== void 0) this.known.set(key, window);
|
|
32221
32584
|
return window === void 0 || window === record.contextWindow ? record : { ...record, contextWindow: window };
|
|
32222
32585
|
}
|
|
@@ -32224,9 +32587,9 @@ var ContextWindowFill = class {
|
|
|
32224
32587
|
function modelKey(source, model) {
|
|
32225
32588
|
return JSON.stringify([source ?? null, model ?? null]);
|
|
32226
32589
|
}
|
|
32227
|
-
function withContextWindows(runner,
|
|
32590
|
+
function withContextWindows(runner, windows) {
|
|
32228
32591
|
return {
|
|
32229
|
-
spawn: async (opts) => windowedSession(await runner.spawn(opts), new ContextWindowFill(
|
|
32592
|
+
spawn: async (opts) => windowedSession(await runner.spawn(opts), new ContextWindowFill(windows, opts.resumeWindow)),
|
|
32230
32593
|
stop: (sessionId) => runner.stop(sessionId),
|
|
32231
32594
|
stopAll: () => runner.stopAll(),
|
|
32232
32595
|
get activeCount() {
|
|
@@ -32257,12 +32620,12 @@ function positive(n) {
|
|
|
32257
32620
|
|
|
32258
32621
|
// src/services/PlannerUsage.ts
|
|
32259
32622
|
var PlannerUsageLedger = class {
|
|
32260
|
-
/** `
|
|
32261
|
-
constructor(
|
|
32262
|
-
this.
|
|
32263
|
-
this.windows = new ContextWindowFill(
|
|
32623
|
+
/** `sessionWindows` serves the window of a planner model whose backend reports none. */
|
|
32624
|
+
constructor(sessionWindows = new SessionWindows()) {
|
|
32625
|
+
this.sessionWindows = sessionWindows;
|
|
32626
|
+
this.windows = new ContextWindowFill(sessionWindows);
|
|
32264
32627
|
}
|
|
32265
|
-
|
|
32628
|
+
sessionWindows;
|
|
32266
32629
|
usage = { totals: {} };
|
|
32267
32630
|
windows;
|
|
32268
32631
|
/** Fold one model call's record into the totals; returns the running ledger. */
|
|
@@ -32273,16 +32636,16 @@ var PlannerUsageLedger = class {
|
|
|
32273
32636
|
/** Adopt a persisted ledger (a reopened session) or start from zero. */
|
|
32274
32637
|
restore(usage) {
|
|
32275
32638
|
this.usage = usage ?? { totals: {} };
|
|
32276
|
-
this.windows = new ContextWindowFill(this.
|
|
32639
|
+
this.windows = new ContextWindowFill(this.sessionWindows, this.usage);
|
|
32277
32640
|
}
|
|
32278
32641
|
/** Start a fresh session's ledger from zero. */
|
|
32279
32642
|
clear() {
|
|
32280
32643
|
this.usage = { totals: {} };
|
|
32281
|
-
this.windows = new ContextWindowFill(this.
|
|
32644
|
+
this.windows = new ContextWindowFill(this.sessionWindows);
|
|
32282
32645
|
}
|
|
32283
|
-
/** Whether
|
|
32646
|
+
/** Whether the ledger has measures or session billing facts to broadcast. */
|
|
32284
32647
|
get hasUsage() {
|
|
32285
|
-
return isMeasured(this.usage.totals);
|
|
32648
|
+
return isMeasured(this.usage.totals) || this.usage.sessionBilling !== void 0;
|
|
32286
32649
|
}
|
|
32287
32650
|
/** The value persisted onto the plan state. */
|
|
32288
32651
|
snapshot() {
|
|
@@ -33731,7 +34094,7 @@ function createSession(deps) {
|
|
|
33731
34094
|
const backend = new PlannerBackend(deps.aiService ? { pinned: deps.aiService, busy } : { provider: () => deps.config.aiProvider, create: () => createAiService(deps.config, { workspaceRoot: deps.workspaceRoot, mcpServer }), busy });
|
|
33732
34095
|
const aiService = () => backend.current();
|
|
33733
34096
|
const store = new PlanStore();
|
|
33734
|
-
const
|
|
34097
|
+
const windows = new SessionWindows((source, model) => deps.modelResolver.contextWindowFor?.(source, model));
|
|
33735
34098
|
const taskLogs = new TaskLogRecorder({
|
|
33736
34099
|
broadcast: deps.broadcast,
|
|
33737
34100
|
// Read through the Session, whose id and workspace are the ones it saves
|
|
@@ -33764,7 +34127,7 @@ function createSession(deps) {
|
|
|
33764
34127
|
const orchestrator = TaskOrchestrator.compose({
|
|
33765
34128
|
config: deps.config,
|
|
33766
34129
|
notifications: deps.notifications,
|
|
33767
|
-
runner: taskLogs.wrap(runnerApprovals.wrap(withContextWindows(deps.runner,
|
|
34130
|
+
runner: taskLogs.wrap(runnerApprovals.wrap(withContextWindows(deps.runner, windows))),
|
|
33768
34131
|
store,
|
|
33769
34132
|
output: deps.taskOutput,
|
|
33770
34133
|
isolation: deps.isolation,
|
|
@@ -33776,7 +34139,7 @@ function createSession(deps) {
|
|
|
33776
34139
|
runSettings: () => session.planState?.runSettings,
|
|
33777
34140
|
isApproved: (taskId) => session.isApproved(taskId)
|
|
33778
34141
|
});
|
|
33779
|
-
const usage = new PlannerUsageLedger(
|
|
34142
|
+
const usage = new PlannerUsageLedger(windows);
|
|
33780
34143
|
const events = new SessionEventRelay({
|
|
33781
34144
|
broadcast: deps.broadcast,
|
|
33782
34145
|
onNotice: deps.onNotice,
|
|
@@ -33846,6 +34209,7 @@ function createSession(deps) {
|
|
|
33846
34209
|
runs: orchestrator.runs,
|
|
33847
34210
|
events,
|
|
33848
34211
|
usage,
|
|
34212
|
+
windows,
|
|
33849
34213
|
approvals,
|
|
33850
34214
|
approvalPolicy,
|
|
33851
34215
|
taskLogs,
|
|
@@ -33873,6 +34237,7 @@ function configSecrets(config) {
|
|
|
33873
34237
|
var Session = class {
|
|
33874
34238
|
backend;
|
|
33875
34239
|
usage;
|
|
34240
|
+
windows;
|
|
33876
34241
|
events;
|
|
33877
34242
|
planner;
|
|
33878
34243
|
orchestrator;
|
|
@@ -33978,6 +34343,7 @@ var Session = class {
|
|
|
33978
34343
|
this.runs = parts.runs;
|
|
33979
34344
|
this.events = parts.events;
|
|
33980
34345
|
this.usage = parts.usage;
|
|
34346
|
+
this.windows = parts.windows;
|
|
33981
34347
|
this.approvals = parts.approvals;
|
|
33982
34348
|
this.approvalPolicy = parts.approvalPolicy;
|
|
33983
34349
|
this.taskLogs = parts.taskLogs;
|
|
@@ -34456,6 +34822,7 @@ var Session = class {
|
|
|
34456
34822
|
this.syncPlanTasks();
|
|
34457
34823
|
this.plan.isolation = this.runs.planIsolation ?? void 0;
|
|
34458
34824
|
this.plan.plannerUsage = this.usage.snapshot();
|
|
34825
|
+
this.plan.contextWindows = this.windows.snapshot();
|
|
34459
34826
|
this.plan.supervision = this.supervision.snapshot();
|
|
34460
34827
|
this.plan.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
34461
34828
|
this.save(this.plan, this.goal, this.workspace, this.currentSessionId);
|
|
@@ -34485,6 +34852,7 @@ var Session = class {
|
|
|
34485
34852
|
this.owingEffects.clear();
|
|
34486
34853
|
this.conversation.reset();
|
|
34487
34854
|
this.usage.clear();
|
|
34855
|
+
this.windows.clear();
|
|
34488
34856
|
this.approvals.clear(isPlannerApproval);
|
|
34489
34857
|
if (!keepGrants) this.approvalPolicy.reset();
|
|
34490
34858
|
this.store.clearLog();
|
|
@@ -34825,7 +35193,18 @@ var Session = class {
|
|
|
34825
35193
|
if (!this.plan) throw new ConversationEditError("No planning conversation to condense");
|
|
34826
35194
|
const opening = this.plannerTurnMayOpen(signal);
|
|
34827
35195
|
if (opening) await opening;
|
|
34828
|
-
|
|
35196
|
+
const compaction = await this.conversation.compact(signal);
|
|
35197
|
+
const usage = this.usage.snapshot();
|
|
35198
|
+
if (usage.lastPromptTokens !== void 0) {
|
|
35199
|
+
this.usage.record({
|
|
35200
|
+
source: usage.source ?? this.config.aiProvider,
|
|
35201
|
+
...usage.model ? { model: usage.model } : {},
|
|
35202
|
+
compacted: true
|
|
35203
|
+
});
|
|
35204
|
+
this.persist();
|
|
35205
|
+
this.broadcast(this.usage.message());
|
|
35206
|
+
}
|
|
35207
|
+
return compaction;
|
|
34829
35208
|
}
|
|
34830
35209
|
rewindTargets() {
|
|
34831
35210
|
return this.conversation.rewindTargets();
|
|
@@ -34912,7 +35291,7 @@ var Session = class {
|
|
|
34912
35291
|
*/
|
|
34913
35292
|
adoptPlannerTasks(tasks, how) {
|
|
34914
35293
|
this.turnEffects.landed(how);
|
|
34915
|
-
for (const runner of
|
|
35294
|
+
for (const runner of runnersOf2(tasks)) this.editor.admitRunner(runner, this.catalog.known(runner));
|
|
34916
35295
|
const runners = this.plan.runners;
|
|
34917
35296
|
const before = this.store.planTasks;
|
|
34918
35297
|
let coerced = resolveAssignments(tasks, before, this.catalog.edit(runners)).tasks;
|
|
@@ -35313,7 +35692,10 @@ var Session = class {
|
|
|
35313
35692
|
if (!plan.approvedTasks && plan.approved) {
|
|
35314
35693
|
plan.approvedTasks = flattenTasks(plan.tasks).map((t) => t.id);
|
|
35315
35694
|
}
|
|
35316
|
-
if (adopting)
|
|
35695
|
+
if (adopting) {
|
|
35696
|
+
this.usage.restore(plan.plannerUsage);
|
|
35697
|
+
this.windows.restore(plan.contextWindows);
|
|
35698
|
+
}
|
|
35317
35699
|
if (opts?.sessionId) this.currentSessionId = opts.sessionId;
|
|
35318
35700
|
if (adopting) {
|
|
35319
35701
|
for (const task of flattenTasks(plan.tasks)) delete task.resumeAt;
|
|
@@ -35395,7 +35777,7 @@ function openTurn(view, { turnId, prompt = "", skills = [], wakeUps = [], forced
|
|
|
35395
35777
|
wakeUps = wakeUps.slice(adopted.wakeUps);
|
|
35396
35778
|
if (wakeUps.length === 0) return adopted.prompt || prompt === "" && adopted.wakeUps > 0 ? view : promptLine(view, turnId, prompt, skills, forced);
|
|
35397
35779
|
const said2 = prompt === "" || adopted.prompt ? view : promptLine(view, turnId, prompt, skills, forced);
|
|
35398
|
-
const at = prompt === "" ? said2.blocks.length : findLastIndex(said2.blocks, (b) => b.type === "message" && b.role === "user" && b.turnId === turnId);
|
|
35780
|
+
const at = prompt === "" ? said2.blocks.length - (said2.blocks.at(-1)?.type === "usage" ? 1 : 0) : findLastIndex(said2.blocks, (b) => b.type === "message" && b.role === "user" && b.turnId === turnId);
|
|
35399
35781
|
const updates = wakeUps.map((text3, n) => settledMessage(`b${said2.nextId + n}`, "wake_up", text3, turnId));
|
|
35400
35782
|
return { ...said2, blocks: [...said2.blocks.slice(0, at), ...updates, ...said2.blocks.slice(at)], nextId: said2.nextId + updates.length };
|
|
35401
35783
|
}
|
|
@@ -36082,17 +36464,12 @@ var AbstractRunner = class {
|
|
|
36082
36464
|
get activeCount() {
|
|
36083
36465
|
return this.sessions.size;
|
|
36084
36466
|
}
|
|
36467
|
+
/** A session winding down stays registered until it exits, so a second stop gets the same wind-down. */
|
|
36085
36468
|
stop(sessionId) {
|
|
36086
|
-
|
|
36087
|
-
if (!session) return;
|
|
36088
|
-
session.kill();
|
|
36089
|
-
this.sessions.delete(sessionId);
|
|
36469
|
+
return this.sessions.get(sessionId)?.stop() ?? Promise.resolve();
|
|
36090
36470
|
}
|
|
36091
36471
|
stopAll() {
|
|
36092
|
-
for (const
|
|
36093
|
-
session.kill();
|
|
36094
|
-
}
|
|
36095
|
-
this.sessions.clear();
|
|
36472
|
+
for (const session of this.sessions.values()) void session.stop();
|
|
36096
36473
|
}
|
|
36097
36474
|
registerSession(id, session) {
|
|
36098
36475
|
this.sessions.set(id, session);
|
|
@@ -36244,12 +36621,17 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36244
36621
|
text;
|
|
36245
36622
|
state = "idle";
|
|
36246
36623
|
turn = null;
|
|
36624
|
+
/** A `task_complete` report waiting for its turn to close; the verdict is published then. */
|
|
36625
|
+
completionReport = null;
|
|
36626
|
+
completionTimer = null;
|
|
36247
36627
|
/** A terminal event ends the turn before the adapter releases its send. */
|
|
36248
36628
|
sending = null;
|
|
36249
36629
|
turnCount = 0;
|
|
36250
36630
|
queue = [];
|
|
36251
36631
|
messageCount = 0;
|
|
36252
36632
|
interrupting = null;
|
|
36633
|
+
/** The wind-down once {@link stop} was asked for: the same for every caller, and nothing new is sent meanwhile. */
|
|
36634
|
+
stopping = null;
|
|
36253
36635
|
lastSessionId = null;
|
|
36254
36636
|
lastSessionCost = null;
|
|
36255
36637
|
structuredEmitter = new import_events2.EventEmitter();
|
|
@@ -36275,7 +36657,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36275
36657
|
await this.startAttached(this.launch.startOptions);
|
|
36276
36658
|
this.state = "working";
|
|
36277
36659
|
setImmediate(() => {
|
|
36278
|
-
if (!this.exited) this.deliver(prompt);
|
|
36660
|
+
if (!this.exited && !this.stopping) this.deliver(prompt);
|
|
36279
36661
|
});
|
|
36280
36662
|
}
|
|
36281
36663
|
getOutput() {
|
|
@@ -36300,7 +36682,9 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36300
36682
|
create: () => this.launch.createAdapter(this.launch.runner, this.launch.deps),
|
|
36301
36683
|
issueToken: () => server4.issueTaskToken(scope, {
|
|
36302
36684
|
taskComplete: async (report) => {
|
|
36303
|
-
this.
|
|
36685
|
+
this.completionReport = report;
|
|
36686
|
+
if (this.completionTimer) clearTimeout(this.completionTimer);
|
|
36687
|
+
this.completionTimer = setTimeout(() => this.flushCompletion(), this.launch.interruptGraceMs);
|
|
36304
36688
|
return { text: "Recorded. End your turn now." };
|
|
36305
36689
|
},
|
|
36306
36690
|
checkpoint: async ({ question }, { signal }) => {
|
|
@@ -36332,6 +36716,8 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36332
36716
|
this.toolToken = null;
|
|
36333
36717
|
}
|
|
36334
36718
|
baseHandleExit(code) {
|
|
36719
|
+
this.undeliverQueued();
|
|
36720
|
+
this.flushCompletion();
|
|
36335
36721
|
this.ended.abort();
|
|
36336
36722
|
this.revokeTools();
|
|
36337
36723
|
this.undeliverQueued();
|
|
@@ -36355,6 +36741,53 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36355
36741
|
this.withdrawPermissions();
|
|
36356
36742
|
this.baseHandleExit(-1);
|
|
36357
36743
|
}
|
|
36744
|
+
stop() {
|
|
36745
|
+
if (this.stopping) return this.stopping;
|
|
36746
|
+
if (this.exited) return Promise.resolve();
|
|
36747
|
+
let woundDown;
|
|
36748
|
+
this.stopping = new Promise((resolve9) => {
|
|
36749
|
+
woundDown = resolve9;
|
|
36750
|
+
});
|
|
36751
|
+
void this.windDown().catch(() => this.kill()).then(woundDown);
|
|
36752
|
+
return this.stopping;
|
|
36753
|
+
}
|
|
36754
|
+
/**
|
|
36755
|
+
* The attempt is over (ADR-0018, L1), so what is still owed goes undelivered
|
|
36756
|
+
* and every tool request unanswered at once. A turn still open is
|
|
36757
|
+
* interrupted, and its close — where Claude Code reports the turn's cost and
|
|
36758
|
+
* its model's window — is waited for; then the runner is told nothing more
|
|
36759
|
+
* is coming and left to exit by itself. All of it within the interrupt
|
|
36760
|
+
* grace: a runner still there once it runs out is killed.
|
|
36761
|
+
*/
|
|
36762
|
+
async windDown() {
|
|
36763
|
+
const adapter = this.adapterAttached ? this.adapter : null;
|
|
36764
|
+
const deadline = Date.now() + this.launch.interruptGraceMs;
|
|
36765
|
+
this.revokeTools();
|
|
36766
|
+
this.undeliverQueued();
|
|
36767
|
+
this.withdrawPermissions();
|
|
36768
|
+
const open = this.turn && !this.turn.ended ? this.turn : null;
|
|
36769
|
+
if (adapter && (!open || await this.turnClosed(open, adapter, deadline)) && adapter.endInput && !this.exited) {
|
|
36770
|
+
adapter.endInput();
|
|
36771
|
+
await this.within(this.gone(), deadline);
|
|
36772
|
+
}
|
|
36773
|
+
if (this.exited) adapter?.dispose();
|
|
36774
|
+
else this.kill();
|
|
36775
|
+
}
|
|
36776
|
+
/** Whether `turn` closed within the deadline once interrupted. */
|
|
36777
|
+
async turnClosed(turn, adapter, deadline) {
|
|
36778
|
+
const closed = new Promise((resolve9) => this.structuredEmitter.once("turnEnd", () => resolve9()));
|
|
36779
|
+
adapter.interrupt(Math.max(0, deadline - Date.now())).catch(() => false);
|
|
36780
|
+
return await this.within(closed, deadline) && turn.ended;
|
|
36781
|
+
}
|
|
36782
|
+
/** Whether `done` settled before the deadline, or the session ended meanwhile. */
|
|
36783
|
+
within(done, deadline) {
|
|
36784
|
+
return settleWithin(done.then(() => true), { timeoutMs: Math.max(0, deadline - Date.now()), onTimeout: () => false, ended: this.gone(), onEnded: () => true });
|
|
36785
|
+
}
|
|
36786
|
+
gone() {
|
|
36787
|
+
const { signal } = this.ended;
|
|
36788
|
+
if (signal.aborted) return Promise.resolve();
|
|
36789
|
+
return new Promise((resolve9) => signal.addEventListener("abort", () => resolve9(), { once: true }));
|
|
36790
|
+
}
|
|
36358
36791
|
turnState() {
|
|
36359
36792
|
return this.state;
|
|
36360
36793
|
}
|
|
@@ -36380,7 +36813,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36380
36813
|
this.messageCount += 1;
|
|
36381
36814
|
const id = `msg-${this.messageCount}`;
|
|
36382
36815
|
const sender2 = opening.from ? { from: opening.from } : {};
|
|
36383
|
-
if (this.exited) this.emitEvent({ type: "message_undelivered", messageId: id, text: text3, ...sender2 });
|
|
36816
|
+
if (this.exited || this.stopping) this.emitEvent({ type: "message_undelivered", messageId: id, text: text3, ...sender2 });
|
|
36384
36817
|
else if (this.state === "idle" && this.adapterStarted && !this.sending) this.deliver(text3, opening);
|
|
36385
36818
|
else {
|
|
36386
36819
|
this.state = "working";
|
|
@@ -36425,6 +36858,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36425
36858
|
* "waiting for input" between the two (F3).
|
|
36426
36859
|
*/
|
|
36427
36860
|
force(message) {
|
|
36861
|
+
this.discardCompletion();
|
|
36428
36862
|
message.forced = true;
|
|
36429
36863
|
const at = this.queue.findIndex((m) => !m.forced);
|
|
36430
36864
|
this.queue.splice(at < 0 ? this.queue.length : at, 0, message);
|
|
@@ -36435,7 +36869,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36435
36869
|
});
|
|
36436
36870
|
}
|
|
36437
36871
|
runningTurn() {
|
|
36438
|
-
return !this.exited && this.adapter !== null && this.turn !== null && !this.turn.ended;
|
|
36872
|
+
return !this.exited && !this.stopping && this.adapter !== null && this.turn !== null && !this.turn.ended;
|
|
36439
36873
|
}
|
|
36440
36874
|
/** No runner can recall a message once offered to it, so only a queued one comes back. */
|
|
36441
36875
|
removeQueued(id) {
|
|
@@ -36496,7 +36930,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36496
36930
|
if (this.interrupting) return this.interrupting;
|
|
36497
36931
|
const turn = this.turn;
|
|
36498
36932
|
const adapter = this.adapter;
|
|
36499
|
-
if (!turn || turn.ended || !adapter) return Promise.resolve();
|
|
36933
|
+
if (!turn || turn.ended || !adapter || this.stopping) return Promise.resolve();
|
|
36500
36934
|
this.interrupting = this.interruptTurn(turn, adapter).finally(() => {
|
|
36501
36935
|
this.interrupting = null;
|
|
36502
36936
|
this.offerNext();
|
|
@@ -36510,7 +36944,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36510
36944
|
const settled = await settleWithin(ended.then(() => true), { timeoutMs: grace, onTimeout: () => false });
|
|
36511
36945
|
if (settled) return;
|
|
36512
36946
|
}
|
|
36513
|
-
if (turn.ended || this.exited) return;
|
|
36947
|
+
if (turn.ended || this.exited || this.stopping) return;
|
|
36514
36948
|
await this.restartInterrupted(turn);
|
|
36515
36949
|
}
|
|
36516
36950
|
/**
|
|
@@ -36566,6 +37000,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36566
37000
|
/** `message` is what opened the turn: a queued one has an id, one delivered straight from idle only its sender. */
|
|
36567
37001
|
openTurn(text3, message) {
|
|
36568
37002
|
this.turnCount += 1;
|
|
37003
|
+
this.discardCompletion();
|
|
36569
37004
|
const turn = { id: this.turnCount, abort: new AbortController(), ended: false, reason: "completed", steerRefused: false };
|
|
36570
37005
|
this.turn = turn;
|
|
36571
37006
|
this.state = "working";
|
|
@@ -36614,7 +37049,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36614
37049
|
offerNext() {
|
|
36615
37050
|
const adapter = this.adapter;
|
|
36616
37051
|
const turn = this.turn;
|
|
36617
|
-
if (!adapter?.steer || !turn || turn.ended || turn.steerRefused || this.interrupting || this.exited) return;
|
|
37052
|
+
if (!adapter?.steer || !turn || turn.ended || turn.steerRefused || this.interrupting || this.exited || this.stopping) return;
|
|
36618
37053
|
if (this.queue.some((m) => m.stage === "steering")) return;
|
|
36619
37054
|
const next = this.queue.find((m) => m.stage === "queued");
|
|
36620
37055
|
if (!next || next.command) return;
|
|
@@ -36658,6 +37093,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36658
37093
|
} else if (event.type === "permission_request") this.openPermission(adapter, event);
|
|
36659
37094
|
else if (event.type === "permission_cancelled") this.cancelPermission(adapter, event.id);
|
|
36660
37095
|
else if (event.type === "message_delivered") {
|
|
37096
|
+
this.discardCompletion();
|
|
36661
37097
|
const message = this.delivered(event.id);
|
|
36662
37098
|
if (message) this.emitEvent({ type: "message_delivered", messageId: message.id, text: message.text, ...message.from ? { from: message.from } : {} });
|
|
36663
37099
|
} else if (event.type === "message_dropped") {
|
|
@@ -36689,7 +37125,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36689
37125
|
}
|
|
36690
37126
|
return;
|
|
36691
37127
|
}
|
|
36692
|
-
if (!turn && TURN_OPENING_EVENTS.has(event.type) && !("subagentId" in event && event.subagentId)) {
|
|
37128
|
+
if (!turn && !this.stopping && TURN_OPENING_EVENTS.has(event.type) && !("subagentId" in event && event.subagentId)) {
|
|
36693
37129
|
turn = this.openTurn("");
|
|
36694
37130
|
this.offerNext();
|
|
36695
37131
|
}
|
|
@@ -36738,6 +37174,24 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36738
37174
|
}
|
|
36739
37175
|
this.emitEvent(event);
|
|
36740
37176
|
}
|
|
37177
|
+
/** Publish a `task_complete` report whose turn has closed, or that waited out the grace. */
|
|
37178
|
+
flushCompletion() {
|
|
37179
|
+
if (this.completionTimer) {
|
|
37180
|
+
clearTimeout(this.completionTimer);
|
|
37181
|
+
this.completionTimer = null;
|
|
37182
|
+
}
|
|
37183
|
+
const report = this.completionReport;
|
|
37184
|
+
this.completionReport = null;
|
|
37185
|
+
if (report) this.structuredEmitter.emit("taskComplete", report);
|
|
37186
|
+
}
|
|
37187
|
+
/** A message the runner reads, or a turn it opens, voids a report made before it (ADR-0023, D6). */
|
|
37188
|
+
discardCompletion() {
|
|
37189
|
+
if (this.completionTimer) {
|
|
37190
|
+
clearTimeout(this.completionTimer);
|
|
37191
|
+
this.completionTimer = null;
|
|
37192
|
+
}
|
|
37193
|
+
this.completionReport = null;
|
|
37194
|
+
}
|
|
36741
37195
|
/**
|
|
36742
37196
|
* A queued message goes out as the turn closes, and the state never passes
|
|
36743
37197
|
* through `idle` on the way, so a listener told the turn ended can already
|
|
@@ -36750,6 +37204,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36750
37204
|
turn.ended = true;
|
|
36751
37205
|
this.text.endTurn();
|
|
36752
37206
|
this.emitEvent({ type: "turn_end", reason });
|
|
37207
|
+
this.flushCompletion();
|
|
36753
37208
|
if (reason === "failed") this.undeliverQueued();
|
|
36754
37209
|
this.state = !this.exited && this.queue.length ? "working" : "idle";
|
|
36755
37210
|
this.structuredEmitter.emit("turnEnd", reason);
|
|
@@ -36760,7 +37215,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
36760
37215
|
* back in Ordewell's hands: while it is on offer or owed, nothing overtakes it.
|
|
36761
37216
|
*/
|
|
36762
37217
|
deliverNext() {
|
|
36763
|
-
if (this.exited || this.sending || this.turn && !this.turn.ended) return;
|
|
37218
|
+
if (this.exited || this.stopping || this.sending || this.turn && !this.turn.ended) return;
|
|
36764
37219
|
const head = this.queue[0];
|
|
36765
37220
|
if (!head || this.queue.some((message) => message.stage !== "queued")) return;
|
|
36766
37221
|
this.queue.shift();
|
|
@@ -37210,7 +37665,10 @@ var PlannerModelMemory = class {
|
|
|
37210
37665
|
collectProviderCredentials,
|
|
37211
37666
|
commandAllowedBy,
|
|
37212
37667
|
configuredProviders,
|
|
37668
|
+
contextMeterText,
|
|
37669
|
+
contextMeterTitle,
|
|
37213
37670
|
continuability,
|
|
37671
|
+
costText,
|
|
37214
37672
|
countedSessionCost,
|
|
37215
37673
|
createAiService,
|
|
37216
37674
|
createEmptyPlan,
|
|
@@ -37253,7 +37711,9 @@ var PlannerModelMemory = class {
|
|
|
37253
37711
|
flattenTasksWithParents,
|
|
37254
37712
|
followTaskLog,
|
|
37255
37713
|
followTurn,
|
|
37714
|
+
formatCost,
|
|
37256
37715
|
formatSearchOutput,
|
|
37716
|
+
formatTokenCount,
|
|
37257
37717
|
fromTranscript,
|
|
37258
37718
|
getLatestSession,
|
|
37259
37719
|
getProviderMeta,
|
|
@@ -37305,6 +37765,7 @@ var PlannerModelMemory = class {
|
|
|
37305
37765
|
migratePlanState,
|
|
37306
37766
|
mintDaemonToken,
|
|
37307
37767
|
mintSessionId,
|
|
37768
|
+
modelCostTitle,
|
|
37308
37769
|
namedRepos,
|
|
37309
37770
|
newTaskFields,
|
|
37310
37771
|
normalizeAgentArgs,
|
|
@@ -37390,6 +37851,7 @@ var PlannerModelMemory = class {
|
|
|
37390
37851
|
stopTurn,
|
|
37391
37852
|
stripAnsi,
|
|
37392
37853
|
stripModelPrefix,
|
|
37854
|
+
subagentCostTitle,
|
|
37393
37855
|
subtreeIds,
|
|
37394
37856
|
summarizeToolCall,
|
|
37395
37857
|
supervisorSummary,
|
|
@@ -37408,6 +37870,7 @@ var PlannerModelMemory = class {
|
|
|
37408
37870
|
taskStartedNotice,
|
|
37409
37871
|
taskStatusKind,
|
|
37410
37872
|
taskUsageSummary,
|
|
37873
|
+
tasksTotalText,
|
|
37411
37874
|
textHasTaskOps,
|
|
37412
37875
|
textHasTaskQuery,
|
|
37413
37876
|
titledRefs,
|
|
@@ -37425,6 +37888,7 @@ var PlannerModelMemory = class {
|
|
|
37425
37888
|
unsendAll,
|
|
37426
37889
|
unsendLatest,
|
|
37427
37890
|
usageLine,
|
|
37891
|
+
usageText,
|
|
37428
37892
|
waitsForApproval,
|
|
37429
37893
|
walkPlan,
|
|
37430
37894
|
wellKnownBinDirs,
|