@iowarp/clio-coder 0.5.0 → 0.5.1
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/CHANGELOG.md +11 -0
- package/README.md +12 -9
- package/dist/{acp-KS7ARGV6.js → acp-X3N7JDGS.js} +2 -2
- package/dist/{agents-YOGJ2YEJ.js → agents-UYGSMAWR.js} +3 -3
- package/dist/assets/codewiki.json +1 -1
- package/dist/{chunk-EOJPDNUP.js → chunk-3OODBJ45.js} +1 -1
- package/dist/{chunk-R7FGRZNF.js → chunk-4D7QM7S5.js} +2 -2
- package/dist/{chunk-X4CEFOSG.js → chunk-6B76U65H.js} +4 -4
- package/dist/{chunk-LTWH2ACR.js → chunk-7FHSA7VB.js} +5 -5
- package/dist/{chunk-GRHOXDKB.js → chunk-7KI4ZSEB.js} +2 -2
- package/dist/{chunk-LYJJ6546.js → chunk-7VNLPEUG.js} +6 -6
- package/dist/{chunk-D65E25JG.js → chunk-A7G7OUND.js} +2 -2
- package/dist/{chunk-3JLTRPN7.js → chunk-AKQ3N3L4.js} +2 -2
- package/dist/{chunk-AVSR2HKI.js → chunk-BIQLF3NZ.js} +3 -3
- package/dist/{chunk-D3KWFOUT.js → chunk-DGMY2DWE.js} +2 -2
- package/dist/{chunk-IN7DGBVS.js → chunk-FBXTHPNZ.js} +2 -2
- package/dist/{chunk-EX57VVWW.js → chunk-GGEEG7CD.js} +2 -2
- package/dist/{chunk-V35KZDUZ.js → chunk-GW2KVAVI.js} +2 -2
- package/dist/{chunk-SJGS3GDI.js → chunk-KIAUZGKF.js} +2 -2
- package/dist/{chunk-CBAQPTDU.js → chunk-LFYA3HYH.js} +2 -2
- package/dist/{chunk-4VDHLCF5.js → chunk-M5FPIZQW.js} +2 -2
- package/dist/{chunk-R2UUNSNQ.js → chunk-O3ZAF7UY.js} +18 -5
- package/dist/{chunk-QNUQ7K7D.js → chunk-OXWRE2QJ.js} +2 -2
- package/dist/{chunk-55RMDDFM.js → chunk-PGQG7ZMW.js} +2 -2
- package/dist/{chunk-HQVN7G4F.js → chunk-QM7HFPQQ.js} +4 -4
- package/dist/{chunk-ZR5YA6ZF.js → chunk-QTYRMBW6.js} +2 -2
- package/dist/{chunk-3LT34CAM.js → chunk-SAL2SBP4.js} +8 -8
- package/dist/{chunk-OJXOA6YU.js → chunk-TL6LNERH.js} +2 -2
- package/dist/{chunk-U6YTUVOX.js → chunk-VMTCZ2GR.js} +2 -2
- package/dist/{chunk-OQ2EOAHA.js → chunk-VWKR2NN5.js} +5 -5
- package/dist/{chunk-QDUUOZ3S.js → chunk-WAISUJNQ.js} +2 -2
- package/dist/{chunk-XDHUDE5K.js → chunk-XOH3SD7H.js} +3 -3
- package/dist/{chunk-62UK7AGW.js → chunk-ZNTGRDFA.js} +281 -14
- package/dist/cli/index.js +17 -17
- package/dist/{clio-K2PBVCEM.js → clio-Z3JTEL2I.js} +2 -2
- package/dist/{config-Q5J7UBAB.js → config-QYIRZHI7.js} +8 -8
- package/dist/{config-graph-P3555574.js → config-graph-TS3SGGQW.js} +8 -8
- package/dist/{configure-KIJ32ZO6.js → configure-OX7KVJTQ.js} +2 -2
- package/dist/{context-LFBQOSXT.js → context-2BNF6HTP.js} +12 -12
- package/dist/{context-WBH3KSVR.js → context-GTMUGAR4.js} +7 -7
- package/dist/{context-clear-YOHMQ6SQ.js → context-clear-FVJQBBL7.js} +7 -7
- package/dist/{context-working-set-MRAEVDNV.js → context-working-set-MOE72SD6.js} +2 -2
- package/dist/{detail-RHJOTGN4.js → detail-QZM3QAAU.js} +8 -8
- package/dist/{dispatch-runner-NC7PYNIO.js → dispatch-runner-2KSQO5FU.js} +8 -8
- package/dist/{doctor-S5KZ2GTN.js → doctor-2U6YR5NH.js} +3 -3
- package/dist/{eval-3D6M36N7.js → eval-ZKK77JWZ.js} +3 -3
- package/dist/{evidence-6IYN52ET.js → evidence-FMP7KZZ2.js} +7 -7
- package/dist/{evidence-JVECPI3F.js → evidence-PJTOE7JA.js} +7 -7
- package/dist/{evidence-XNVPPLVK.js → evidence-POSEQW4Q.js} +9 -9
- package/dist/{evolve-TLPUDJDM.js → evolve-JT6PAMZE.js} +7 -7
- package/dist/{fleet-WKVBF5SZ.js → fleet-4XR2ZEOF.js} +16 -16
- package/dist/{fleet-6RJWIOEO.js → fleet-6JJKNIMV.js} +8 -8
- package/dist/{fleet-inspect-236FZPRB.js → fleet-inspect-SWJMFVDP.js} +9 -9
- package/dist/{fleet-preflight-NZQHABWO.js → fleet-preflight-KSIHWQAU.js} +4 -4
- package/dist/{fleet-verify-E3RO6A5W.js → fleet-verify-QM6XU2KL.js} +7 -7
- package/dist/{fleet-view-KMZQY7YJ.js → fleet-view-RLKFHFCM.js} +8 -8
- package/dist/gui/reads-worker.js +4 -4
- package/dist/{init-DV5B6HQG.js → init-VBUPHABR.js} +11 -11
- package/dist/{interactive-OXGYZYAS.js → interactive-JCVBPVYT.js} +1597 -503
- package/dist/{inventory-OEGIL6PY.js → inventory-54G7FSVY.js} +8 -8
- package/dist/{mcp-2GSPYQ54.js → mcp-EFCWUPUR.js} +3 -3
- package/dist/{memory-J3STUUIK.js → memory-5BPQSSOP.js} +7 -7
- package/dist/{models-Y3GMFJ2V.js → models-R6HILX6X.js} +3 -3
- package/dist/{monitor-LHNH577J.js → monitor-HVRKSFEQ.js} +10 -10
- package/dist/{orchestrator-DTAPXULA.js → orchestrator-OR7V4YWK.js} +26 -26
- package/dist/{preload-OJBMAIXF.js → preload-KLXFOX2F.js} +7 -7
- package/dist/{run-H64MEVTA.js → run-UIDRUYAY.js} +16 -16
- package/dist/{skills-eval-CQ23MYQK.js → skills-eval-JDAXOPWC.js} +7 -7
- package/dist/{slash-commands-ROG3KIFJ.js → slash-commands-MNKQ5QP3.js} +4 -4
- package/dist/{startup-background-KDWCQMWT.js → startup-background-PJMWTKVO.js} +7 -7
- package/dist/{system-ZDW44ZGX.js → system-IVVZRKGI.js} +2 -2
- package/dist/{targets-AJWA6E7Z.js → targets-RFRUMCQ5.js} +4 -4
- package/dist/{terminal-lease-YFUSDPAD.js → terminal-lease-V2N2BIKQ.js} +2 -2
- package/dist/{usage-XSILDHKF.js → usage-LA33SG3R.js} +9 -9
- package/dist/{wiki-generate-RP22WP5K.js → wiki-generate-HL73TICA.js} +10 -10
- package/dist/worker/entry.js +4 -4
- package/docs/architecture/architecture.md +1 -0
- package/docs/architecture/context-engine.md +2 -2
- package/docs/architecture/observability.md +6 -4
- package/docs/architecture/prompt-envelope-and-tools.md +1 -1
- package/docs/architecture/session-lifecycle.md +1 -1
- package/docs/architecture/tui-design.md +12 -9
- package/docs/gui/parity/02-slash-and-surfaces.md +1 -1
- package/docs/guide/commands-and-modes.md +91 -5
- package/docs/guide/configuration-and-targets.md +2 -2
- package/docs/guide/proactive-memory.md +3 -3
- package/package.json +1 -1
- package/src/cli/usage.ts +2 -2
- package/src/domains/gateway/mcp/client.ts +1 -1
- package/src/domains/observability/background-memory-usage.ts +1 -1
- package/src/domains/observability/cost.ts +3 -3
- package/src/domains/observability/extension.ts +1 -1
- package/src/domains/observability/metrics.ts +1 -1
- package/src/domains/quota/anthropic-max-provider.ts +134 -0
- package/src/domains/quota/anthropic-usage.ts +220 -0
- package/src/domains/quota/antigravity-provider.ts +339 -0
- package/src/domains/quota/cache.ts +87 -0
- package/src/domains/quota/claude-code-provider.ts +169 -0
- package/src/domains/quota/codex-provider.ts +237 -0
- package/src/domains/quota/presentation.ts +240 -0
- package/src/domains/quota/registry.ts +23 -0
- package/src/domains/quota/service.ts +93 -0
- package/src/domains/quota/summary-feed.ts +86 -0
- package/src/domains/quota/types.ts +84 -0
- package/src/domains/session/usage.ts +2 -2
- package/src/engine/acp/commands.ts +1 -1
- package/src/entry/orchestrator.ts +2 -2
- package/src/interactive/chat-loop-messages.ts +2 -2
- package/src/interactive/chat-loop.ts +3 -3
- package/src/interactive/dispatch-board.ts +21 -3
- package/src/interactive/footer/dashboard.ts +13 -0
- package/src/interactive/footer/key-hints.ts +2 -2
- package/src/interactive/footer/pages.ts +55 -20
- package/src/interactive/footer-panel.ts +1 -1
- package/src/interactive/interactive-application.ts +5 -2
- package/src/interactive/interactive-input-runtime.ts +2 -2
- package/src/interactive/interactive-presentation.ts +15 -0
- package/src/interactive/interactive-slash-runtime.ts +2 -2
- package/src/interactive/interactive-tickers.ts +7 -1
- package/src/interactive/overlay-general-openers.ts +12 -8
- package/src/interactive/overlay-key-routing.ts +6 -9
- package/src/interactive/overlay-lifecycle.ts +10 -6
- package/src/interactive/overlay-session-lifecycle.ts +3 -3
- package/src/interactive/quota-view.ts +229 -0
- package/src/interactive/session-last-turn.ts +1 -1
- package/src/interactive/session-usage-reseed.ts +2 -2
- package/src/interactive/side-question.ts +2 -2
- package/src/interactive/slash-commands.ts +8 -8
- package/src/interactive/turn-context.ts +1 -1
- package/src/interactive/turn-prewarm.ts +1 -1
- package/src/interactive/{cost-overlay.ts → usage-overlay.ts} +121 -33
- package/src/interactive/welcome-dashboard.ts +26 -3
|
@@ -23,7 +23,6 @@ import { formatUserTaskHandoff } from "../domains/user-tasks/handoff.js";
|
|
|
23
23
|
import type { UserTasksStore } from "../domains/user-tasks/store.js";
|
|
24
24
|
import type { TUI } from "../engine/tui.js";
|
|
25
25
|
import { type OpenContextOverlayOptions, openContextOverlay } from "./context-overlay.js";
|
|
26
|
-
import { openCostOverlay } from "./cost-overlay.js";
|
|
27
26
|
import {
|
|
28
27
|
type createDispatchBoardView,
|
|
29
28
|
isDispatchBoardRowCancellable,
|
|
@@ -43,6 +42,7 @@ import { openFleetRunApprovalOverlay } from "./overlays/fleet-run-approval.js";
|
|
|
43
42
|
import { openSideQuestionOverlay } from "./overlays/side-question.js";
|
|
44
43
|
import type { ContextClearCommandOptions } from "./slash-commands.js";
|
|
45
44
|
import { openTasksOverlay } from "./tasks-overlay.js";
|
|
45
|
+
import { openUsageOverlay } from "./usage-overlay.js";
|
|
46
46
|
import { type ArtifactProviderDeps, createDefaultArtifactProviders } from "./view/artifacts.js";
|
|
47
47
|
import { openViewOverlay } from "./view/view-overlay.js";
|
|
48
48
|
|
|
@@ -51,6 +51,8 @@ export interface OverlayGeneralOpenersDeps {
|
|
|
51
51
|
tui: TUI;
|
|
52
52
|
transitions: OverlayTransitions;
|
|
53
53
|
observability: ObservabilityContract;
|
|
54
|
+
getQuotaSnapshots?: () => ReadonlyArray<import("../domains/quota/types.js").UsageSnapshot>;
|
|
55
|
+
getDispatchRows?: () => ReadonlyArray<import("./dispatch-board.js").DispatchBoardRow>;
|
|
54
56
|
getSessionId?: () => string | null;
|
|
55
57
|
getContextLedger: () => ContextLedger;
|
|
56
58
|
contextChat: NonNullable<OpenContextOverlayOptions["chat"]>;
|
|
@@ -80,7 +82,7 @@ export interface OverlayGeneralOpenersDeps {
|
|
|
80
82
|
startDispatchBoardTicker: () => void;
|
|
81
83
|
closeOverlay: () => void;
|
|
82
84
|
showOverlayFrame?: typeof showClioOverlayFrame;
|
|
83
|
-
|
|
85
|
+
openUsageOverlay?: typeof openUsageOverlay;
|
|
84
86
|
openContextOverlay?: typeof openContextOverlay;
|
|
85
87
|
openContextResetOverlay?: typeof openContextResetOverlay;
|
|
86
88
|
openTasksOverlay?: typeof openTasksOverlay;
|
|
@@ -121,7 +123,7 @@ export interface OverlayGeneralOpenersDeps {
|
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
export interface OverlayGeneralOpeners {
|
|
124
|
-
|
|
126
|
+
openUsage(): void;
|
|
125
127
|
openContextView(): void;
|
|
126
128
|
openContextReset(): void;
|
|
127
129
|
toggleFooter(): void;
|
|
@@ -136,7 +138,7 @@ export interface OverlayGeneralOpeners {
|
|
|
136
138
|
}
|
|
137
139
|
|
|
138
140
|
export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): OverlayGeneralOpeners {
|
|
139
|
-
const
|
|
141
|
+
const openUsageOverlayFactory = deps.openUsageOverlay ?? openUsageOverlay;
|
|
140
142
|
const openContextOverlayFactory = deps.openContextOverlay ?? openContextOverlay;
|
|
141
143
|
const openContextResetOverlayFactory = deps.openContextResetOverlay ?? openContextResetOverlay;
|
|
142
144
|
const openTasksOverlayFactory = deps.openTasksOverlay ?? openTasksOverlay;
|
|
@@ -146,11 +148,13 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
|
|
|
146
148
|
const showOverlayFrameFactory = deps.showOverlayFrame ?? showClioOverlayFrame;
|
|
147
149
|
const openSideQuestionOverlayFactory = deps.openSideQuestionOverlay ?? openSideQuestionOverlay;
|
|
148
150
|
|
|
149
|
-
const
|
|
151
|
+
const openUsage = (): void => {
|
|
150
152
|
if (deps.transitions.state !== "closed") return;
|
|
151
|
-
deps.transitions.state = "
|
|
152
|
-
deps.transitions.handle =
|
|
153
|
+
deps.transitions.state = "usage";
|
|
154
|
+
deps.transitions.handle = openUsageOverlayFactory(deps.tui, deps.observability, {
|
|
153
155
|
sessionId: deps.getSessionId?.() ?? null,
|
|
156
|
+
...(deps.getQuotaSnapshots ? { getQuotaSnapshots: deps.getQuotaSnapshots } : {}),
|
|
157
|
+
...(deps.getDispatchRows ? { getDispatchRows: deps.getDispatchRows } : {}),
|
|
154
158
|
...(deps.readSessionEntries
|
|
155
159
|
? {
|
|
156
160
|
getSessionEntries: () => deps.readSessionEntries?.() ?? [],
|
|
@@ -525,7 +529,7 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
|
|
|
525
529
|
};
|
|
526
530
|
|
|
527
531
|
return {
|
|
528
|
-
|
|
532
|
+
openUsage,
|
|
529
533
|
openContextView,
|
|
530
534
|
openContextReset,
|
|
531
535
|
toggleFooter,
|
|
@@ -7,7 +7,7 @@ export type OverlayState =
|
|
|
7
7
|
| "permission-confirm"
|
|
8
8
|
| "dispatch-board"
|
|
9
9
|
| "auth"
|
|
10
|
-
| "
|
|
10
|
+
| "usage"
|
|
11
11
|
| "context-view"
|
|
12
12
|
| "context-reset"
|
|
13
13
|
| "tasks"
|
|
@@ -213,8 +213,8 @@ function routeAuthOverlayKey(data: string, deps: CloseOverlayKeyDeps): boolean {
|
|
|
213
213
|
return false;
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
/**
|
|
217
|
-
function
|
|
216
|
+
/** Esc closes an inspection overlay; callers decide how to route its remaining keys. */
|
|
217
|
+
function routeReadOnlyOverlayKey(data: string, deps: CloseOverlayKeyDeps): boolean {
|
|
218
218
|
if (isEscapeKey(data)) {
|
|
219
219
|
deps.closeOverlay();
|
|
220
220
|
return true;
|
|
@@ -262,19 +262,16 @@ export function routeOverlayKey(
|
|
|
262
262
|
return true;
|
|
263
263
|
}
|
|
264
264
|
if (overlayState === "auth") return routeAuthOverlayKey(data, deps);
|
|
265
|
-
if (overlayState === "
|
|
266
|
-
routeCostOverlayKey(data, deps);
|
|
267
|
-
return true;
|
|
268
|
-
}
|
|
265
|
+
if (overlayState === "usage") return routeReadOnlyOverlayKey(data, deps);
|
|
269
266
|
if (overlayState === "context-view") {
|
|
270
|
-
|
|
267
|
+
routeReadOnlyOverlayKey(data, deps);
|
|
271
268
|
return true;
|
|
272
269
|
}
|
|
273
270
|
// Esc is the only key the side-question overlay answers: it aborts a round
|
|
274
271
|
// that is still streaming and closes one that has settled. Everything else is
|
|
275
272
|
// swallowed so a keystroke cannot reach the composer behind it.
|
|
276
273
|
if (overlayState === "side-question") {
|
|
277
|
-
|
|
274
|
+
routeReadOnlyOverlayKey(data, deps);
|
|
278
275
|
return true;
|
|
279
276
|
}
|
|
280
277
|
// The handoff review overlay owns Enter, `e`, the arrows, and Esc itself, so
|
|
@@ -75,6 +75,8 @@ function interopProposalsFor(
|
|
|
75
75
|
|
|
76
76
|
export interface OverlayLifecycleRuntimeDeps {
|
|
77
77
|
app: OverlayLifecycleApplicationDeps;
|
|
78
|
+
getQuotaSnapshots?: () => ReadonlyArray<import("../domains/quota/types.js").UsageSnapshot>;
|
|
79
|
+
getDispatchRows?: () => ReadonlyArray<import("./dispatch-board.js").DispatchBoardRow>;
|
|
78
80
|
tui: import("../engine/tui.js").TUI;
|
|
79
81
|
footer: import("./footer/dashboard.js").FooterDashboardPanel;
|
|
80
82
|
interactiveTickers: import("./interactive-tickers.js").InteractiveTickers;
|
|
@@ -117,7 +119,7 @@ export interface OverlayLifecycleRuntimeDeps {
|
|
|
117
119
|
openTreeOverlay?: typeof import("./overlays/tree-selector.js").openTreeOverlay;
|
|
118
120
|
openMessagePickerOverlay?: typeof import("./overlays/message-picker.js").openMessagePickerOverlay;
|
|
119
121
|
openCwdFallbackOverlay?: typeof import("./overlays/cwd-fallback.js").openCwdFallbackOverlay;
|
|
120
|
-
|
|
122
|
+
openUsageOverlay?: typeof import("./usage-overlay.js").openUsageOverlay;
|
|
121
123
|
openContextOverlay?: typeof import("./context-overlay.js").openContextOverlay;
|
|
122
124
|
openContextResetOverlay?: typeof import("./overlays/context-reset.js").openContextResetOverlay;
|
|
123
125
|
openTasksOverlay?: typeof import("./tasks-overlay.js").openTasksOverlay;
|
|
@@ -140,7 +142,7 @@ export interface OverlayLifecycleController {
|
|
|
140
142
|
isAskUserWaiting(): boolean;
|
|
141
143
|
resetAskUserCancellation(): void;
|
|
142
144
|
refreshSettingsOverlay(): void;
|
|
143
|
-
|
|
145
|
+
openUsageOverlayState(): void;
|
|
144
146
|
openContextViewOverlayState(): void;
|
|
145
147
|
openContextResetOverlayState(): void;
|
|
146
148
|
toggleFooterDashboardState(): void;
|
|
@@ -207,7 +209,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
207
209
|
openTreeOverlay: openTreeOverlayFactory,
|
|
208
210
|
openMessagePickerOverlay: openMessagePickerOverlayFactory,
|
|
209
211
|
openCwdFallbackOverlay: openCwdFallbackOverlayFactory,
|
|
210
|
-
|
|
212
|
+
openUsageOverlay: openUsageOverlayFactory,
|
|
211
213
|
openContextOverlay: openContextOverlayFactory,
|
|
212
214
|
openContextResetOverlay: openContextResetOverlayFactory,
|
|
213
215
|
openTasksOverlay: openTasksOverlayFactory,
|
|
@@ -417,6 +419,8 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
417
419
|
tui,
|
|
418
420
|
transitions: overlayTransitions,
|
|
419
421
|
observability: deps.app.observability,
|
|
422
|
+
...(deps.getQuotaSnapshots ? { getQuotaSnapshots: deps.getQuotaSnapshots } : {}),
|
|
423
|
+
...(deps.getDispatchRows ? { getDispatchRows: deps.getDispatchRows } : {}),
|
|
420
424
|
...(deps.app.getSessionId ? { getSessionId: deps.app.getSessionId } : {}),
|
|
421
425
|
getContextLedger: () => deps.app.chat.contextLedger(),
|
|
422
426
|
contextChat: deps.app.chat,
|
|
@@ -445,7 +449,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
445
449
|
startDispatchBoardTicker: () => interactiveTickers.startDispatchBoardTicker(),
|
|
446
450
|
closeOverlay,
|
|
447
451
|
showOverlayFrame,
|
|
448
|
-
...(
|
|
452
|
+
...(openUsageOverlayFactory ? { openUsageOverlay: openUsageOverlayFactory } : {}),
|
|
449
453
|
...(openContextOverlayFactory ? { openContextOverlay: openContextOverlayFactory } : {}),
|
|
450
454
|
...(openContextResetOverlayFactory ? { openContextResetOverlay: openContextResetOverlayFactory } : {}),
|
|
451
455
|
...(openTasksOverlayFactory ? { openTasksOverlay: openTasksOverlayFactory } : {}),
|
|
@@ -462,7 +466,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
462
466
|
const openResumeOverlayState = overlaySessions.openResume;
|
|
463
467
|
const openTreeOverlayState = overlaySessions.openTree;
|
|
464
468
|
const openMessagePickerOverlayState = overlaySessions.openMessagePicker;
|
|
465
|
-
const
|
|
469
|
+
const openUsageOverlayState = overlayGeneralOpeners.openUsage;
|
|
466
470
|
const openContextViewOverlayState = overlayGeneralOpeners.openContextView;
|
|
467
471
|
const openContextResetOverlayState = overlayGeneralOpeners.openContextReset;
|
|
468
472
|
const toggleFooterDashboardState = overlayGeneralOpeners.toggleFooter;
|
|
@@ -484,7 +488,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
484
488
|
isAskUserWaiting: overlayAskUser.isWaiting,
|
|
485
489
|
resetAskUserCancellation: overlayAskUser.resetCancellation,
|
|
486
490
|
refreshSettingsOverlay: overlayModelSelectors.refreshSettingsOverlay,
|
|
487
|
-
|
|
491
|
+
openUsageOverlayState,
|
|
488
492
|
openContextViewOverlayState,
|
|
489
493
|
openContextResetOverlayState,
|
|
490
494
|
toggleFooterDashboardState,
|
|
@@ -92,7 +92,7 @@ export interface OverlaySessionLifecycle {
|
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
94
|
* The target and model a session started under, so reseeded calls land in the
|
|
95
|
-
* same `/
|
|
95
|
+
* same `/usage` bucket the live path writes to. Without it a single target read
|
|
96
96
|
* as two providers, one under the target id and one under the runtime name.
|
|
97
97
|
*/
|
|
98
98
|
function sessionUsageDefaults(session: SessionContract): { target?: string | null; model?: string | null } {
|
|
@@ -120,7 +120,7 @@ function restorePriorSessionOrReopen(
|
|
|
120
120
|
|
|
121
121
|
export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps): OverlaySessionLifecycle {
|
|
122
122
|
/**
|
|
123
|
-
* Point every running number the footer and `/
|
|
123
|
+
* Point every running number the footer and `/usage` show at one branch. The
|
|
124
124
|
* session total and the last-turn line are read off the same lineage in the
|
|
125
125
|
* same call, so a switch cannot move one and leave the other behind.
|
|
126
126
|
*/
|
|
@@ -244,7 +244,7 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
|
|
|
244
244
|
const replayMessages = buildModelReplayAgentMessagesFromTurns(turns, { uptoTurnId: turnId });
|
|
245
245
|
deps.chat.resetForSession(turnId, replayMessages);
|
|
246
246
|
// The same branch the transcript above was just scoped to. Without the
|
|
247
|
-
// leaf, /
|
|
247
|
+
// leaf, /usage, the footer Σ, and the last-turn line kept reporting the
|
|
248
248
|
// abandoned turns.
|
|
249
249
|
rescopeToBranch(session, turns, turnId);
|
|
250
250
|
} catch (error) {
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { costAggregateForAmount, formatCostAggregate } from "../domains/observability/index.js";
|
|
2
|
+
import {
|
|
3
|
+
foldDuplicateAccounts,
|
|
4
|
+
formatPct,
|
|
5
|
+
primaryWindow,
|
|
6
|
+
type QuotaSeverity,
|
|
7
|
+
severityForPct,
|
|
8
|
+
windowSeverity,
|
|
9
|
+
} from "../domains/quota/presentation.js";
|
|
10
|
+
import type { UsageSnapshot } from "../domains/quota/types.js";
|
|
11
|
+
import { sanitizeCallTargetText } from "../domains/safety/call-target.js";
|
|
12
|
+
import { redactSecretString } from "../domains/safety/redaction.js";
|
|
13
|
+
import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../engine/tui.js";
|
|
14
|
+
import type { DispatchBoardRow } from "./dispatch-board.js";
|
|
15
|
+
import { formatFooterTokens } from "./footer-panel.js";
|
|
16
|
+
import { type ClioToken, clioTheme, formatCompactMs } from "./theme/index.js";
|
|
17
|
+
|
|
18
|
+
const clean = (text: string) => sanitizeCallTargetText(redactSecretString(text));
|
|
19
|
+
const tone = (severity: QuotaSeverity): ClioToken =>
|
|
20
|
+
severity === "critical" ? "error" : severity === "normal" ? "accent" : "warning";
|
|
21
|
+
|
|
22
|
+
/** Filled cells always mean consumed usage, on every surface. */
|
|
23
|
+
export function quotaMeter(usedPct: number, cells: number, severity: QuotaSeverity = severityForPct(usedPct)): string {
|
|
24
|
+
const count = Math.max(1, Math.floor(cells));
|
|
25
|
+
const used = Math.max(0, Math.min(100, Number.isFinite(usedPct) ? usedPct : 0));
|
|
26
|
+
const filled = Math.round((used / 100) * count);
|
|
27
|
+
const theme = clioTheme();
|
|
28
|
+
return theme.fg(tone(severity), "━".repeat(filled)) + theme.fg("frame", "─".repeat(count - filled));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function quotaResetLabel(
|
|
32
|
+
reset: string | null,
|
|
33
|
+
now = Date.now(),
|
|
34
|
+
timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
35
|
+
): string {
|
|
36
|
+
const timestamp = reset === null ? NaN : Date.parse(reset);
|
|
37
|
+
if (!Number.isFinite(timestamp)) return "Reset time not reported";
|
|
38
|
+
const absolute = new Intl.DateTimeFormat("en-US", {
|
|
39
|
+
month: "short",
|
|
40
|
+
day: "numeric",
|
|
41
|
+
hour: "numeric",
|
|
42
|
+
minute: "2-digit",
|
|
43
|
+
timeZone,
|
|
44
|
+
}).format(timestamp);
|
|
45
|
+
const minutes = Math.max(0, Math.ceil((timestamp - now) / 60_000));
|
|
46
|
+
const duration =
|
|
47
|
+
minutes >= 1440
|
|
48
|
+
? `${Math.floor(minutes / 1440)}d ${Math.floor((minutes % 1440) / 60)}h`
|
|
49
|
+
: minutes >= 60
|
|
50
|
+
? `${Math.floor(minutes / 60)}h ${minutes % 60}m`
|
|
51
|
+
: `${minutes}m`;
|
|
52
|
+
return minutes === 0
|
|
53
|
+
? `Reset time passed · awaiting provider update (${absolute} · ${timeZone})`
|
|
54
|
+
: `Resets in ${duration} · ${absolute} (${timeZone})`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Account-wide facts only: never infer a worker's share from changing percentages. */
|
|
58
|
+
export function renderQuotaAccounts(
|
|
59
|
+
snapshots: ReadonlyArray<UsageSnapshot>,
|
|
60
|
+
width: number,
|
|
61
|
+
options: { compact?: boolean; now?: number; timeZone?: string } = {},
|
|
62
|
+
): string[] {
|
|
63
|
+
const theme = clioTheme();
|
|
64
|
+
const now = options.now ?? Date.now();
|
|
65
|
+
const accounts = foldDuplicateAccounts(snapshots).filter((account) => account.status !== "no_credentials");
|
|
66
|
+
if (!accounts.length) return [theme.fg("dim", "Subscriptions · loading")];
|
|
67
|
+
const out: string[] = [];
|
|
68
|
+
for (const account of accounts) {
|
|
69
|
+
const title = clean(`${account.displayName}${account.plan ? ` (${account.plan})` : ""}`);
|
|
70
|
+
const age =
|
|
71
|
+
account.fetchedAt === null ? null : Math.max(0, Math.floor((now - Date.parse(account.fetchedAt)) / 60_000));
|
|
72
|
+
const freshness = account.stale
|
|
73
|
+
? "STALE · last good reading"
|
|
74
|
+
: age === null || !Number.isFinite(age)
|
|
75
|
+
? ""
|
|
76
|
+
: age === 0
|
|
77
|
+
? "updated just now"
|
|
78
|
+
: `updated ${age}m ago`;
|
|
79
|
+
if (options.compact) {
|
|
80
|
+
const window = primaryWindow(account);
|
|
81
|
+
const ordered =
|
|
82
|
+
window && ["warning", "critical"].includes(windowSeverity(window))
|
|
83
|
+
? [window, ...account.windows.filter((item) => item !== window)]
|
|
84
|
+
: account.windows;
|
|
85
|
+
const windows = ordered
|
|
86
|
+
.map(
|
|
87
|
+
(item) =>
|
|
88
|
+
`${item.scope && item.scope !== item.label ? `${item.scope} ` : ""}${item.label} ${formatPct(item.usedPct)} used`,
|
|
89
|
+
)
|
|
90
|
+
.join(" · ");
|
|
91
|
+
const detail =
|
|
92
|
+
account.status !== "ok"
|
|
93
|
+
? (account.message ?? account.status)
|
|
94
|
+
: account.providerId === "local"
|
|
95
|
+
? "$0.00 · no subscription window consumed"
|
|
96
|
+
: windows || account.credits?.display || "no windows reported";
|
|
97
|
+
const meter = window && width >= 65 ? `${quotaMeter(window.usedPct, 8, windowSeverity(window))} ` : "";
|
|
98
|
+
out.push(
|
|
99
|
+
truncateToWidth(
|
|
100
|
+
`${meter}${theme.fg(account.status !== "ok" || account.stale ? "warning" : "muted", `${account.stale ? "STALE · " : ""}${title}`)} ${clean(detail)}`,
|
|
101
|
+
width,
|
|
102
|
+
"…",
|
|
103
|
+
true,
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (out.length) out.push("");
|
|
109
|
+
out.push(theme.style("accent", title, { bold: true }));
|
|
110
|
+
if (freshness) out.push(theme.fg(account.stale ? "warning" : "dim", freshness));
|
|
111
|
+
if (account.status !== "ok")
|
|
112
|
+
out.push(theme.fg("warning", clean(`${account.status} · ${account.message ?? "usage unavailable"}`)));
|
|
113
|
+
let scope: string | undefined;
|
|
114
|
+
for (const window of account.windows) {
|
|
115
|
+
if (window.scope && scope !== window.scope) {
|
|
116
|
+
scope = window.scope;
|
|
117
|
+
out.push(theme.style("muted", clean(scope), { bold: true }));
|
|
118
|
+
}
|
|
119
|
+
const label = `${window.key.startsWith("weekly_scoped.") ? "Weekly" : window.label} · ${formatPct(window.usedPct)} used · ${formatPct(Math.max(0, 100 - window.usedPct))} remaining${window.active ? " · active limit" : ""}`;
|
|
120
|
+
out.push(clean(label));
|
|
121
|
+
out.push(quotaMeter(window.usedPct, Math.max(1, Math.min(48, width)), windowSeverity(window)));
|
|
122
|
+
out.push(theme.fg("dim", quotaResetLabel(window.resetsAt, now, options.timeZone)));
|
|
123
|
+
}
|
|
124
|
+
if (account.credits) {
|
|
125
|
+
out.push(
|
|
126
|
+
clean(
|
|
127
|
+
`${account.providerId === "local" ? "Local inference" : "Usage credits"} · ${account.credits.display}${account.credits.usedPct === null ? "" : ` · ${formatPct(account.credits.usedPct)} used`}`,
|
|
128
|
+
),
|
|
129
|
+
);
|
|
130
|
+
if (account.credits.usedPct !== null) out.push(quotaMeter(account.credits.usedPct, Math.min(48, width)));
|
|
131
|
+
}
|
|
132
|
+
if (account.status === "ok" && account.message)
|
|
133
|
+
out.push(theme.fg(account.stale ? "warning" : "dim", clean(account.message)));
|
|
134
|
+
if (account.status === "ok" && !account.windows.length && !account.credits && !account.message)
|
|
135
|
+
out.push(theme.fg("dim", "Connected · no usage windows reported"));
|
|
136
|
+
if (account.retryAfterSeconds != null) out.push(theme.fg("warning", `Retry after ${account.retryAfterSeconds}s`));
|
|
137
|
+
}
|
|
138
|
+
return out.flatMap((line) => (visibleWidth(line) > width ? wrapTextWithAnsi(line, Math.max(1, width)) : [line]));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The route must share the credential owner read by the quota adapter. */
|
|
142
|
+
function routeAccount(
|
|
143
|
+
route: Pick<DispatchBoardRow, "runtimeId" | "node">,
|
|
144
|
+
snapshots: ReadonlyArray<UsageSnapshot>,
|
|
145
|
+
): UsageSnapshot | undefined {
|
|
146
|
+
if (route.node && route.node !== "local") return undefined;
|
|
147
|
+
const provider = (
|
|
148
|
+
{
|
|
149
|
+
"claude-code": "claude-code",
|
|
150
|
+
"claude-sdk": "claude-code",
|
|
151
|
+
"anthropic-max": "anthropic-max",
|
|
152
|
+
"antigravity-code": "antigravity",
|
|
153
|
+
} as Record<string, string>
|
|
154
|
+
)[route.runtimeId];
|
|
155
|
+
// Clio's openai-codex OAuth storage is independent of Codex CLI auth.json.
|
|
156
|
+
return snapshots.find((item) => item.providerId === provider);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** A weekly badge only when both the account and model group are known. */
|
|
160
|
+
export function routeWeeklyQuota(
|
|
161
|
+
route: Pick<DispatchBoardRow, "runtimeId" | "wireModelId" | "node">,
|
|
162
|
+
snapshots: ReadonlyArray<UsageSnapshot>,
|
|
163
|
+
): { label: string; account: string; severity: QuotaSeverity } | null {
|
|
164
|
+
const account = routeAccount(route, snapshots);
|
|
165
|
+
if (account?.status !== "ok") return null;
|
|
166
|
+
const weekly = account.windows.filter((window) => window.key === "weekly" || window.key.endsWith(".weekly"));
|
|
167
|
+
const model = route.wireModelId.toLowerCase();
|
|
168
|
+
const matches =
|
|
169
|
+
account.providerId === "antigravity"
|
|
170
|
+
? weekly.filter((window) => {
|
|
171
|
+
const scope = window.scope?.toLowerCase() ?? "";
|
|
172
|
+
if (model.includes("gemini")) return scope.includes("gemini");
|
|
173
|
+
if (/claude|opus|sonnet|haiku/.test(model)) return scope.includes("claude");
|
|
174
|
+
if (model.includes("gpt")) return scope.includes("gpt");
|
|
175
|
+
return false;
|
|
176
|
+
})
|
|
177
|
+
: weekly.filter((window) => !window.scope);
|
|
178
|
+
if (matches.length !== 1) return null;
|
|
179
|
+
const window = matches[0];
|
|
180
|
+
if (!window) return null;
|
|
181
|
+
return {
|
|
182
|
+
label: `${account.stale ? "STALE · " : ""}weekly ${formatPct(Math.max(0, Math.min(100, 100 - window.usedPct)))} left`,
|
|
183
|
+
account: account.displayName,
|
|
184
|
+
severity: windowSeverity(window),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function workerQuotaLabel(
|
|
189
|
+
row: Pick<DispatchBoardRow, "runtimeId" | "node">,
|
|
190
|
+
snapshots: ReadonlyArray<UsageSnapshot>,
|
|
191
|
+
): string {
|
|
192
|
+
// Remote workers and generic transports may use different credentials. Only
|
|
193
|
+
// known local credential owners can be associated with this process's reads.
|
|
194
|
+
if (row.node && row.node !== "local") return "Account quota unavailable for remote credentials";
|
|
195
|
+
const account = routeAccount(row, snapshots);
|
|
196
|
+
if (!account) return "Account quota not linked to this runtime";
|
|
197
|
+
if (account.status !== "ok") return `${account.displayName} · ${account.message ?? account.status}`;
|
|
198
|
+
return `Shared ${account.displayName}${account.stale ? " · STALE" : ""} · ${account.windows.map((window) => `${window.scope && window.scope !== window.label ? `${window.scope} ` : ""}${window.label} ${formatPct(Math.max(0, 100 - window.usedPct))} left`).join(" · ") || "no windows reported"}`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function renderWorkerUsage(
|
|
202
|
+
rows: ReadonlyArray<DispatchBoardRow>,
|
|
203
|
+
snapshots: ReadonlyArray<UsageSnapshot>,
|
|
204
|
+
width: number,
|
|
205
|
+
): string[] {
|
|
206
|
+
const theme = clioTheme();
|
|
207
|
+
const out = [theme.fg("dim", "Active and recent workers · recorded tokens and cost · account limits are shared.")];
|
|
208
|
+
if (!rows.length) out.push("", "No worker invocations recorded.");
|
|
209
|
+
for (const row of rows) {
|
|
210
|
+
const cost = formatCostAggregate(costAggregateForAmount(row.costUsd, row.costProvenance)) ?? "cost not reported";
|
|
211
|
+
out.push(
|
|
212
|
+
"",
|
|
213
|
+
theme.style("agent", clean(`${row.agentId} · ${row.status} · ${formatCompactMs(row.elapsedMs)}`), { bold: true }),
|
|
214
|
+
);
|
|
215
|
+
out.push(clean(`${row.targetId} / ${row.wireModelId}${row.node ? ` · ${row.node}` : ""}`));
|
|
216
|
+
out.push(
|
|
217
|
+
row.tokenCount > 0 || row.inputTokens > 0 || row.outputTokens > 0 || row.progress?.inputTokens !== undefined
|
|
218
|
+
? `${formatFooterTokens(row.tokenCount)} recorded tokens · input ${formatFooterTokens(row.inputTokens)} · output ${formatFooterTokens(row.outputTokens)} · ${cost}`
|
|
219
|
+
: `Token usage not reported · ${cost}`,
|
|
220
|
+
);
|
|
221
|
+
if (row.lastContextTokens !== undefined)
|
|
222
|
+
out.push(
|
|
223
|
+
`Context ${formatFooterTokens(row.lastContextTokens)}${row.contextWindow ? ` / ${formatFooterTokens(row.contextWindow)}` : ""}`,
|
|
224
|
+
);
|
|
225
|
+
out.push(theme.fg("muted", clean(workerQuotaLabel(row, snapshots))));
|
|
226
|
+
out.push(theme.fg("dim", clean(`/view dispatch:${row.runId}`)));
|
|
227
|
+
}
|
|
228
|
+
return out.flatMap((line) => wrapTextWithAnsi(line, Math.max(1, width)));
|
|
229
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* The line is a live accumulator: it describes whatever turn this process last
|
|
5
5
|
* ran. current.jsonl is append-only, so after a `/tree` switch that turn can sit
|
|
6
6
|
* on a branch the reader just left, and the line kept describing it while the Σ
|
|
7
|
-
* total and `/
|
|
7
|
+
* total and `/usage` beside it had already been rescoped by the usage reseed. One
|
|
8
8
|
* footer, two branches, no way to tell which line belonged to which.
|
|
9
9
|
*
|
|
10
10
|
* The fold reads the same lineage the reseed does, through
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Rebuild the running usage totals from a session's own ledger.
|
|
3
3
|
*
|
|
4
|
-
* The `/
|
|
4
|
+
* The `/usage` overlay and the footer both render one process-lifetime
|
|
5
5
|
* accumulator, and only `startNewSession` ever reset it. Resuming did not, so a
|
|
6
6
|
* resumed session showed the *previous* session's numbers under the resumed
|
|
7
7
|
* session's id: one process, two sessions, byte-identical totals relabelled.
|
|
@@ -56,7 +56,7 @@ export interface SessionUsageSink {
|
|
|
56
56
|
* `activeLeafTurnId` scopes the fold to one branch, through the same
|
|
57
57
|
* `filterEntriesToActivePath` the transcript replays through. current.jsonl is
|
|
58
58
|
* append-only, so after a `/tree` switch the abandoned sibling turns are still
|
|
59
|
-
* in the file: the transcript stopped showing them and `/
|
|
59
|
+
* in the file: the transcript stopped showing them and `/usage` and the footer Σ
|
|
60
60
|
* kept counting them, which put a session total on screen for turns the reader
|
|
61
61
|
* had just been told were not on this branch. Omitted, the fold follows the
|
|
62
62
|
* newest message's ancestry, the same fallback an offline read of the transcript
|
|
@@ -100,7 +100,7 @@ function positive(value: unknown): number {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
|
-
* Normalize one provider usage object into the shape `/
|
|
103
|
+
* Normalize one provider usage object into the shape `/usage` and the
|
|
104
104
|
* out-of-turn usage store take. Shared with the session pre-warm, which is
|
|
105
105
|
* another call billed beside the session rather than inside it.
|
|
106
106
|
*/
|
|
@@ -144,7 +144,7 @@ function textFromMessage(message: unknown): string {
|
|
|
144
144
|
|
|
145
145
|
/**
|
|
146
146
|
* Run the side-question round. Resolves with the answer, the provider usage
|
|
147
|
-
* (which is still real money and is reported to `/
|
|
147
|
+
* (which is still real money and is reported to `/usage`), and whether the
|
|
148
148
|
* operator aborted it. Provider failures reject; the caller renders the reason
|
|
149
149
|
* in the overlay.
|
|
150
150
|
*/
|
|
@@ -183,7 +183,7 @@ type SlashCommandVariant =
|
|
|
183
183
|
| { kind: "fleet-run-usage"; reason?: string }
|
|
184
184
|
| { kind: "fleet" }
|
|
185
185
|
| { kind: "agents"; connect?: boolean }
|
|
186
|
-
| { kind: "
|
|
186
|
+
| { kind: "usage" }
|
|
187
187
|
| { kind: "context-view" }
|
|
188
188
|
| { kind: "tasks" }
|
|
189
189
|
| { kind: "decisions" }
|
|
@@ -706,7 +706,7 @@ export interface SlashCommandContext {
|
|
|
706
706
|
}>;
|
|
707
707
|
exportShareArchive?: (outPath: string) => { fileCount: number; path: string };
|
|
708
708
|
importShareArchive?: (path: string, options: { dryRun?: boolean; force?: boolean }) => ShareImportPlan;
|
|
709
|
-
|
|
709
|
+
openUsage: () => void;
|
|
710
710
|
/**
|
|
711
711
|
* `/doctor [deep]`: the CLI doctor's findings, rendered as one notice.
|
|
712
712
|
* `deep` adds the live tool probe on the session's targets and the
|
|
@@ -1686,14 +1686,14 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
|
|
|
1686
1686
|
},
|
|
1687
1687
|
},
|
|
1688
1688
|
{
|
|
1689
|
-
name: "
|
|
1690
|
-
description: "Show session token and cost totals",
|
|
1689
|
+
name: "usage",
|
|
1690
|
+
description: "Show subscription quota, credits, and session token and cost totals",
|
|
1691
1691
|
group: "Inspect",
|
|
1692
|
-
kinds: ["
|
|
1692
|
+
kinds: ["usage"],
|
|
1693
1693
|
args: {},
|
|
1694
|
-
fromArgs: fromArgsOrUsage("
|
|
1694
|
+
fromArgs: fromArgsOrUsage("usage", { kind: "usage" }),
|
|
1695
1695
|
handle(_command, ctx) {
|
|
1696
|
-
ctx.
|
|
1696
|
+
ctx.openUsage();
|
|
1697
1697
|
},
|
|
1698
1698
|
},
|
|
1699
1699
|
{
|
|
@@ -2648,7 +2648,7 @@ const COMMAND_ORDER = [
|
|
|
2648
2648
|
"memory",
|
|
2649
2649
|
"view",
|
|
2650
2650
|
"panes",
|
|
2651
|
-
"
|
|
2651
|
+
"usage",
|
|
2652
2652
|
"doctor",
|
|
2653
2653
|
"decisions",
|
|
2654
2654
|
"library",
|
|
@@ -1083,7 +1083,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
|
|
|
1083
1083
|
|
|
1084
1084
|
// The checkpoint identifies the selected summary route; older results
|
|
1085
1085
|
// fall back to the active chat route. The ledger carries usage for reseed; this is the
|
|
1086
|
-
// live sink, so `/
|
|
1086
|
+
// live sink, so `/usage` and the footer move the moment /context compact
|
|
1087
1087
|
// returns instead of staying byte-identical to before it ran.
|
|
1088
1088
|
recordCompactionUsage(agentRuntime, result);
|
|
1089
1089
|
|
|
@@ -72,7 +72,7 @@ export interface TurnPrewarmDeps {
|
|
|
72
72
|
) => Promise<{ ok: true; runtime: AgentRuntime; apiKey: string | undefined } | { ok: false; reason: string }>;
|
|
73
73
|
/** Freeze the session tool surface onto the agent, exactly as a submit does. */
|
|
74
74
|
applySessionTools: (runtime: AgentRuntime) => void;
|
|
75
|
-
/** Report the round's provider usage under its own `/
|
|
75
|
+
/** Report the round's provider usage under its own `/usage` label. */
|
|
76
76
|
recordUsage: (runtime: AgentRuntime, usage: Usage | null) => void;
|
|
77
77
|
/**
|
|
78
78
|
* Claim one in-flight request on the runtime's endpoint for the duration of
|