@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.
Files changed (40) hide show
  1. package/dist/{IFileSystem-C4_mpfze.d.ts → IFileSystem-1PqRCjhu.d.mts} +23 -3
  2. package/dist/{IFileSystem-DUgeWWQN.d.mts → IFileSystem-lTRL_G9Z.d.ts} +23 -3
  3. package/dist/{ModeResolver-4qNjxcDs.d.mts → ModeResolver-6TQXVsde.d.mts} +1 -1
  4. package/dist/{ModeResolver-DGK2rWHE.d.ts → ModeResolver-CoJAJD-f.d.ts} +1 -1
  5. package/dist/{Task-C0o4i3Nn.d.mts → Task-CfBM0wkV.d.mts} +100 -24
  6. package/dist/{Task-C0o4i3Nn.d.ts → Task-CfBM0wkV.d.ts} +100 -24
  7. package/dist/{chunk-H7QX32BH.mjs → chunk-24EL4Z2W.mjs} +2 -2
  8. package/dist/{chunk-DMG4ISUG.mjs → chunk-ESKK5I4F.mjs} +145 -21
  9. package/dist/chunk-ESKK5I4F.mjs.map +1 -0
  10. package/dist/{chunk-LTHKFBKM.mjs → chunk-KWS3VEER.mjs} +3 -3
  11. package/dist/{chunk-EEE2GBLG.mjs → chunk-PFH7WFIB.mjs} +1 -1
  12. package/dist/chunk-PFH7WFIB.mjs.map +1 -0
  13. package/dist/index.d.mts +145 -20
  14. package/dist/index.d.ts +145 -20
  15. package/dist/index.js +668 -112
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.mjs +537 -96
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/parsing.d.mts +2 -2
  20. package/dist/parsing.d.ts +2 -2
  21. package/dist/parsing.js.map +1 -1
  22. package/dist/parsing.mjs +3 -3
  23. package/dist/plan-utils.d.mts +4 -4
  24. package/dist/plan-utils.d.ts +4 -4
  25. package/dist/plan-utils.js +144 -18
  26. package/dist/plan-utils.js.map +1 -1
  27. package/dist/plan-utils.mjs +22 -4
  28. package/dist/{runSettingsText-BgOmLxdw.d.mts → runSettingsText-C1i9wBG1.d.mts} +61 -21
  29. package/dist/{runSettingsText-BgXvajj2.d.ts → runSettingsText-CWNrmF5U.d.ts} +61 -21
  30. package/dist/testing.d.mts +7 -2
  31. package/dist/testing.d.ts +7 -2
  32. package/dist/testing.js +11 -0
  33. package/dist/testing.js.map +1 -1
  34. package/dist/testing.mjs +12 -1
  35. package/dist/testing.mjs.map +1 -1
  36. package/package.json +1 -1
  37. package/dist/chunk-DMG4ISUG.mjs.map +0 -1
  38. package/dist/chunk-EEE2GBLG.mjs.map +0 -1
  39. /package/dist/{chunk-H7QX32BH.mjs.map → chunk-24EL4Z2W.mjs.map} +0 -0
  40. /package/dist/{chunk-LTHKFBKM.mjs.map → chunk-KWS3VEER.mjs.map} +0 -0
@@ -15,7 +15,7 @@ import {
15
15
  stripTrailingCommas,
16
16
  taggedReadOnlyMode,
17
17
  taskWhere
18
- } from "./chunk-H7QX32BH.mjs";
18
+ } from "./chunk-24EL4Z2W.mjs";
19
19
  import {
20
20
  addTaskToPlan,
21
21
  canSetDependencies,
@@ -36,7 +36,7 @@ import {
36
36
  titledTaskRefOf,
37
37
  updateTaskInPlan,
38
38
  walkPlan
39
- } from "./chunk-EEE2GBLG.mjs";
39
+ } from "./chunk-PFH7WFIB.mjs";
40
40
 
41
41
  // src/models/Usage.ts
42
42
  function partedPromptUsage(parts) {
@@ -55,25 +55,66 @@ function addUsage(totals, record) {
55
55
  }
56
56
  if (record.reportedCost) {
57
57
  const { amount, currency } = record.reportedCost;
58
- next.reportedCost = { ...totals.reportedCost, [currency]: (totals.reportedCost?.[currency] ?? 0) + amount };
58
+ const provenance = record.reportedCost.kind || record.sessionBilling ? {
59
+ kind: record.reportedCost.kind ?? "unknown",
60
+ billing: record.sessionBilling?.type ?? "unknown",
61
+ ...record.reportedCost.basis ? { basis: record.reportedCost.basis } : {}
62
+ } : void 0;
63
+ addCost(next, currency, amount, provenance);
59
64
  }
60
65
  return next;
61
66
  }
67
+ function addCost(totals, currency, amount, provenance) {
68
+ const hadCost = totals.reportedCost?.[currency] !== void 0;
69
+ const previous = totals.costProvenance?.[currency];
70
+ totals.reportedCost = { ...totals.reportedCost, [currency]: (totals.reportedCost?.[currency] ?? 0) + amount };
71
+ if (!previous && !provenance) return;
72
+ const unknown = { kind: "unknown", billing: "unknown" };
73
+ const a = previous ?? unknown;
74
+ const b = provenance ?? unknown;
75
+ const merged = !hadCost ? b : {
76
+ kind: a.kind === "estimate" || b.kind === "estimate" ? "estimate" : a.kind === "billed" && b.kind === "billed" ? "billed" : "unknown",
77
+ billing: a.billing === b.billing ? a.billing : "mixed",
78
+ ...a.basis && a.basis === b.basis ? { basis: a.basis } : {}
79
+ };
80
+ totals.costProvenance = { ...totals.costProvenance, [currency]: merged };
81
+ }
62
82
  function addPlannerUsage(usage, record) {
63
- const next = { ...usage, totals: addUsage(usage.totals, record), byModel: addModelUsage(usage.byModel ?? [], record) };
83
+ if (record.compacted) {
84
+ if (record.subagentId || record.aggregate) return usage;
85
+ const next2 = { ...usage };
86
+ if (record.source !== void 0) next2.source = record.source;
87
+ if (record.model !== void 0) {
88
+ if (record.source !== usage.source || record.model !== usage.model) delete next2.contextWindow;
89
+ next2.model = record.model;
90
+ }
91
+ if (record.contextTokens !== void 0) next2.lastPromptTokens = record.contextTokens;
92
+ else delete next2.lastPromptTokens;
93
+ if (record.contextWindow !== void 0) next2.contextWindow = record.contextWindow > 0 ? record.contextWindow : void 0;
94
+ return next2;
95
+ }
96
+ if (record.sessionBilling && !record.reportedCost && record.inputTokens === void 0 && record.outputTokens === void 0 && record.cachedInputTokens === void 0 && record.contextWindow === void 0) {
97
+ return record.subagentId ? usage : { ...usage, sessionBilling: record.sessionBilling, source: record.source };
98
+ }
99
+ record = { ...record, sessionBilling: record.sessionBilling ?? (usage.source === void 0 || record.source === usage.source ? usage.sessionBilling : void 0) };
100
+ const next = { ...usage, ...!record.subagentId && record.sessionBilling ? { sessionBilling: record.sessionBilling } : {}, totals: addUsage(usage.totals, record), byModel: addModelUsage(usage.byModel ?? [], record) };
64
101
  if (record.subagentId) {
65
102
  next.bySubagent = { ...usage.bySubagent, [record.subagentId]: addUsage(usage.bySubagent?.[record.subagentId] ?? {}, record) };
66
103
  return next;
67
104
  }
68
- if (!record.aggregate) {
69
- if (record.source !== usage.source || record.model !== usage.model) {
70
- delete next.lastPromptTokens;
71
- delete next.contextWindow;
72
- }
73
- next.source = record.source;
74
- next.model = record.model;
105
+ if (record.aggregate) {
106
+ if (record.source !== usage.source && !record.sessionBilling) delete next.sessionBilling;
107
+ return next;
108
+ }
109
+ if (record.source !== usage.source || record.model !== usage.model) {
110
+ delete next.lastPromptTokens;
111
+ delete next.contextWindow;
75
112
  }
76
- if (record.inputTokens !== void 0) next.lastPromptTokens = record.inputTokens;
113
+ if (record.source !== usage.source && !record.sessionBilling) delete next.sessionBilling;
114
+ next.source = record.source;
115
+ next.model = record.model;
116
+ const used = record.contextTokens ?? record.inputTokens;
117
+ if (used !== void 0) next.lastPromptTokens = used;
77
118
  if (record.contextWindow !== void 0) next.contextWindow = record.contextWindow > 0 ? record.contextWindow : void 0;
78
119
  return next;
79
120
  }
@@ -84,6 +125,7 @@ function usageLine(usage) {
84
125
  const contextFill = plannerContextFill(usage);
85
126
  return {
86
127
  totals: usage.totals,
128
+ ...usage.sessionBilling ? { sessionBilling: usage.sessionBilling } : {},
87
129
  ...usage.byModel ? { byModel: usage.byModel } : {},
88
130
  ...usage.model ? { model: usage.model } : {},
89
131
  ...usage.bySubagent ? { bySubagent: usage.bySubagent } : {},
@@ -91,8 +133,8 @@ function usageLine(usage) {
91
133
  };
92
134
  }
93
135
  function plannerContextFill(usage) {
94
- if (usage.lastPromptTokens === void 0 || usage.contextWindow === void 0 || usage.contextWindow <= 0) return void 0;
95
- return { usedTokens: usage.lastPromptTokens, windowTokens: usage.contextWindow };
136
+ if (usage.lastPromptTokens === void 0) return void 0;
137
+ return { usedTokens: usage.lastPromptTokens, ...usage.contextWindow !== void 0 && usage.contextWindow > 0 ? { windowTokens: usage.contextWindow } : {} };
96
138
  }
97
139
  function addModelUsage(models, record) {
98
140
  const index = models.findIndex((m) => m.source === record.source && m.model === record.model);
@@ -104,13 +146,14 @@ function startAttemptUsage(attempt, runner, startedAt) {
104
146
  }
105
147
  function addAttemptUsage(usage, record, now) {
106
148
  const folded = addPlannerUsage(usage, record);
107
- return { ...usage, ...folded, lastPromptTokens: folded.lastPromptTokens, contextWindow: folded.contextWindow, wallTimeMs: Math.max(0, Date.parse(now) - Date.parse(usage.startedAt)) };
149
+ return { ...usage, ...folded, lastPromptTokens: folded.lastPromptTokens, contextWindow: folded.contextWindow, wallTimeMs: Math.max(0, Date.parse(usage.endedAt ?? now) - Date.parse(usage.startedAt)) };
108
150
  }
109
151
  function endAttemptUsage(usage, now, runnerSessionCost, sessionId) {
152
+ const endedAt = usage.endedAt ?? now;
110
153
  return {
111
154
  ...usage,
112
- endedAt: now,
113
- wallTimeMs: Math.max(0, Date.parse(now) - Date.parse(usage.startedAt)),
155
+ endedAt,
156
+ wallTimeMs: Math.max(0, Date.parse(endedAt) - Date.parse(usage.startedAt)),
114
157
  ...sessionId ? { sessionId } : {},
115
158
  ...runnerSessionCost ? { runnerSessionCost } : {}
116
159
  };
@@ -123,8 +166,8 @@ function resumedSessionWindow(attempts, runner, attempt, sessionId) {
123
166
  return last ? { source: last.source, model: last.model, contextWindow: last.contextWindow } : void 0;
124
167
  }
125
168
  function addUsageTotals(totals, addition) {
126
- let next = addUsage(totals, { source: "", inputTokens: addition.inputTokens, outputTokens: addition.outputTokens, cachedInputTokens: addition.cachedInputTokens });
127
- for (const [currency, amount] of Object.entries(addition.reportedCost ?? {})) next = addUsage(next, { source: "", reportedCost: { currency, amount } });
169
+ const next = addUsage(totals, { source: "", inputTokens: addition.inputTokens, outputTokens: addition.outputTokens, cachedInputTokens: addition.cachedInputTokens });
170
+ for (const [currency, amount] of Object.entries(addition.reportedCost ?? {})) addCost(next, currency, amount, addition.costProvenance?.[currency]);
128
171
  return next;
129
172
  }
130
173
  function planUsage(tasks) {
@@ -159,6 +202,71 @@ function taskUsageSummary(task) {
159
202
  ...contextFill ? { contextFill } : {}
160
203
  };
161
204
  }
205
+ function formatTokenCount(n) {
206
+ if (n < 1e3) return String(n);
207
+ if (n < 1e4) return `${(n / 1e3).toFixed(1).replace(/\.0$/, "")}k`;
208
+ if (n < 999500) return `${Math.round(n / 1e3)}k`;
209
+ return `${(n / 1e6).toFixed(1).replace(/\.0$/, "")}M`;
210
+ }
211
+ function formatCost(currency, amount) {
212
+ const value = amount >= 1 ? amount.toFixed(2) : (([whole, frac = ""]) => `${whole}.${frac.padEnd(2, "0")}`)(amount.toFixed(4).replace(/0+$/, "").split("."));
213
+ return currency.toLowerCase() === "usd" ? `$${value}` : `${value} ${currency.toUpperCase()}`;
214
+ }
215
+ function contextMeterText(fill) {
216
+ if (!fill) return null;
217
+ const used = `ctx ${formatTokenCount(fill.usedTokens)}`;
218
+ if (!fill.windowTokens) return used;
219
+ const share = fill.usedTokens / fill.windowTokens * 100;
220
+ return `${used} (${share < 1 ? "<1" : Math.round(share)}%)`;
221
+ }
222
+ function contextMeterTitle(fill) {
223
+ const used = fill.usedTokens.toLocaleString("en-US");
224
+ return fill.windowTokens ? `${used} of ${fill.windowTokens.toLocaleString("en-US")} tokens` : `${used} tokens`;
225
+ }
226
+ function costParts(totals) {
227
+ const rank = (currency) => currency.toLowerCase() === "usd" ? "" : currency.toUpperCase();
228
+ return Object.entries(totals.reportedCost ?? {}).sort(([a], [b]) => rank(a).localeCompare(rank(b))).map(([currency, amount]) => {
229
+ const provenance = totals.costProvenance?.[currency];
230
+ const equivalent = provenance?.billing === "subscription";
231
+ return `${provenance?.kind === "estimate" || equivalent ? "\u2248" : ""}${formatCost(currency, amount)}${equivalent ? " API-equivalent" : ""}`;
232
+ });
233
+ }
234
+ function costText(totals) {
235
+ const parts = costParts(totals);
236
+ return parts.length > 0 ? parts.join(" \xB7 ") : null;
237
+ }
238
+ function usageText(usage) {
239
+ const parts = [contextMeterText(usage.contextFill), costText(usage.totals)].filter((p) => p !== null);
240
+ return parts.length > 0 ? parts.join(" \xB7 ") : null;
241
+ }
242
+ function modelCostTitle(byModel) {
243
+ const lines2 = byModel.flatMap((m) => {
244
+ const cost = costParts(m.totals);
245
+ const descriptions = [...new Set(Object.values(m.totals.costProvenance ?? {}).map((p) => {
246
+ const source = m.source === "claude-code" ? "Claude Code" : m.source === "opencode" ? "OpenCode" : m.source === "openrouter" ? "OpenRouter" : m.source;
247
+ const basis = p.basis === "list" ? " at list price" : p.basis === "managed" ? " at managed price" : "";
248
+ const kind = p.kind === "estimate" ? `${source}'s estimate${basis}` : p.kind === "billed" ? `Amount billed by ${source}` : "Reported cost";
249
+ return `${kind}${p.billing === "subscription" ? "; not billed on a subscription" : ""}`;
250
+ }))];
251
+ return cost.length > 0 ? [`${m.source} / ${m.model ?? "unattributed"}: ${cost.join(" \xB7 ")}${descriptions.length ? ` \u2014 ${descriptions.join("; ")}` : ""}`] : [];
252
+ });
253
+ return lines2.length > 0 ? lines2.join("\n") : void 0;
254
+ }
255
+ function subagentCostTitle(bySubagent) {
256
+ return Object.entries(bySubagent).map(([id, totals]) => `${id}: ${costParts(totals).join(" \xB7 ") || "reported no cost"}`).join("\n");
257
+ }
258
+ function runnersOf(tasks) {
259
+ return tasks.flatMap((task) => [...task.attemptUsage ?? [], ...runnersOf(task.subtasks ?? [])]);
260
+ }
261
+ function tasksTotalText(tasks) {
262
+ const attempts = runnersOf(tasks);
263
+ if (attempts.length === 0) return null;
264
+ const costed = (a) => Object.keys(a.totals.reportedCost ?? {}).length > 0;
265
+ 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)));
266
+ 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`];
267
+ const parts = [...costParts(planUsage(tasks).totals), ...note];
268
+ return parts.length > 0 ? `Tasks total: ${parts.join(" \xB7 ")}` : null;
269
+ }
162
270
 
163
271
  // src/services/harness/agentTools.ts
164
272
  var KNOWN_TOOLS = {
@@ -1972,6 +2080,12 @@ function withoutIsolation(tasks) {
1972
2080
  function handoffOf({ type: _type, ...handoff }) {
1973
2081
  return handoff;
1974
2082
  }
2083
+ function stillToMerge(handoff, runMerge) {
2084
+ if (!handoff?.landed.length) return handoff;
2085
+ if (!runMerge) return null;
2086
+ const same = runMerge.landed.length === handoff.landed.length && runMerge.landed.every((t, i) => t.taskId === handoff.landed[i].taskId);
2087
+ return same ? handoff : { ...handoff, repos: runMerge.repos, landed: runMerge.landed };
2088
+ }
1975
2089
  function planTasks(plan) {
1976
2090
  if ("tasks" in plan) return plan.tasks;
1977
2091
  return rejoinPlanTree(plan.pendingTasks, plan.phase === "executing" ? plan.executionLog : []);
@@ -1992,6 +2106,7 @@ function reduceRun(view, input) {
1992
2106
  ...view,
1993
2107
  tasks: applyStatus(view.tasks, new Map(input.tasks.map((t) => [t.id, t]))),
1994
2108
  runMerge: input.runMerge ?? null,
2109
+ handoff: stillToMerge(view.handoff, input.runMerge),
1995
2110
  usage: input.usage
1996
2111
  };
1997
2112
  case "task_started":
@@ -2068,7 +2183,7 @@ function openTurn(view, { turnId, prompt = "", skills = [], wakeUps = [], forced
2068
2183
  wakeUps = wakeUps.slice(adopted.wakeUps);
2069
2184
  if (wakeUps.length === 0) return adopted.prompt || prompt === "" && adopted.wakeUps > 0 ? view : promptLine(view, turnId, prompt, skills, forced);
2070
2185
  const said = prompt === "" || adopted.prompt ? view : promptLine(view, turnId, prompt, skills, forced);
2071
- const at = prompt === "" ? said.blocks.length : findLastIndex(said.blocks, (b) => b.type === "message" && b.role === "user" && b.turnId === turnId);
2186
+ const at = prompt === "" ? said.blocks.length - (said.blocks.at(-1)?.type === "usage" ? 1 : 0) : findLastIndex(said.blocks, (b) => b.type === "message" && b.role === "user" && b.turnId === turnId);
2072
2187
  const updates = wakeUps.map((text2, n) => settledMessage(`b${said.nextId + n}`, "wake_up", text2, turnId));
2073
2188
  return { ...said, blocks: [...said.blocks.slice(0, at), ...updates, ...said.blocks.slice(at)], nextId: said.nextId + updates.length };
2074
2189
  }
@@ -2742,6 +2857,15 @@ export {
2742
2857
  addUsageTotals,
2743
2858
  planUsage,
2744
2859
  taskUsageSummary,
2860
+ formatTokenCount,
2861
+ formatCost,
2862
+ contextMeterText,
2863
+ contextMeterTitle,
2864
+ costText,
2865
+ usageText,
2866
+ modelCostTitle,
2867
+ subagentCostTitle,
2868
+ tasksTotalText,
2745
2869
  sameMessage,
2746
2870
  mapAgentTool,
2747
2871
  normalizeAgentArgs,
@@ -2835,4 +2959,4 @@ export {
2835
2959
  pastGateConfirmation,
2836
2960
  newTaskFields
2837
2961
  };
2838
- //# sourceMappingURL=chunk-DMG4ISUG.mjs.map
2962
+ //# sourceMappingURL=chunk-ESKK5I4F.mjs.map