@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
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
parseEditorBashCommand,
|
|
12
12
|
unguardPastedEditorOperator,
|
|
13
13
|
writeInputWedgeDump
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-O3ZAF7UY.js";
|
|
15
15
|
import {
|
|
16
16
|
OperatorExtensionRuntime
|
|
17
17
|
} from "./chunk-C7AQTPCE.js";
|
|
@@ -68,6 +68,7 @@ import {
|
|
|
68
68
|
isDispatchBoardRowSteerable,
|
|
69
69
|
listRecentModels,
|
|
70
70
|
listViewArtifacts,
|
|
71
|
+
localQuotaSnapshot,
|
|
71
72
|
loopBlockedAuditReason,
|
|
72
73
|
loopBlockedStopReason,
|
|
73
74
|
mergeHandoffDecisions,
|
|
@@ -83,6 +84,8 @@ import {
|
|
|
83
84
|
previewBudget,
|
|
84
85
|
previewRows,
|
|
85
86
|
providerErrorEvidence,
|
|
87
|
+
quotaMeter,
|
|
88
|
+
quotaSummaryLine,
|
|
86
89
|
readWorkerReceiptFacts,
|
|
87
90
|
reasoningFromTally,
|
|
88
91
|
rehydrateChatPanelFromTurns,
|
|
@@ -93,13 +96,16 @@ import {
|
|
|
93
96
|
renderDispatchActivity,
|
|
94
97
|
renderEvictedTokensLine,
|
|
95
98
|
renderHandoffDocument,
|
|
99
|
+
renderQuotaAccounts,
|
|
96
100
|
renderRetryStatus,
|
|
97
101
|
renderToolArguments,
|
|
98
102
|
renderToolAwaitingApproval,
|
|
99
103
|
renderToolExecution,
|
|
100
104
|
renderToolPreview,
|
|
105
|
+
renderWorkerUsage,
|
|
101
106
|
resolveFooterVerb,
|
|
102
107
|
restartRequiredNotice,
|
|
108
|
+
routeWeeklyQuota,
|
|
103
109
|
safeWorkerAnswerText,
|
|
104
110
|
safetyBlockedNotice,
|
|
105
111
|
sessionQuadrant,
|
|
@@ -119,12 +125,12 @@ import {
|
|
|
119
125
|
workspaceQuadrant,
|
|
120
126
|
zipColumnBlocks,
|
|
121
127
|
zipColumns
|
|
122
|
-
} from "./chunk-
|
|
128
|
+
} from "./chunk-ZNTGRDFA.js";
|
|
123
129
|
import "./chunk-YQ6XEFVK.js";
|
|
124
130
|
import {
|
|
125
131
|
extractText,
|
|
126
132
|
isSelfExplainingAbort
|
|
127
|
-
} from "./chunk-
|
|
133
|
+
} from "./chunk-QTYRMBW6.js";
|
|
128
134
|
import "./chunk-ZJPORZTC.js";
|
|
129
135
|
import {
|
|
130
136
|
CLIO_APP_KEYBINDINGS,
|
|
@@ -199,22 +205,22 @@ import {
|
|
|
199
205
|
parseSlashCommand,
|
|
200
206
|
renderArgsSpec,
|
|
201
207
|
workerAskedByModel
|
|
202
|
-
} from "./chunk-
|
|
208
|
+
} from "./chunk-SAL2SBP4.js";
|
|
203
209
|
import "./chunk-DGC6X6XR.js";
|
|
204
210
|
import {
|
|
205
211
|
resolveExtensionCommands
|
|
206
212
|
} from "./chunk-RFCKPS2V.js";
|
|
207
213
|
import "./chunk-QUIJZSDW.js";
|
|
208
214
|
import "./chunk-UCINIRHW.js";
|
|
209
|
-
import "./chunk-
|
|
215
|
+
import "./chunk-TL6LNERH.js";
|
|
210
216
|
import "./chunk-GGPF6LLE.js";
|
|
211
217
|
import {
|
|
212
218
|
formatUserTaskHandoff
|
|
213
219
|
} from "./chunk-ISHUS7HC.js";
|
|
214
|
-
import "./chunk-
|
|
220
|
+
import "./chunk-QM7HFPQQ.js";
|
|
215
221
|
import {
|
|
216
222
|
appendOutOfTurnUsageRow
|
|
217
|
-
} from "./chunk-
|
|
223
|
+
} from "./chunk-GW2KVAVI.js";
|
|
218
224
|
import "./chunk-HOEDA42N.js";
|
|
219
225
|
import {
|
|
220
226
|
SETTINGS_DESCRIPTIONS_BY_ID,
|
|
@@ -287,7 +293,7 @@ import {
|
|
|
287
293
|
loadMemoryRecordsSync,
|
|
288
294
|
proposeMemoryPromotion,
|
|
289
295
|
renderCostAggregate
|
|
290
|
-
} from "./chunk-
|
|
296
|
+
} from "./chunk-7FHSA7VB.js";
|
|
291
297
|
import "./chunk-PECBKCDK.js";
|
|
292
298
|
import "./chunk-OV62D6KO.js";
|
|
293
299
|
import "./chunk-I4ELN5BX.js";
|
|
@@ -309,7 +315,7 @@ import {
|
|
|
309
315
|
ledgerUsageCalls,
|
|
310
316
|
responseModelIdObservationCountsLabel,
|
|
311
317
|
responseModelIdObservationFromRecord
|
|
312
|
-
} from "./chunk-
|
|
318
|
+
} from "./chunk-3OODBJ45.js";
|
|
313
319
|
import "./chunk-BOBCCDJ6.js";
|
|
314
320
|
import {
|
|
315
321
|
describeLocalCapacity
|
|
@@ -334,7 +340,7 @@ import {
|
|
|
334
340
|
import {
|
|
335
341
|
filterEntriesToActivePath
|
|
336
342
|
} from "./chunk-O5F25NIE.js";
|
|
337
|
-
import "./chunk-
|
|
343
|
+
import "./chunk-VMTCZ2GR.js";
|
|
338
344
|
import "./chunk-SD7SHNDW.js";
|
|
339
345
|
import "./chunk-7RET77VB.js";
|
|
340
346
|
import "./chunk-YQ3GFEUY.js";
|
|
@@ -381,7 +387,7 @@ import {
|
|
|
381
387
|
validateFleetGraph
|
|
382
388
|
} from "./chunk-5LP5FNNR.js";
|
|
383
389
|
import "./chunk-NVPXXW2H.js";
|
|
384
|
-
import "./chunk-
|
|
390
|
+
import "./chunk-4D7QM7S5.js";
|
|
385
391
|
import "./chunk-DOUISWEE.js";
|
|
386
392
|
import {
|
|
387
393
|
retiredIntegrityVersionOf
|
|
@@ -396,7 +402,7 @@ import {
|
|
|
396
402
|
decisionFactsForAnswer,
|
|
397
403
|
openDetachedBatchViews,
|
|
398
404
|
runBashCommand
|
|
399
|
-
} from "./chunk-
|
|
405
|
+
} from "./chunk-VWKR2NN5.js";
|
|
400
406
|
import {
|
|
401
407
|
foldSessionTaskHistory,
|
|
402
408
|
foldTaskBoard,
|
|
@@ -422,7 +428,7 @@ import {
|
|
|
422
428
|
requestExecutionRole,
|
|
423
429
|
withAttemptRole
|
|
424
430
|
} from "./chunk-BRCOSE7O.js";
|
|
425
|
-
import "./chunk-
|
|
431
|
+
import "./chunk-PGQG7ZMW.js";
|
|
426
432
|
import "./chunk-L4N3DWNW.js";
|
|
427
433
|
import "./chunk-TDGYTWM6.js";
|
|
428
434
|
import "./chunk-CTFPFW3H.js";
|
|
@@ -480,6 +486,7 @@ import {
|
|
|
480
486
|
resolveProviderReference
|
|
481
487
|
} from "./chunk-SYF7FVBL.js";
|
|
482
488
|
import {
|
|
489
|
+
openAuthStorage,
|
|
483
490
|
targetRequiresAuth
|
|
484
491
|
} from "./chunk-Z4GLJ6J4.js";
|
|
485
492
|
import "./chunk-VNBICSS2.js";
|
|
@@ -3188,273 +3195,6 @@ function openContextOverlay(tui, getLedger, options) {
|
|
|
3188
3195
|
};
|
|
3189
3196
|
}
|
|
3190
3197
|
|
|
3191
|
-
// src/interactive/cost-overlay.ts
|
|
3192
|
-
init_esm_shims();
|
|
3193
|
-
var DEFAULT_CONTENT_WIDTH2 = 80;
|
|
3194
|
-
var COST_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH2 + 4;
|
|
3195
|
-
function formatTokens2(n) {
|
|
3196
|
-
return n.toLocaleString("en-US");
|
|
3197
|
-
}
|
|
3198
|
-
function aggregateCostEntries(entries) {
|
|
3199
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
3200
|
-
for (const entry of entries) {
|
|
3201
|
-
const key = `${entry.providerId}::${entry.attributedModelId}`;
|
|
3202
|
-
const existing = grouped.get(key);
|
|
3203
|
-
if (existing) {
|
|
3204
|
-
existing.row.runs += 1;
|
|
3205
|
-
existing.row.tokens += entry.tokens;
|
|
3206
|
-
existing.row.input += entry.input;
|
|
3207
|
-
existing.row.output += entry.output;
|
|
3208
|
-
existing.row.cacheRead += entry.cacheRead;
|
|
3209
|
-
existing.row.cacheWrite += entry.cacheWrite;
|
|
3210
|
-
existing.row.reasoningTokens += entry.reasoningTokens;
|
|
3211
|
-
existing.row.apiCalls += entry.apiCalls ?? 1;
|
|
3212
|
-
if (entry.label === "side-question") existing.row.sideQuestions += 1;
|
|
3213
|
-
if (entry.label === "handoff") existing.row.handoffs += 1;
|
|
3214
|
-
if (entry.label === "prewarm") existing.row.prewarms += 1;
|
|
3215
|
-
if (entry.label === "background-memory") existing.row.backgroundMemory += 1;
|
|
3216
|
-
if (entry.label === "failed-compaction") existing.row.failedCompaction += 1;
|
|
3217
|
-
for (const requestedModelId of entry.requestedModelIds) existing.requestedModelIds.add(requestedModelId);
|
|
3218
|
-
addResponseModelIdObservationCounts(
|
|
3219
|
-
existing.responseModelIdObservationCounts,
|
|
3220
|
-
entry.responseModelIdObservationCounts
|
|
3221
|
-
);
|
|
3222
|
-
existing.entries.push(entry);
|
|
3223
|
-
continue;
|
|
3224
|
-
}
|
|
3225
|
-
grouped.set(key, {
|
|
3226
|
-
row: {
|
|
3227
|
-
providerId: entry.providerId,
|
|
3228
|
-
attributedModelId: entry.attributedModelId,
|
|
3229
|
-
runs: 1,
|
|
3230
|
-
tokens: entry.tokens,
|
|
3231
|
-
input: entry.input,
|
|
3232
|
-
output: entry.output,
|
|
3233
|
-
cacheRead: entry.cacheRead,
|
|
3234
|
-
cacheWrite: entry.cacheWrite,
|
|
3235
|
-
reasoningTokens: entry.reasoningTokens,
|
|
3236
|
-
apiCalls: entry.apiCalls ?? 1,
|
|
3237
|
-
sideQuestions: entry.label === "side-question" ? 1 : 0,
|
|
3238
|
-
handoffs: entry.label === "handoff" ? 1 : 0,
|
|
3239
|
-
prewarms: entry.label === "prewarm" ? 1 : 0,
|
|
3240
|
-
backgroundMemory: entry.label === "background-memory" ? 1 : 0,
|
|
3241
|
-
failedCompaction: entry.label === "failed-compaction" ? 1 : 0
|
|
3242
|
-
},
|
|
3243
|
-
requestedModelIds: new Set(entry.requestedModelIds),
|
|
3244
|
-
responseModelIdObservationCounts: { ...entry.responseModelIdObservationCounts },
|
|
3245
|
-
entries: [entry]
|
|
3246
|
-
});
|
|
3247
|
-
}
|
|
3248
|
-
const rows = Array.from(grouped.values(), ({ row: row2, entries: entries2, requestedModelIds, responseModelIdObservationCounts }) => ({
|
|
3249
|
-
...row2,
|
|
3250
|
-
requestedModelIds: [...requestedModelIds].sort(),
|
|
3251
|
-
responseModelIdObservationCounts,
|
|
3252
|
-
cost: aggregateCostAmounts(entries2.map((entry) => ({ usd: entry.usd, provenance: entry.provenance })))
|
|
3253
|
-
}));
|
|
3254
|
-
rows.sort((a, b) => {
|
|
3255
|
-
if (a.providerId !== b.providerId) return a.providerId < b.providerId ? -1 : 1;
|
|
3256
|
-
if (a.attributedModelId !== b.attributedModelId) return a.attributedModelId < b.attributedModelId ? -1 : 1;
|
|
3257
|
-
return 0;
|
|
3258
|
-
});
|
|
3259
|
-
return rows;
|
|
3260
|
-
}
|
|
3261
|
-
function sumRows(rows) {
|
|
3262
|
-
return rows.reduce(
|
|
3263
|
-
(acc, row2) => ({
|
|
3264
|
-
runs: acc.runs + row2.runs,
|
|
3265
|
-
tokens: acc.tokens + row2.tokens,
|
|
3266
|
-
input: acc.input + row2.input,
|
|
3267
|
-
output: acc.output + row2.output,
|
|
3268
|
-
cacheRead: acc.cacheRead + row2.cacheRead,
|
|
3269
|
-
cacheWrite: acc.cacheWrite + row2.cacheWrite,
|
|
3270
|
-
reasoningTokens: acc.reasoningTokens + row2.reasoningTokens,
|
|
3271
|
-
apiCalls: acc.apiCalls + row2.apiCalls,
|
|
3272
|
-
sideQuestions: acc.sideQuestions + row2.sideQuestions,
|
|
3273
|
-
handoffs: acc.handoffs + row2.handoffs,
|
|
3274
|
-
prewarms: acc.prewarms + row2.prewarms,
|
|
3275
|
-
backgroundMemory: acc.backgroundMemory + row2.backgroundMemory,
|
|
3276
|
-
failedCompaction: acc.failedCompaction + row2.failedCompaction
|
|
3277
|
-
}),
|
|
3278
|
-
{
|
|
3279
|
-
runs: 0,
|
|
3280
|
-
tokens: 0,
|
|
3281
|
-
input: 0,
|
|
3282
|
-
output: 0,
|
|
3283
|
-
cacheRead: 0,
|
|
3284
|
-
cacheWrite: 0,
|
|
3285
|
-
reasoningTokens: 0,
|
|
3286
|
-
apiCalls: 0,
|
|
3287
|
-
sideQuestions: 0,
|
|
3288
|
-
handoffs: 0,
|
|
3289
|
-
prewarms: 0,
|
|
3290
|
-
backgroundMemory: 0,
|
|
3291
|
-
failedCompaction: 0
|
|
3292
|
-
}
|
|
3293
|
-
);
|
|
3294
|
-
}
|
|
3295
|
-
function cacheReadValue(cacheRead, apiCalls) {
|
|
3296
|
-
if (apiCalls > 1 && cacheRead > 0) {
|
|
3297
|
-
return [formatTokens2(cacheRead), `(avg/call ${formatTokens2(Math.round(cacheRead / apiCalls))})`];
|
|
3298
|
-
}
|
|
3299
|
-
return [formatTokens2(cacheRead)];
|
|
3300
|
-
}
|
|
3301
|
-
function reasoningValue(reasoningTokens) {
|
|
3302
|
-
return [formatTokens2(reasoningTokens), "provider-reported only"];
|
|
3303
|
-
}
|
|
3304
|
-
function kvBlock(entries) {
|
|
3305
|
-
const theme2 = clioTheme();
|
|
3306
|
-
const keyWidth = entries.reduce((max, [key]) => Math.max(max, key.length), 0);
|
|
3307
|
-
const valueWidth = entries.reduce((max, [, value]) => Math.max(max, value.length), 0);
|
|
3308
|
-
return entries.map(([key, value, annotation]) => {
|
|
3309
|
-
const row2 = `${theme2.fg("dim", key.padEnd(keyWidth))} ${theme2.fg("muted", value.padStart(valueWidth))}`;
|
|
3310
|
-
return annotation ? `${row2} ${theme2.fg("dim", annotation)}` : row2;
|
|
3311
|
-
});
|
|
3312
|
-
}
|
|
3313
|
-
function summaryBlock(totalCost, totalTokens, rows, promptCache) {
|
|
3314
|
-
const totals = sumRows(rows);
|
|
3315
|
-
const resolvedTotal = totalTokens > 0 ? totalTokens : totals.tokens;
|
|
3316
|
-
const cost = formatCostAggregate(totalCost);
|
|
3317
|
-
const cacheRows = promptCache !== null && hasPromptCacheTelemetry(promptCache) ? [
|
|
3318
|
-
[
|
|
3319
|
-
"uncached prefill",
|
|
3320
|
-
promptCache.uncachedPrefillTokens === null ? "not reported" : formatTokens2(promptCache.uncachedPrefillTokens)
|
|
3321
|
-
],
|
|
3322
|
-
[
|
|
3323
|
-
"cache verdicts",
|
|
3324
|
-
`hot ${promptCache.verdictCounts.hot} \xB7 partial ${promptCache.verdictCounts.partial} \xB7 cold ${promptCache.verdictCounts.cold} \xB7 small ${promptCache.verdictCounts.small} \xB7 unknown ${promptCache.verdictCounts.unknown}`
|
|
3325
|
-
]
|
|
3326
|
-
] : [];
|
|
3327
|
-
return kvBlock([
|
|
3328
|
-
[
|
|
3329
|
-
"turns",
|
|
3330
|
-
formatTokens2(
|
|
3331
|
-
totals.runs - totals.sideQuestions - totals.handoffs - totals.prewarms - totals.backgroundMemory - totals.failedCompaction
|
|
3332
|
-
)
|
|
3333
|
-
],
|
|
3334
|
-
["model calls", formatTokens2(totals.apiCalls)],
|
|
3335
|
-
...totals.sideQuestions > 0 ? [["side questions", formatTokens2(totals.sideQuestions)]] : [],
|
|
3336
|
-
...totals.handoffs > 0 ? [["handoffs", formatTokens2(totals.handoffs)]] : [],
|
|
3337
|
-
...totals.prewarms > 0 ? [["pre-warms", formatTokens2(totals.prewarms)]] : [],
|
|
3338
|
-
...totals.backgroundMemory > 0 ? [["memory steps", formatTokens2(totals.backgroundMemory)]] : [],
|
|
3339
|
-
...totals.failedCompaction > 0 ? [
|
|
3340
|
-
["failed compaction calls", formatTokens2(totals.failedCompaction)],
|
|
3341
|
-
["compaction usage", "known subtotals; missing usage is unknown"]
|
|
3342
|
-
] : [],
|
|
3343
|
-
...cost === null ? [] : [["cost", cost]],
|
|
3344
|
-
["input", formatTokens2(totals.input)],
|
|
3345
|
-
["output", formatTokens2(totals.output)],
|
|
3346
|
-
["reasoning", ...reasoningValue(totals.reasoningTokens)],
|
|
3347
|
-
["cache read", ...cacheReadValue(totals.cacheRead, totals.apiCalls)],
|
|
3348
|
-
["cache write", formatTokens2(totals.cacheWrite)],
|
|
3349
|
-
...cacheRows,
|
|
3350
|
-
["processed", `${formatTokens2(resolvedTotal)} tokens`]
|
|
3351
|
-
]);
|
|
3352
|
-
}
|
|
3353
|
-
function modelBlock(row2) {
|
|
3354
|
-
const cost = formatCostAggregate(row2.cost);
|
|
3355
|
-
return kvBlock([
|
|
3356
|
-
["requested model ids", row2.requestedModelIds.join(", ")],
|
|
3357
|
-
["response model id observation", responseModelIdObservationCountsLabel(row2.responseModelIdObservationCounts)],
|
|
3358
|
-
[
|
|
3359
|
-
"turns",
|
|
3360
|
-
formatTokens2(
|
|
3361
|
-
row2.runs - row2.sideQuestions - row2.handoffs - row2.prewarms - row2.backgroundMemory - row2.failedCompaction
|
|
3362
|
-
)
|
|
3363
|
-
],
|
|
3364
|
-
["model calls", formatTokens2(row2.apiCalls)],
|
|
3365
|
-
...row2.sideQuestions > 0 ? [["side questions", formatTokens2(row2.sideQuestions)]] : [],
|
|
3366
|
-
...row2.handoffs > 0 ? [["handoffs", formatTokens2(row2.handoffs)]] : [],
|
|
3367
|
-
...row2.prewarms > 0 ? [["pre-warms", formatTokens2(row2.prewarms)]] : [],
|
|
3368
|
-
...row2.backgroundMemory > 0 ? [["memory steps", formatTokens2(row2.backgroundMemory)]] : [],
|
|
3369
|
-
...row2.failedCompaction > 0 ? [["failed compaction calls", formatTokens2(row2.failedCompaction)]] : [],
|
|
3370
|
-
...cost === null ? [] : [["cost", cost]],
|
|
3371
|
-
["input", formatTokens2(row2.input)],
|
|
3372
|
-
["output", formatTokens2(row2.output)],
|
|
3373
|
-
["reasoning", ...reasoningValue(row2.reasoningTokens)],
|
|
3374
|
-
["cache read", ...cacheReadValue(row2.cacheRead, row2.apiCalls)],
|
|
3375
|
-
["cache write", formatTokens2(row2.cacheWrite)],
|
|
3376
|
-
["processed", `${formatTokens2(row2.tokens)} tokens`]
|
|
3377
|
-
]);
|
|
3378
|
-
}
|
|
3379
|
-
function formatCostOverlayBodyLines(totalCost, totalTokens, rows, contentWidth, promptCache = null) {
|
|
3380
|
-
const theme2 = clioTheme();
|
|
3381
|
-
const lines = [];
|
|
3382
|
-
for (const line of summaryBlock(totalCost, totalTokens, rows, promptCache)) {
|
|
3383
|
-
lines.push(line);
|
|
3384
|
-
}
|
|
3385
|
-
lines.push(rule(theme2, contentWidth));
|
|
3386
|
-
if (rows.length === 0) {
|
|
3387
|
-
lines.push(theme2.fg("muted", "no token usage recorded for this session"));
|
|
3388
|
-
} else {
|
|
3389
|
-
for (const [index, row2] of rows.entries()) {
|
|
3390
|
-
if (index > 0) lines.push("");
|
|
3391
|
-
lines.push(theme2.style("accent", `${row2.providerId} \xB7 attributed model ${row2.attributedModelId}`, { bold: true }));
|
|
3392
|
-
for (const line of modelBlock(row2)) {
|
|
3393
|
-
lines.push(line);
|
|
3394
|
-
}
|
|
3395
|
-
}
|
|
3396
|
-
}
|
|
3397
|
-
return lines;
|
|
3398
|
-
}
|
|
3399
|
-
function buildCostSnapshot(observability, sessionId, snapshot, getSessionEntries) {
|
|
3400
|
-
const entries = observability.costEntries();
|
|
3401
|
-
const rows = aggregateCostEntries(entries);
|
|
3402
|
-
const totalTokens = rows.reduce((sum, r) => sum + r.tokens, 0);
|
|
3403
|
-
return {
|
|
3404
|
-
sessionId,
|
|
3405
|
-
totalCost: snapshot?.session.cost ?? observability.sessionCostSummary(),
|
|
3406
|
-
totalTokens,
|
|
3407
|
-
rows,
|
|
3408
|
-
promptCache: getSessionEntries ? foldPromptCacheTelemetry(getSessionEntries()) : null
|
|
3409
|
-
};
|
|
3410
|
-
}
|
|
3411
|
-
var CostOverlayBody = class {
|
|
3412
|
-
constructor(getSnapshot) {
|
|
3413
|
-
this.getSnapshot = getSnapshot;
|
|
3414
|
-
}
|
|
3415
|
-
getSnapshot;
|
|
3416
|
-
render(width) {
|
|
3417
|
-
const contentWidth = Math.max(1, Math.floor(width));
|
|
3418
|
-
const snapshot = this.getSnapshot();
|
|
3419
|
-
return formatCostOverlayBodyLines(
|
|
3420
|
-
snapshot.totalCost,
|
|
3421
|
-
snapshot.totalTokens,
|
|
3422
|
-
snapshot.rows,
|
|
3423
|
-
contentWidth,
|
|
3424
|
-
snapshot.promptCache
|
|
3425
|
-
);
|
|
3426
|
-
}
|
|
3427
|
-
invalidate() {
|
|
3428
|
-
}
|
|
3429
|
-
};
|
|
3430
|
-
function openCostOverlay(tui, observability, options) {
|
|
3431
|
-
const sessionId = options?.sessionId ?? null;
|
|
3432
|
-
let latest = observability.snapshot();
|
|
3433
|
-
const body = new CostOverlayBody(
|
|
3434
|
-
() => buildCostSnapshot(observability, sessionId, latest, options?.getSessionEntries)
|
|
3435
|
-
);
|
|
3436
|
-
const handle = showClioOverlayFrame(tui, body, {
|
|
3437
|
-
anchor: "center",
|
|
3438
|
-
width: COST_OVERLAY_WIDTH,
|
|
3439
|
-
// Not derived from the title: that one carries the session id.
|
|
3440
|
-
markerId: "cost",
|
|
3441
|
-
title: sessionId && sessionId.length > 0 ? `Session usage (${sessionId})` : "Session usage",
|
|
3442
|
-
footerHint: buildHint([])
|
|
3443
|
-
});
|
|
3444
|
-
const unsubscribe = observability.subscribe((snapshot) => {
|
|
3445
|
-
latest = snapshot;
|
|
3446
|
-
body.invalidate();
|
|
3447
|
-
tui.requestRender();
|
|
3448
|
-
});
|
|
3449
|
-
return {
|
|
3450
|
-
...handle,
|
|
3451
|
-
hide() {
|
|
3452
|
-
unsubscribe();
|
|
3453
|
-
handle.hide();
|
|
3454
|
-
}
|
|
3455
|
-
};
|
|
3456
|
-
}
|
|
3457
|
-
|
|
3458
3198
|
// src/interactive/fleet-run-preview.ts
|
|
3459
3199
|
init_esm_shims();
|
|
3460
3200
|
function defaultLoad(workspaceRoot, name) {
|
|
@@ -4338,9 +4078,9 @@ function openListOverlay(tui, options) {
|
|
|
4338
4078
|
}
|
|
4339
4079
|
|
|
4340
4080
|
// src/interactive/memory-overlay.ts
|
|
4341
|
-
var
|
|
4081
|
+
var DEFAULT_CONTENT_WIDTH2 = 96;
|
|
4342
4082
|
var REFRESH_MS = 1e3;
|
|
4343
|
-
var MEMORY_OVERLAY_WIDTH =
|
|
4083
|
+
var MEMORY_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH2 + 4;
|
|
4344
4084
|
var EMPTY_MESSAGE = "no approved lessons, task-bank entries, or memory steps captured yet.";
|
|
4345
4085
|
function formatMemoryStatusLine(status, contentWidth) {
|
|
4346
4086
|
const theme2 = clioTheme();
|
|
@@ -4707,8 +4447,8 @@ function openContextResetOverlay(tui, deps) {
|
|
|
4707
4447
|
|
|
4708
4448
|
// src/interactive/overlays/decisions.ts
|
|
4709
4449
|
init_esm_shims();
|
|
4710
|
-
var
|
|
4711
|
-
var DECISIONS_OVERLAY_WIDTH =
|
|
4450
|
+
var DEFAULT_CONTENT_WIDTH3 = 88;
|
|
4451
|
+
var DECISIONS_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH3 + 4;
|
|
4712
4452
|
function formatDecisionCorrectionTurn(selection, correction) {
|
|
4713
4453
|
return `Decision "${selection.label}" (previously: ${selection.value}) is superseded by the operator. New direction: ${correction}. Acknowledge and adjust the plan.`;
|
|
4714
4454
|
}
|
|
@@ -4772,7 +4512,7 @@ function decisionLines(decision, selected, expanded, width) {
|
|
|
4772
4512
|
}
|
|
4773
4513
|
return lines;
|
|
4774
4514
|
}
|
|
4775
|
-
function formatDecisionsOverlayBodyLines(interviews, selectedIndex = 0, expandedKey = null, contentWidth =
|
|
4515
|
+
function formatDecisionsOverlayBodyLines(interviews, selectedIndex = 0, expandedKey = null, contentWidth = DEFAULT_CONTENT_WIDTH3, now = Date.now()) {
|
|
4776
4516
|
const width = Math.max(1, Math.floor(contentWidth));
|
|
4777
4517
|
const theme2 = clioTheme();
|
|
4778
4518
|
if (interviews.length === 0) {
|
|
@@ -5171,9 +4911,9 @@ function openSideQuestionOverlay(tui, options) {
|
|
|
5171
4911
|
// src/interactive/tasks-overlay.ts
|
|
5172
4912
|
init_esm_shims();
|
|
5173
4913
|
import { isAbsolute, relative as relative3, resolve } from "node:path";
|
|
5174
|
-
var
|
|
4914
|
+
var DEFAULT_CONTENT_WIDTH4 = 84;
|
|
5175
4915
|
var REFRESH_MS2 = 1e3;
|
|
5176
|
-
var TASKS_OVERLAY_WIDTH =
|
|
4916
|
+
var TASKS_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH4 + 4;
|
|
5177
4917
|
var STATUS_PRESENTATION = {
|
|
5178
4918
|
pending: { glyph: GLYPH.queued, token: "dim", label: "pending" },
|
|
5179
4919
|
active: { glyph: GLYPH.running, token: "accent", label: "active" },
|
|
@@ -5243,7 +4983,7 @@ function taskReceiptRows(task, width) {
|
|
|
5243
4983
|
}
|
|
5244
4984
|
return [];
|
|
5245
4985
|
}
|
|
5246
|
-
function formatTasksOverlayBodyLines(board, contentWidth =
|
|
4986
|
+
function formatTasksOverlayBodyLines(board, contentWidth = DEFAULT_CONTENT_WIDTH4, selectedTaskId) {
|
|
5247
4987
|
const theme2 = clioTheme();
|
|
5248
4988
|
const width = Math.max(1, Math.floor(contentWidth));
|
|
5249
4989
|
if (!board || board.tasks.length === 0) {
|
|
@@ -5326,7 +5066,7 @@ var USER_TASK_PRESENTATION = {
|
|
|
5326
5066
|
done: { glyph: GLYPH.ok, token: "success" },
|
|
5327
5067
|
dropped: { glyph: GLYPH.cancelled, token: "dim" }
|
|
5328
5068
|
};
|
|
5329
|
-
function formatCompositeTasksOverlayBodyLines(state, contentWidth =
|
|
5069
|
+
function formatCompositeTasksOverlayBodyLines(state, contentWidth = DEFAULT_CONTENT_WIDTH4) {
|
|
5330
5070
|
const width = Math.max(1, Math.floor(contentWidth));
|
|
5331
5071
|
const theme2 = clioTheme();
|
|
5332
5072
|
const rows = selectableRows2(state);
|
|
@@ -5535,46 +5275,397 @@ var TasksOverlayBody = class {
|
|
|
5535
5275
|
} catch (error) {
|
|
5536
5276
|
this.status = error instanceof Error ? error.message : String(error);
|
|
5537
5277
|
}
|
|
5538
|
-
this.requestRender();
|
|
5539
|
-
};
|
|
5540
|
-
this.addInput = input;
|
|
5541
|
-
this.status = "";
|
|
5542
|
-
this.requestRender();
|
|
5543
|
-
}
|
|
5544
|
-
mutateUserTask(status, id, mutation) {
|
|
5545
|
-
if (!mutation) {
|
|
5546
|
-
this.status = "Operator task actions are unavailable.";
|
|
5547
|
-
this.requestRender();
|
|
5548
|
-
return;
|
|
5278
|
+
this.requestRender();
|
|
5279
|
+
};
|
|
5280
|
+
this.addInput = input;
|
|
5281
|
+
this.status = "";
|
|
5282
|
+
this.requestRender();
|
|
5283
|
+
}
|
|
5284
|
+
mutateUserTask(status, id, mutation) {
|
|
5285
|
+
if (!mutation) {
|
|
5286
|
+
this.status = "Operator task actions are unavailable.";
|
|
5287
|
+
this.requestRender();
|
|
5288
|
+
return;
|
|
5289
|
+
}
|
|
5290
|
+
try {
|
|
5291
|
+
mutation(id);
|
|
5292
|
+
this.refreshUserTasks();
|
|
5293
|
+
this.status = `Operator task ${id} ${status}.`;
|
|
5294
|
+
} catch (error) {
|
|
5295
|
+
this.status = error instanceof Error ? error.message : String(error);
|
|
5296
|
+
}
|
|
5297
|
+
this.requestRender();
|
|
5298
|
+
}
|
|
5299
|
+
requestRender() {
|
|
5300
|
+
this.options.requestRender?.();
|
|
5301
|
+
}
|
|
5302
|
+
};
|
|
5303
|
+
function openTasksOverlay(tui, getBoard, options = {}) {
|
|
5304
|
+
const body = new TasksOverlayBody(getBoard, options);
|
|
5305
|
+
const handle = showClioOverlayFrame(tui, body, {
|
|
5306
|
+
anchor: "center",
|
|
5307
|
+
width: TASKS_OVERLAY_WIDTH,
|
|
5308
|
+
markerId: "tasks",
|
|
5309
|
+
title: () => "Tasks",
|
|
5310
|
+
footerHint: () => body.footerHint()
|
|
5311
|
+
});
|
|
5312
|
+
const timer = setInterval(() => tui.requestRender(), REFRESH_MS2);
|
|
5313
|
+
timer.unref?.();
|
|
5314
|
+
return {
|
|
5315
|
+
...handle,
|
|
5316
|
+
hide() {
|
|
5317
|
+
clearInterval(timer);
|
|
5318
|
+
handle.hide();
|
|
5319
|
+
}
|
|
5320
|
+
};
|
|
5321
|
+
}
|
|
5322
|
+
|
|
5323
|
+
// src/interactive/usage-overlay.ts
|
|
5324
|
+
init_esm_shims();
|
|
5325
|
+
var DEFAULT_CONTENT_WIDTH5 = 104;
|
|
5326
|
+
var USAGE_OVERLAY_WIDTH = DEFAULT_CONTENT_WIDTH5 + 4;
|
|
5327
|
+
function formatTokens2(n) {
|
|
5328
|
+
return n.toLocaleString("en-US");
|
|
5329
|
+
}
|
|
5330
|
+
function aggregateCostEntries(entries) {
|
|
5331
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
5332
|
+
for (const entry of entries) {
|
|
5333
|
+
const key = `${entry.providerId}::${entry.attributedModelId}`;
|
|
5334
|
+
const existing = grouped.get(key);
|
|
5335
|
+
if (existing) {
|
|
5336
|
+
existing.row.runs += 1;
|
|
5337
|
+
existing.row.tokens += entry.tokens;
|
|
5338
|
+
existing.row.input += entry.input;
|
|
5339
|
+
existing.row.output += entry.output;
|
|
5340
|
+
existing.row.cacheRead += entry.cacheRead;
|
|
5341
|
+
existing.row.cacheWrite += entry.cacheWrite;
|
|
5342
|
+
existing.row.reasoningTokens += entry.reasoningTokens;
|
|
5343
|
+
existing.row.apiCalls += entry.apiCalls ?? 1;
|
|
5344
|
+
if (entry.label === "side-question") existing.row.sideQuestions += 1;
|
|
5345
|
+
if (entry.label === "handoff") existing.row.handoffs += 1;
|
|
5346
|
+
if (entry.label === "prewarm") existing.row.prewarms += 1;
|
|
5347
|
+
if (entry.label === "background-memory") existing.row.backgroundMemory += 1;
|
|
5348
|
+
if (entry.label === "failed-compaction") existing.row.failedCompaction += 1;
|
|
5349
|
+
for (const requestedModelId of entry.requestedModelIds) existing.requestedModelIds.add(requestedModelId);
|
|
5350
|
+
addResponseModelIdObservationCounts(
|
|
5351
|
+
existing.responseModelIdObservationCounts,
|
|
5352
|
+
entry.responseModelIdObservationCounts
|
|
5353
|
+
);
|
|
5354
|
+
existing.entries.push(entry);
|
|
5355
|
+
continue;
|
|
5356
|
+
}
|
|
5357
|
+
grouped.set(key, {
|
|
5358
|
+
row: {
|
|
5359
|
+
providerId: entry.providerId,
|
|
5360
|
+
attributedModelId: entry.attributedModelId,
|
|
5361
|
+
runs: 1,
|
|
5362
|
+
tokens: entry.tokens,
|
|
5363
|
+
input: entry.input,
|
|
5364
|
+
output: entry.output,
|
|
5365
|
+
cacheRead: entry.cacheRead,
|
|
5366
|
+
cacheWrite: entry.cacheWrite,
|
|
5367
|
+
reasoningTokens: entry.reasoningTokens,
|
|
5368
|
+
apiCalls: entry.apiCalls ?? 1,
|
|
5369
|
+
sideQuestions: entry.label === "side-question" ? 1 : 0,
|
|
5370
|
+
handoffs: entry.label === "handoff" ? 1 : 0,
|
|
5371
|
+
prewarms: entry.label === "prewarm" ? 1 : 0,
|
|
5372
|
+
backgroundMemory: entry.label === "background-memory" ? 1 : 0,
|
|
5373
|
+
failedCompaction: entry.label === "failed-compaction" ? 1 : 0
|
|
5374
|
+
},
|
|
5375
|
+
requestedModelIds: new Set(entry.requestedModelIds),
|
|
5376
|
+
responseModelIdObservationCounts: { ...entry.responseModelIdObservationCounts },
|
|
5377
|
+
entries: [entry]
|
|
5378
|
+
});
|
|
5379
|
+
}
|
|
5380
|
+
const rows = Array.from(grouped.values(), ({ row: row2, entries: entries2, requestedModelIds, responseModelIdObservationCounts }) => ({
|
|
5381
|
+
...row2,
|
|
5382
|
+
requestedModelIds: [...requestedModelIds].sort(),
|
|
5383
|
+
responseModelIdObservationCounts,
|
|
5384
|
+
cost: aggregateCostAmounts(entries2.map((entry) => ({ usd: entry.usd, provenance: entry.provenance })))
|
|
5385
|
+
}));
|
|
5386
|
+
rows.sort((a, b) => {
|
|
5387
|
+
if (a.providerId !== b.providerId) return a.providerId < b.providerId ? -1 : 1;
|
|
5388
|
+
if (a.attributedModelId !== b.attributedModelId) return a.attributedModelId < b.attributedModelId ? -1 : 1;
|
|
5389
|
+
return 0;
|
|
5390
|
+
});
|
|
5391
|
+
return rows;
|
|
5392
|
+
}
|
|
5393
|
+
function sumRows(rows) {
|
|
5394
|
+
return rows.reduce(
|
|
5395
|
+
(acc, row2) => ({
|
|
5396
|
+
runs: acc.runs + row2.runs,
|
|
5397
|
+
tokens: acc.tokens + row2.tokens,
|
|
5398
|
+
input: acc.input + row2.input,
|
|
5399
|
+
output: acc.output + row2.output,
|
|
5400
|
+
cacheRead: acc.cacheRead + row2.cacheRead,
|
|
5401
|
+
cacheWrite: acc.cacheWrite + row2.cacheWrite,
|
|
5402
|
+
reasoningTokens: acc.reasoningTokens + row2.reasoningTokens,
|
|
5403
|
+
apiCalls: acc.apiCalls + row2.apiCalls,
|
|
5404
|
+
sideQuestions: acc.sideQuestions + row2.sideQuestions,
|
|
5405
|
+
handoffs: acc.handoffs + row2.handoffs,
|
|
5406
|
+
prewarms: acc.prewarms + row2.prewarms,
|
|
5407
|
+
backgroundMemory: acc.backgroundMemory + row2.backgroundMemory,
|
|
5408
|
+
failedCompaction: acc.failedCompaction + row2.failedCompaction
|
|
5409
|
+
}),
|
|
5410
|
+
{
|
|
5411
|
+
runs: 0,
|
|
5412
|
+
tokens: 0,
|
|
5413
|
+
input: 0,
|
|
5414
|
+
output: 0,
|
|
5415
|
+
cacheRead: 0,
|
|
5416
|
+
cacheWrite: 0,
|
|
5417
|
+
reasoningTokens: 0,
|
|
5418
|
+
apiCalls: 0,
|
|
5419
|
+
sideQuestions: 0,
|
|
5420
|
+
handoffs: 0,
|
|
5421
|
+
prewarms: 0,
|
|
5422
|
+
backgroundMemory: 0,
|
|
5423
|
+
failedCompaction: 0
|
|
5424
|
+
}
|
|
5425
|
+
);
|
|
5426
|
+
}
|
|
5427
|
+
function cacheReadValue(cacheRead, apiCalls) {
|
|
5428
|
+
if (apiCalls > 1 && cacheRead > 0) {
|
|
5429
|
+
return [formatTokens2(cacheRead), `(avg/call ${formatTokens2(Math.round(cacheRead / apiCalls))})`];
|
|
5430
|
+
}
|
|
5431
|
+
return [formatTokens2(cacheRead)];
|
|
5432
|
+
}
|
|
5433
|
+
function reasoningValue(reasoningTokens) {
|
|
5434
|
+
return [formatTokens2(reasoningTokens), "provider-reported only"];
|
|
5435
|
+
}
|
|
5436
|
+
function kvBlock(entries) {
|
|
5437
|
+
const theme2 = clioTheme();
|
|
5438
|
+
const keyWidth = entries.reduce((max, [key]) => Math.max(max, key.length), 0);
|
|
5439
|
+
const valueWidth = entries.reduce((max, [, value]) => Math.max(max, value.length), 0);
|
|
5440
|
+
return entries.map(([key, value, annotation]) => {
|
|
5441
|
+
const row2 = `${theme2.fg("dim", key.padEnd(keyWidth))} ${theme2.fg("muted", value.padStart(valueWidth))}`;
|
|
5442
|
+
return annotation ? `${row2} ${theme2.fg("dim", annotation)}` : row2;
|
|
5443
|
+
});
|
|
5444
|
+
}
|
|
5445
|
+
function summaryBlock(totalCost, totalTokens, rows, promptCache) {
|
|
5446
|
+
const totals = sumRows(rows);
|
|
5447
|
+
const resolvedTotal = totalTokens > 0 ? totalTokens : totals.tokens;
|
|
5448
|
+
const cost = formatCostAggregate(totalCost);
|
|
5449
|
+
const cacheRows = promptCache !== null && hasPromptCacheTelemetry(promptCache) ? [
|
|
5450
|
+
[
|
|
5451
|
+
"uncached prefill",
|
|
5452
|
+
promptCache.uncachedPrefillTokens === null ? "not reported" : formatTokens2(promptCache.uncachedPrefillTokens)
|
|
5453
|
+
],
|
|
5454
|
+
[
|
|
5455
|
+
"cache verdicts",
|
|
5456
|
+
`hot ${promptCache.verdictCounts.hot} \xB7 partial ${promptCache.verdictCounts.partial} \xB7 cold ${promptCache.verdictCounts.cold} \xB7 small ${promptCache.verdictCounts.small} \xB7 unknown ${promptCache.verdictCounts.unknown}`
|
|
5457
|
+
]
|
|
5458
|
+
] : [];
|
|
5459
|
+
return kvBlock([
|
|
5460
|
+
[
|
|
5461
|
+
"turns",
|
|
5462
|
+
formatTokens2(
|
|
5463
|
+
totals.runs - totals.sideQuestions - totals.handoffs - totals.prewarms - totals.backgroundMemory - totals.failedCompaction
|
|
5464
|
+
)
|
|
5465
|
+
],
|
|
5466
|
+
["model calls", formatTokens2(totals.apiCalls)],
|
|
5467
|
+
...totals.sideQuestions > 0 ? [["side questions", formatTokens2(totals.sideQuestions)]] : [],
|
|
5468
|
+
...totals.handoffs > 0 ? [["handoffs", formatTokens2(totals.handoffs)]] : [],
|
|
5469
|
+
...totals.prewarms > 0 ? [["pre-warms", formatTokens2(totals.prewarms)]] : [],
|
|
5470
|
+
...totals.backgroundMemory > 0 ? [["memory steps", formatTokens2(totals.backgroundMemory)]] : [],
|
|
5471
|
+
...totals.failedCompaction > 0 ? [
|
|
5472
|
+
["failed compaction calls", formatTokens2(totals.failedCompaction)],
|
|
5473
|
+
["compaction usage", "known subtotals; missing usage is unknown"]
|
|
5474
|
+
] : [],
|
|
5475
|
+
...cost === null ? [] : [["cost", cost]],
|
|
5476
|
+
["input", formatTokens2(totals.input)],
|
|
5477
|
+
["output", formatTokens2(totals.output)],
|
|
5478
|
+
["reasoning", ...reasoningValue(totals.reasoningTokens)],
|
|
5479
|
+
["cache read", ...cacheReadValue(totals.cacheRead, totals.apiCalls)],
|
|
5480
|
+
["cache write", formatTokens2(totals.cacheWrite)],
|
|
5481
|
+
...cacheRows,
|
|
5482
|
+
["processed", `${formatTokens2(resolvedTotal)} tokens`]
|
|
5483
|
+
]);
|
|
5484
|
+
}
|
|
5485
|
+
function modelBlock(row2) {
|
|
5486
|
+
const cost = formatCostAggregate(row2.cost);
|
|
5487
|
+
return kvBlock([
|
|
5488
|
+
["requested model ids", row2.requestedModelIds.join(", ")],
|
|
5489
|
+
["response model id observation", responseModelIdObservationCountsLabel(row2.responseModelIdObservationCounts)],
|
|
5490
|
+
[
|
|
5491
|
+
"turns",
|
|
5492
|
+
formatTokens2(
|
|
5493
|
+
row2.runs - row2.sideQuestions - row2.handoffs - row2.prewarms - row2.backgroundMemory - row2.failedCompaction
|
|
5494
|
+
)
|
|
5495
|
+
],
|
|
5496
|
+
["model calls", formatTokens2(row2.apiCalls)],
|
|
5497
|
+
...row2.sideQuestions > 0 ? [["side questions", formatTokens2(row2.sideQuestions)]] : [],
|
|
5498
|
+
...row2.handoffs > 0 ? [["handoffs", formatTokens2(row2.handoffs)]] : [],
|
|
5499
|
+
...row2.prewarms > 0 ? [["pre-warms", formatTokens2(row2.prewarms)]] : [],
|
|
5500
|
+
...row2.backgroundMemory > 0 ? [["memory steps", formatTokens2(row2.backgroundMemory)]] : [],
|
|
5501
|
+
...row2.failedCompaction > 0 ? [["failed compaction calls", formatTokens2(row2.failedCompaction)]] : [],
|
|
5502
|
+
...cost === null ? [] : [["cost", cost]],
|
|
5503
|
+
["input", formatTokens2(row2.input)],
|
|
5504
|
+
["output", formatTokens2(row2.output)],
|
|
5505
|
+
["reasoning", ...reasoningValue(row2.reasoningTokens)],
|
|
5506
|
+
["cache read", ...cacheReadValue(row2.cacheRead, row2.apiCalls)],
|
|
5507
|
+
["cache write", formatTokens2(row2.cacheWrite)],
|
|
5508
|
+
["processed", `${formatTokens2(row2.tokens)} tokens`]
|
|
5509
|
+
]);
|
|
5510
|
+
}
|
|
5511
|
+
function modelDetailLines(rows, contentWidth) {
|
|
5512
|
+
const theme2 = clioTheme();
|
|
5513
|
+
const lines = [rule(theme2, contentWidth)];
|
|
5514
|
+
if (rows.length === 0) {
|
|
5515
|
+
lines.push(theme2.fg("muted", "no token usage recorded for this session"));
|
|
5516
|
+
} else {
|
|
5517
|
+
for (const [index, row2] of rows.entries()) {
|
|
5518
|
+
if (index > 0) lines.push("");
|
|
5519
|
+
lines.push(theme2.style("accent", `${row2.providerId} \xB7 attributed model ${row2.attributedModelId}`, { bold: true }));
|
|
5520
|
+
for (const line of modelBlock(row2)) {
|
|
5521
|
+
lines.push(line);
|
|
5522
|
+
}
|
|
5549
5523
|
}
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5524
|
+
}
|
|
5525
|
+
return lines;
|
|
5526
|
+
}
|
|
5527
|
+
function buildCostSnapshot(observability, sessionId, snapshot, getSessionEntries) {
|
|
5528
|
+
const entries = observability.costEntries();
|
|
5529
|
+
const rows = aggregateCostEntries(entries);
|
|
5530
|
+
const totalTokens = rows.reduce((sum, r) => sum + r.tokens, 0);
|
|
5531
|
+
return {
|
|
5532
|
+
sessionId,
|
|
5533
|
+
totalCost: snapshot?.session.cost ?? observability.sessionCostSummary(),
|
|
5534
|
+
totalTokens,
|
|
5535
|
+
rows,
|
|
5536
|
+
promptCache: getSessionEntries ? foldPromptCacheTelemetry(getSessionEntries()) : null
|
|
5537
|
+
};
|
|
5538
|
+
}
|
|
5539
|
+
var USAGE_TABS = ["Accounts", "Session", "Models", "Workers"];
|
|
5540
|
+
var UsageOverlayBody = class {
|
|
5541
|
+
constructor(getSnapshot, getQuotaSnapshots, getDispatchRows, requestRender) {
|
|
5542
|
+
this.getSnapshot = getSnapshot;
|
|
5543
|
+
this.getQuotaSnapshots = getQuotaSnapshots;
|
|
5544
|
+
this.getDispatchRows = getDispatchRows;
|
|
5545
|
+
this.requestRender = requestRender;
|
|
5546
|
+
}
|
|
5547
|
+
getSnapshot;
|
|
5548
|
+
getQuotaSnapshots;
|
|
5549
|
+
getDispatchRows;
|
|
5550
|
+
requestRender;
|
|
5551
|
+
tab = 0;
|
|
5552
|
+
offsets = [0, 0, 0, 0];
|
|
5553
|
+
height = 20;
|
|
5554
|
+
pageHeight = 16;
|
|
5555
|
+
lineCount = 0;
|
|
5556
|
+
setHeight(rows) {
|
|
5557
|
+
this.height = Math.max(1, rows - 3);
|
|
5558
|
+
}
|
|
5559
|
+
handleInput(data) {
|
|
5560
|
+
const selected = ["1", "2", "3", "4"].findIndex((key) => matchesKey(data, key));
|
|
5561
|
+
if (selected >= 0) this.tab = selected;
|
|
5562
|
+
else if (matchesKey(data, "tab") || matchesKey(data, "right")) this.tab = (this.tab + 1) % USAGE_TABS.length;
|
|
5563
|
+
else if (matchesKey(data, "shift+tab") || matchesKey(data, "left"))
|
|
5564
|
+
this.tab = (this.tab + USAGE_TABS.length - 1) % USAGE_TABS.length;
|
|
5565
|
+
else {
|
|
5566
|
+
let offset = this.offsets[this.tab] ?? 0;
|
|
5567
|
+
if (matchesKey(data, "up")) offset--;
|
|
5568
|
+
else if (matchesKey(data, "down")) offset++;
|
|
5569
|
+
else if (matchesKey(data, "pageUp")) offset -= this.pageHeight;
|
|
5570
|
+
else if (matchesKey(data, "pageDown")) offset += this.pageHeight;
|
|
5571
|
+
else if (matchesKey(data, "home") || matchesKey(data, "ctrl+home")) offset = 0;
|
|
5572
|
+
else if (matchesKey(data, "end") || matchesKey(data, "ctrl+end")) offset = this.lineCount;
|
|
5573
|
+
else return;
|
|
5574
|
+
this.offsets[this.tab] = Math.max(0, Math.min(offset, this.lineCount - this.pageHeight));
|
|
5556
5575
|
}
|
|
5557
5576
|
this.requestRender();
|
|
5558
5577
|
}
|
|
5559
|
-
|
|
5560
|
-
|
|
5578
|
+
render(width) {
|
|
5579
|
+
const contentWidth = Math.max(1, Math.floor(width));
|
|
5580
|
+
const snapshot = this.getSnapshot();
|
|
5581
|
+
const quota = this.getQuotaSnapshots();
|
|
5582
|
+
const theme2 = clioTheme();
|
|
5583
|
+
const tabs = wrapTextWithAnsi(
|
|
5584
|
+
USAGE_TABS.map(
|
|
5585
|
+
(name, index) => index === this.tab ? theme2.style("accent", `${index + 1} ${name}`, { bold: true, underline: true }) : theme2.fg("dim", `${index + 1} ${name}`)
|
|
5586
|
+
).join(" "),
|
|
5587
|
+
contentWidth
|
|
5588
|
+
);
|
|
5589
|
+
let body;
|
|
5590
|
+
if (this.tab === 0)
|
|
5591
|
+
body = [
|
|
5592
|
+
theme2.fg("dim", "Account-wide limits \xB7 filled = used \xB7 shared across sessions and devices"),
|
|
5593
|
+
"",
|
|
5594
|
+
...renderQuotaAccounts(quota, contentWidth)
|
|
5595
|
+
];
|
|
5596
|
+
else if (this.tab === 1)
|
|
5597
|
+
body = [
|
|
5598
|
+
theme2.style("accent", "Session tokens & cost", { bold: true }),
|
|
5599
|
+
theme2.fg("dim", "Recorded calls in this session \xB7 estimates are marked in the cost totals"),
|
|
5600
|
+
"",
|
|
5601
|
+
...summaryBlock(snapshot.totalCost, snapshot.totalTokens, snapshot.rows, snapshot.promptCache),
|
|
5602
|
+
"",
|
|
5603
|
+
rule(theme2, contentWidth),
|
|
5604
|
+
theme2.fg("dim", "Session tokens cannot be converted to subscription percentages."),
|
|
5605
|
+
...snapshot.rows.length ? [] : ["no token usage recorded for this session"]
|
|
5606
|
+
];
|
|
5607
|
+
else if (this.tab === 2) {
|
|
5608
|
+
body = [
|
|
5609
|
+
theme2.style("accent", "Model activity \xB7 this session", { bold: true }),
|
|
5610
|
+
theme2.fg("dim", "Share of recorded processed tokens, including cache traffic; not account quota."),
|
|
5611
|
+
""
|
|
5612
|
+
];
|
|
5613
|
+
for (const row2 of [...snapshot.rows].sort((a, b) => b.tokens - a.tokens)) {
|
|
5614
|
+
const share = snapshot.totalTokens > 0 ? row2.tokens / snapshot.totalTokens * 100 : 0;
|
|
5615
|
+
body.push(
|
|
5616
|
+
`${row2.providerId} \xB7 ${row2.attributedModelId}`,
|
|
5617
|
+
`${quotaMeter(share, Math.min(24, contentWidth), "normal")} ${share.toFixed(1)}% \xB7 ${formatTokens2(row2.tokens)} tokens`
|
|
5618
|
+
);
|
|
5619
|
+
}
|
|
5620
|
+
body.push("", ...modelDetailLines(snapshot.rows, contentWidth));
|
|
5621
|
+
} else body = renderWorkerUsage(this.getDispatchRows(), quota, contentWidth);
|
|
5622
|
+
const lines = body.flatMap((line) => wrapTextWithAnsi(line, contentWidth));
|
|
5623
|
+
this.lineCount = lines.length;
|
|
5624
|
+
this.pageHeight = Math.max(1, this.height - tabs.length - 1);
|
|
5625
|
+
const offset = Math.max(0, Math.min(this.offsets[this.tab] ?? 0, lines.length - this.pageHeight));
|
|
5626
|
+
this.offsets[this.tab] = offset;
|
|
5627
|
+
const visible = [...tabs, "", ...lines.slice(offset, offset + this.pageHeight)];
|
|
5628
|
+
if (lines.length > this.pageHeight)
|
|
5629
|
+
visible.push(theme2.fg("dim", `${offset + 1}\u2013${Math.min(lines.length, offset + this.pageHeight)} / ${lines.length}`));
|
|
5630
|
+
return visible;
|
|
5631
|
+
}
|
|
5632
|
+
invalidate() {
|
|
5561
5633
|
}
|
|
5562
5634
|
};
|
|
5563
|
-
function
|
|
5564
|
-
const
|
|
5635
|
+
function openUsageOverlay(tui, observability, options) {
|
|
5636
|
+
const sessionId = options?.sessionId ?? null;
|
|
5637
|
+
let latest = observability.snapshot();
|
|
5638
|
+
const body = new UsageOverlayBody(
|
|
5639
|
+
() => buildCostSnapshot(observability, sessionId, latest, options?.getSessionEntries),
|
|
5640
|
+
options?.getQuotaSnapshots ?? (() => []),
|
|
5641
|
+
options?.getDispatchRows ?? (() => []),
|
|
5642
|
+
() => tui.requestRender()
|
|
5643
|
+
);
|
|
5565
5644
|
const handle = showClioOverlayFrame(tui, body, {
|
|
5566
5645
|
anchor: "center",
|
|
5567
|
-
width:
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5646
|
+
width: USAGE_OVERLAY_WIDTH,
|
|
5647
|
+
// Not derived from the title: that one carries the session id.
|
|
5648
|
+
markerId: "usage",
|
|
5649
|
+
title: sessionId && sessionId.length > 0 ? `Usage (${sessionId})` : "Usage",
|
|
5650
|
+
footerHint: buildResponsiveHint([
|
|
5651
|
+
{ key: "1\u20134 / Tab", verb: "view", critical: true },
|
|
5652
|
+
{ key: "\u2191\u2193", verb: "scroll" },
|
|
5653
|
+
{ key: "PgUp/PgDn", verb: "page" }
|
|
5654
|
+
]),
|
|
5655
|
+
visible: (_width, height) => {
|
|
5656
|
+
body.setHeight(height);
|
|
5657
|
+
return true;
|
|
5658
|
+
}
|
|
5659
|
+
});
|
|
5660
|
+
const unsubscribe = observability.subscribe((snapshot) => {
|
|
5661
|
+
latest = snapshot;
|
|
5662
|
+
body.invalidate();
|
|
5663
|
+
tui.requestRender();
|
|
5571
5664
|
});
|
|
5572
|
-
const timer = setInterval(() => tui.requestRender(), REFRESH_MS2);
|
|
5573
|
-
timer.unref?.();
|
|
5574
5665
|
return {
|
|
5575
5666
|
...handle,
|
|
5576
5667
|
hide() {
|
|
5577
|
-
|
|
5668
|
+
unsubscribe();
|
|
5578
5669
|
handle.hide();
|
|
5579
5670
|
}
|
|
5580
5671
|
};
|
|
@@ -5582,7 +5673,7 @@ function openTasksOverlay(tui, getBoard, options = {}) {
|
|
|
5582
5673
|
|
|
5583
5674
|
// src/interactive/overlay-general-openers.ts
|
|
5584
5675
|
function createOverlayGeneralOpeners(deps) {
|
|
5585
|
-
const
|
|
5676
|
+
const openUsageOverlayFactory = deps.openUsageOverlay ?? openUsageOverlay;
|
|
5586
5677
|
const openContextOverlayFactory = deps.openContextOverlay ?? openContextOverlay;
|
|
5587
5678
|
const openContextResetOverlayFactory = deps.openContextResetOverlay ?? openContextResetOverlay;
|
|
5588
5679
|
const openTasksOverlayFactory = deps.openTasksOverlay ?? openTasksOverlay;
|
|
@@ -5591,11 +5682,13 @@ function createOverlayGeneralOpeners(deps) {
|
|
|
5591
5682
|
const openViewOverlayFactory = deps.openViewOverlay ?? openViewOverlay;
|
|
5592
5683
|
const showOverlayFrameFactory = deps.showOverlayFrame ?? showClioOverlayFrame;
|
|
5593
5684
|
const openSideQuestionOverlayFactory = deps.openSideQuestionOverlay ?? openSideQuestionOverlay;
|
|
5594
|
-
const
|
|
5685
|
+
const openUsage = () => {
|
|
5595
5686
|
if (deps.transitions.state !== "closed") return;
|
|
5596
|
-
deps.transitions.state = "
|
|
5597
|
-
deps.transitions.handle =
|
|
5687
|
+
deps.transitions.state = "usage";
|
|
5688
|
+
deps.transitions.handle = openUsageOverlayFactory(deps.tui, deps.observability, {
|
|
5598
5689
|
sessionId: deps.getSessionId?.() ?? null,
|
|
5690
|
+
...deps.getQuotaSnapshots ? { getQuotaSnapshots: deps.getQuotaSnapshots } : {},
|
|
5691
|
+
...deps.getDispatchRows ? { getDispatchRows: deps.getDispatchRows } : {},
|
|
5599
5692
|
...deps.readSessionEntries ? {
|
|
5600
5693
|
getSessionEntries: () => deps.readSessionEntries?.() ?? []
|
|
5601
5694
|
} : {}
|
|
@@ -5805,16 +5898,16 @@ function createOverlayGeneralOpeners(deps) {
|
|
|
5805
5898
|
});
|
|
5806
5899
|
deps.transitions.handle = session;
|
|
5807
5900
|
deps.requestRender();
|
|
5808
|
-
void ask(question, { signal: controller.signal, onDelta: (partialText) => session.setAnswer(partialText) }).then((
|
|
5809
|
-
if (
|
|
5810
|
-
session.settle({ kind: "answered", text:
|
|
5901
|
+
void ask(question, { signal: controller.signal, onDelta: (partialText) => session.setAnswer(partialText) }).then((outcome2) => {
|
|
5902
|
+
if (outcome2.status === "answered") {
|
|
5903
|
+
session.settle({ kind: "answered", text: outcome2.text });
|
|
5811
5904
|
return;
|
|
5812
5905
|
}
|
|
5813
|
-
if (
|
|
5814
|
-
session.settle({ kind: "aborted", text:
|
|
5906
|
+
if (outcome2.status === "aborted") {
|
|
5907
|
+
session.settle({ kind: "aborted", text: outcome2.text });
|
|
5815
5908
|
return;
|
|
5816
5909
|
}
|
|
5817
|
-
session.settle({ kind: "error", reason:
|
|
5910
|
+
session.settle({ kind: "error", reason: outcome2.reason });
|
|
5818
5911
|
}).catch((error) => {
|
|
5819
5912
|
session.settle({ kind: "error", reason: error instanceof Error ? error.message : String(error) });
|
|
5820
5913
|
});
|
|
@@ -5905,13 +5998,13 @@ function createOverlayGeneralOpeners(deps) {
|
|
|
5905
5998
|
},
|
|
5906
5999
|
onNotice: (text) => deps.stderr(`[fleet ${preview.name}] ${text}
|
|
5907
6000
|
`)
|
|
5908
|
-
}).then((
|
|
6001
|
+
}).then((outcome2) => {
|
|
5909
6002
|
deps.notify(
|
|
5910
|
-
|
|
6003
|
+
outcome2.cleanRun ? "success" : "warning",
|
|
5911
6004
|
// `totalCost` rather than the bare `totalCostUsd` sum: a run whose
|
|
5912
6005
|
// receipts were all unpriced reads as not measured instead of as a
|
|
5913
6006
|
// measured $0.0000 nobody put a number on.
|
|
5914
|
-
`fleet ${preview.name}: ${
|
|
6007
|
+
`fleet ${preview.name}: ${outcome2.succeededStepCount}/${outcome2.requiredStepCount} steps succeeded, cost ${renderCostAggregate(outcome2.totalCost)}`,
|
|
5915
6008
|
"fleet-run:settled"
|
|
5916
6009
|
);
|
|
5917
6010
|
}).catch((error) => {
|
|
@@ -5923,7 +6016,7 @@ function createOverlayGeneralOpeners(deps) {
|
|
|
5923
6016
|
});
|
|
5924
6017
|
};
|
|
5925
6018
|
return {
|
|
5926
|
-
|
|
6019
|
+
openUsage,
|
|
5927
6020
|
openContextView,
|
|
5928
6021
|
openContextReset,
|
|
5929
6022
|
toggleFooter,
|
|
@@ -7033,13 +7126,13 @@ function editNumberSubmenu(title, id) {
|
|
|
7033
7126
|
numberSettingNote(rule2)
|
|
7034
7127
|
);
|
|
7035
7128
|
input.onSubmit = (val) => {
|
|
7036
|
-
const
|
|
7037
|
-
if ("refusal" in
|
|
7038
|
-
wrapper.setProblem(
|
|
7129
|
+
const outcome2 = parseNumberSetting(val, rule2);
|
|
7130
|
+
if ("refusal" in outcome2) {
|
|
7131
|
+
wrapper.setProblem(outcome2.refusal);
|
|
7039
7132
|
return;
|
|
7040
7133
|
}
|
|
7041
7134
|
wrapper.setProblem(null);
|
|
7042
|
-
done(
|
|
7135
|
+
done(outcome2.value === null ? "" : val.trim());
|
|
7043
7136
|
};
|
|
7044
7137
|
input.onEscape = () => done();
|
|
7045
7138
|
return wrapper;
|
|
@@ -7067,13 +7160,13 @@ function editEnumListSubmenu(title, legal) {
|
|
|
7067
7160
|
`Comma-separated, from ${legal.join(", ")}; blank activates none.`
|
|
7068
7161
|
);
|
|
7069
7162
|
input.onSubmit = (val) => {
|
|
7070
|
-
const
|
|
7071
|
-
if ("refusal" in
|
|
7072
|
-
wrapper.setProblem(
|
|
7163
|
+
const outcome2 = parseEnumListSetting(val, legal);
|
|
7164
|
+
if ("refusal" in outcome2) {
|
|
7165
|
+
wrapper.setProblem(outcome2.refusal);
|
|
7073
7166
|
return;
|
|
7074
7167
|
}
|
|
7075
7168
|
wrapper.setProblem(null);
|
|
7076
|
-
done(
|
|
7169
|
+
done(outcome2.values.join(", "));
|
|
7077
7170
|
};
|
|
7078
7171
|
input.onEscape = () => done();
|
|
7079
7172
|
return wrapper;
|
|
@@ -7289,12 +7382,12 @@ function scopedModelsSubmenu(selectedRefs, providers) {
|
|
|
7289
7382
|
"Target entries cycle by target, using its default model when switching targets. Model entries pin one exact target/model reference."
|
|
7290
7383
|
);
|
|
7291
7384
|
}
|
|
7292
|
-
function chainSubmenus(first, second,
|
|
7385
|
+
function chainSubmenus(first, second, join6) {
|
|
7293
7386
|
return (_currentValue, done) => {
|
|
7294
7387
|
let active = first("", (head) => {
|
|
7295
7388
|
const trimmed = head?.trim() ?? "";
|
|
7296
7389
|
if (!trimmed) return done();
|
|
7297
|
-
active = second(trimmed)("", (tail) => done(tail?.trim() ?
|
|
7390
|
+
active = second(trimmed)("", (tail) => done(tail?.trim() ? join6(trimmed, tail.trim()) : void 0));
|
|
7298
7391
|
});
|
|
7299
7392
|
return {
|
|
7300
7393
|
render: (width) => active.render(width),
|
|
@@ -8277,9 +8370,9 @@ function applyNonNegativeInteger(value, set) {
|
|
|
8277
8370
|
if (Number.isFinite(parsed) && parsed >= 0) set(Math.floor(parsed));
|
|
8278
8371
|
}
|
|
8279
8372
|
function applyNumberSetting(id, value, set) {
|
|
8280
|
-
const
|
|
8281
|
-
if ("refusal" in
|
|
8282
|
-
set(
|
|
8373
|
+
const outcome2 = parseNumberSetting(value, NUMBER_SETTING_RULES[id]);
|
|
8374
|
+
if ("refusal" in outcome2) return;
|
|
8375
|
+
set(outcome2.value);
|
|
8283
8376
|
}
|
|
8284
8377
|
function applyDelegationAgentChange(settings, value) {
|
|
8285
8378
|
if (value.startsWith(DELEGATION_REMOVE_PREFIX)) {
|
|
@@ -8567,13 +8660,13 @@ function applySettingChange(settings, id, value) {
|
|
|
8567
8660
|
});
|
|
8568
8661
|
return;
|
|
8569
8662
|
case "routing.activeRoles": {
|
|
8570
|
-
const
|
|
8571
|
-
if ("values" in
|
|
8663
|
+
const outcome2 = parseEnumListSetting(value, ACTIVE_ROUTING_ROLES);
|
|
8664
|
+
if ("values" in outcome2) settings.fleet.adaptiveRouting.roles = outcome2.values;
|
|
8572
8665
|
return;
|
|
8573
8666
|
}
|
|
8574
8667
|
case "routing.activePostures": {
|
|
8575
|
-
const
|
|
8576
|
-
if ("values" in
|
|
8668
|
+
const outcome2 = parseEnumListSetting(value, ACTIVE_ROUTING_POSTURES);
|
|
8669
|
+
if ("values" in outcome2) settings.fleet.adaptiveRouting.postures = outcome2.values;
|
|
8577
8670
|
return;
|
|
8578
8671
|
}
|
|
8579
8672
|
case "prewarm.enabled":
|
|
@@ -8692,12 +8785,12 @@ function applyEntrySettingChange(settings, id, value) {
|
|
|
8692
8785
|
}
|
|
8693
8786
|
function changedLeafPaths(before, after) {
|
|
8694
8787
|
const out = [];
|
|
8695
|
-
const
|
|
8788
|
+
const isRecord6 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
8696
8789
|
const visit = (a, b, path) => {
|
|
8697
8790
|
if (a === b) return;
|
|
8698
8791
|
const profileLeaf = path.startsWith("fleet.profiles.") && path.split(".").length === 3;
|
|
8699
8792
|
const collectionLeaf = path === "interface.keybindings" || path === "fleet.rosters";
|
|
8700
|
-
if (!
|
|
8793
|
+
if (!isRecord6(a) || !isRecord6(b) || profileLeaf || collectionLeaf) {
|
|
8701
8794
|
if (JSON.stringify(a) !== JSON.stringify(b)) out.push(path);
|
|
8702
8795
|
return;
|
|
8703
8796
|
}
|
|
@@ -11435,28 +11528,28 @@ function evidenceLabel(evidence, refresh) {
|
|
|
11435
11528
|
return "resource evidence from before refresh; current session admission is unverified because refresh failed";
|
|
11436
11529
|
return "resource evidence from before refresh; no session refresh applied";
|
|
11437
11530
|
}
|
|
11438
|
-
function outcomeLines(
|
|
11531
|
+
function outcomeLines(outcome2, width, refresh) {
|
|
11439
11532
|
const theme2 = clioTheme();
|
|
11440
|
-
const token =
|
|
11533
|
+
const token = outcome2.status === "committed" ? "success" : outcome2.status === "failed" ? "error" : "dim";
|
|
11441
11534
|
const rows = wrap(
|
|
11442
|
-
theme2.fg(token, `${
|
|
11535
|
+
theme2.fg(token, `${outcome2.status}: ${outcome2.operation} ${outcome2.identity.ref} (${outcome2.identity.scope})`),
|
|
11443
11536
|
width
|
|
11444
11537
|
);
|
|
11445
|
-
if (
|
|
11446
|
-
rows.push(...wrap(theme2.fg("error", `${
|
|
11447
|
-
if (
|
|
11538
|
+
if (outcome2.error) {
|
|
11539
|
+
rows.push(...wrap(theme2.fg("error", `${outcome2.error.code}: ${outcome2.error.message}`), width));
|
|
11540
|
+
if (outcome2.error.changed)
|
|
11448
11541
|
rows.push(
|
|
11449
11542
|
...wrap(
|
|
11450
11543
|
theme2.fg(
|
|
11451
11544
|
"dim",
|
|
11452
|
-
`changed fact ${
|
|
11545
|
+
`changed fact ${outcome2.error.changed.fact}: reviewed ${JSON.stringify(outcome2.error.changed.expected)}, observed ${JSON.stringify(outcome2.error.changed.observed)}`
|
|
11453
11546
|
),
|
|
11454
11547
|
width
|
|
11455
11548
|
)
|
|
11456
11549
|
);
|
|
11457
|
-
rows.push(...wrap(theme2.fg("info",
|
|
11550
|
+
rows.push(...wrap(theme2.fg("info", outcome2.error.next), width));
|
|
11458
11551
|
}
|
|
11459
|
-
const verification =
|
|
11552
|
+
const verification = outcome2.verification;
|
|
11460
11553
|
if (verification) {
|
|
11461
11554
|
rows.push(...wrap(theme2.fg("dim", `disk: tree ${verification.tree}, record ${verification.record}`), width));
|
|
11462
11555
|
rows.push(
|
|
@@ -11480,11 +11573,11 @@ function outcomeLines(outcome, width, refresh) {
|
|
|
11480
11573
|
)
|
|
11481
11574
|
);
|
|
11482
11575
|
}
|
|
11483
|
-
if (
|
|
11484
|
-
rows.push(...wrap(theme2.fg("info", `recovered content: ${
|
|
11485
|
-
if (
|
|
11486
|
-
rows.push(...wrap(theme2.fg("dim", `state backup: ${
|
|
11487
|
-
for (const diagnostic of
|
|
11576
|
+
if (outcome2.recovery?.packageBackup)
|
|
11577
|
+
rows.push(...wrap(theme2.fg("info", `recovered content: ${outcome2.recovery.packageBackup}`), width));
|
|
11578
|
+
if (outcome2.recovery?.stateBackup)
|
|
11579
|
+
rows.push(...wrap(theme2.fg("dim", `state backup: ${outcome2.recovery.stateBackup}`), width));
|
|
11580
|
+
for (const diagnostic of outcome2.diagnostics) rows.push(...wrap(theme2.fg("dim", diagnostic), width));
|
|
11488
11581
|
return rows;
|
|
11489
11582
|
}
|
|
11490
11583
|
function formatLibraryOutcome(result, width) {
|
|
@@ -11497,7 +11590,7 @@ function formatLibraryOutcome(result, width) {
|
|
|
11497
11590
|
width
|
|
11498
11591
|
);
|
|
11499
11592
|
rows.push(rule(theme2, width));
|
|
11500
|
-
for (const
|
|
11593
|
+
for (const outcome2 of result.outcomes) rows.push(...outcomeLines(outcome2, width, result.refresh));
|
|
11501
11594
|
rows.push(rule(theme2, width));
|
|
11502
11595
|
rows.push(...wrap(refreshLine(result.refresh), width));
|
|
11503
11596
|
if (result.unattempted > 0)
|
|
@@ -11533,7 +11626,7 @@ function formatLibraryImportReview(plan, width, options = {}) {
|
|
|
11533
11626
|
...wrap(
|
|
11534
11627
|
theme2.fg(
|
|
11535
11628
|
"info",
|
|
11536
|
-
`projects ${plan.outcomes.length} resource${plan.outcomes.length === 1 ? "" : "s"}${plan.outcomes.length > 0 ? `: ${plan.outcomes.map((
|
|
11629
|
+
`projects ${plan.outcomes.length} resource${plan.outcomes.length === 1 ? "" : "s"}${plan.outcomes.length > 0 ? `: ${plan.outcomes.map((outcome2) => `${outcome2.kind}:${outcome2.name} (${outcome2.status})`).join(", ")}` : ""}`
|
|
11537
11630
|
),
|
|
11538
11631
|
width
|
|
11539
11632
|
)
|
|
@@ -11631,8 +11724,8 @@ function openReviewOverlay(tui, spec) {
|
|
|
11631
11724
|
if (matchesKey(data, "enter")) {
|
|
11632
11725
|
if (!spec.applicable) return;
|
|
11633
11726
|
settled = true;
|
|
11634
|
-
const
|
|
11635
|
-
body.outcome = () =>
|
|
11727
|
+
const outcome2 = spec.commit();
|
|
11728
|
+
body.outcome = () => outcome2(width);
|
|
11636
11729
|
body.reset();
|
|
11637
11730
|
tui.requestRender();
|
|
11638
11731
|
return;
|
|
@@ -11935,15 +12028,15 @@ function openLibraryOverlay(tui, deps) {
|
|
|
11935
12028
|
columns: columns(),
|
|
11936
12029
|
commit: (accepted) => (deps.applyImport ?? applyLibraryImport)(accepted, true),
|
|
11937
12030
|
outcome: (accepted, result) => {
|
|
11938
|
-
const
|
|
11939
|
-
const committed =
|
|
12031
|
+
const outcome2 = libraryImportOutcome(accepted, result);
|
|
12032
|
+
const committed = outcome2.status === "committed";
|
|
11940
12033
|
return {
|
|
11941
12034
|
planId: accepted.reviewFingerprint,
|
|
11942
12035
|
dryRun: false,
|
|
11943
|
-
outcomes: [
|
|
12036
|
+
outcomes: [outcome2],
|
|
11944
12037
|
committed: committed ? 1 : 0,
|
|
11945
|
-
failed:
|
|
11946
|
-
unattempted:
|
|
12038
|
+
failed: outcome2.status === "failed" ? 1 : 0,
|
|
12039
|
+
unattempted: outcome2.status === "unattempted" ? 1 : 0,
|
|
11947
12040
|
refresh: committed ? deps.lifecycle.retryRefresh(cwd) : { status: "not-applicable", reason: "nothing was imported" }
|
|
11948
12041
|
};
|
|
11949
12042
|
},
|
|
@@ -13969,7 +14062,7 @@ function routeAuthOverlayKey(data, deps) {
|
|
|
13969
14062
|
}
|
|
13970
14063
|
return false;
|
|
13971
14064
|
}
|
|
13972
|
-
function
|
|
14065
|
+
function routeReadOnlyOverlayKey(data, deps) {
|
|
13973
14066
|
if (isEscapeKey(data)) {
|
|
13974
14067
|
deps.closeOverlay();
|
|
13975
14068
|
return true;
|
|
@@ -13999,16 +14092,13 @@ function routeOverlayKey(data, overlayState, deps, matches) {
|
|
|
13999
14092
|
return true;
|
|
14000
14093
|
}
|
|
14001
14094
|
if (overlayState === "auth") return routeAuthOverlayKey(data, deps);
|
|
14002
|
-
if (overlayState === "
|
|
14003
|
-
routeCostOverlayKey(data, deps);
|
|
14004
|
-
return true;
|
|
14005
|
-
}
|
|
14095
|
+
if (overlayState === "usage") return routeReadOnlyOverlayKey(data, deps);
|
|
14006
14096
|
if (overlayState === "context-view") {
|
|
14007
|
-
|
|
14097
|
+
routeReadOnlyOverlayKey(data, deps);
|
|
14008
14098
|
return true;
|
|
14009
14099
|
}
|
|
14010
14100
|
if (overlayState === "side-question") {
|
|
14011
|
-
|
|
14101
|
+
routeReadOnlyOverlayKey(data, deps);
|
|
14012
14102
|
return true;
|
|
14013
14103
|
}
|
|
14014
14104
|
if (overlayState === "handoff-review") return false;
|
|
@@ -14072,7 +14162,7 @@ function createOverlayLifecycle(deps) {
|
|
|
14072
14162
|
openTreeOverlay: openTreeOverlayFactory,
|
|
14073
14163
|
openMessagePickerOverlay: openMessagePickerOverlayFactory,
|
|
14074
14164
|
openCwdFallbackOverlay: openCwdFallbackOverlayFactory,
|
|
14075
|
-
|
|
14165
|
+
openUsageOverlay: openUsageOverlayFactory,
|
|
14076
14166
|
openContextOverlay: openContextOverlayFactory,
|
|
14077
14167
|
openContextResetOverlay: openContextResetOverlayFactory,
|
|
14078
14168
|
openTasksOverlay: openTasksOverlayFactory,
|
|
@@ -14264,6 +14354,8 @@ function createOverlayLifecycle(deps) {
|
|
|
14264
14354
|
tui,
|
|
14265
14355
|
transitions: overlayTransitions,
|
|
14266
14356
|
observability: deps.app.observability,
|
|
14357
|
+
...deps.getQuotaSnapshots ? { getQuotaSnapshots: deps.getQuotaSnapshots } : {},
|
|
14358
|
+
...deps.getDispatchRows ? { getDispatchRows: deps.getDispatchRows } : {},
|
|
14267
14359
|
...deps.app.getSessionId ? { getSessionId: deps.app.getSessionId } : {},
|
|
14268
14360
|
getContextLedger: () => deps.app.chat.contextLedger(),
|
|
14269
14361
|
contextChat: deps.app.chat,
|
|
@@ -14292,7 +14384,7 @@ function createOverlayLifecycle(deps) {
|
|
|
14292
14384
|
startDispatchBoardTicker: () => interactiveTickers.startDispatchBoardTicker(),
|
|
14293
14385
|
closeOverlay,
|
|
14294
14386
|
showOverlayFrame,
|
|
14295
|
-
...
|
|
14387
|
+
...openUsageOverlayFactory ? { openUsageOverlay: openUsageOverlayFactory } : {},
|
|
14296
14388
|
...openContextOverlayFactory ? { openContextOverlay: openContextOverlayFactory } : {},
|
|
14297
14389
|
...openContextResetOverlayFactory ? { openContextResetOverlay: openContextResetOverlayFactory } : {},
|
|
14298
14390
|
...openTasksOverlayFactory ? { openTasksOverlay: openTasksOverlayFactory } : {},
|
|
@@ -14308,7 +14400,7 @@ function createOverlayLifecycle(deps) {
|
|
|
14308
14400
|
const openResumeOverlayState = overlaySessions.openResume;
|
|
14309
14401
|
const openTreeOverlayState = overlaySessions.openTree;
|
|
14310
14402
|
const openMessagePickerOverlayState = overlaySessions.openMessagePicker;
|
|
14311
|
-
const
|
|
14403
|
+
const openUsageOverlayState = overlayGeneralOpeners.openUsage;
|
|
14312
14404
|
const openContextViewOverlayState = overlayGeneralOpeners.openContextView;
|
|
14313
14405
|
const openContextResetOverlayState = overlayGeneralOpeners.openContextReset;
|
|
14314
14406
|
const toggleFooterDashboardState = overlayGeneralOpeners.toggleFooter;
|
|
@@ -14329,7 +14421,7 @@ function createOverlayLifecycle(deps) {
|
|
|
14329
14421
|
isAskUserWaiting: overlayAskUser.isWaiting,
|
|
14330
14422
|
resetAskUserCancellation: overlayAskUser.resetCancellation,
|
|
14331
14423
|
refreshSettingsOverlay: overlayModelSelectors.refreshSettingsOverlay,
|
|
14332
|
-
|
|
14424
|
+
openUsageOverlayState,
|
|
14333
14425
|
openContextViewOverlayState,
|
|
14334
14426
|
openContextResetOverlayState,
|
|
14335
14427
|
toggleFooterDashboardState,
|
|
@@ -15553,10 +15645,10 @@ function createInteractiveEventProjection(deps) {
|
|
|
15553
15645
|
deps.bus.on(BusChannels.ContextPruned, (payload) => {
|
|
15554
15646
|
const event = payload;
|
|
15555
15647
|
if (event && typeof event === "object" && typeof event.tokensBefore === "number" && typeof event.tokensAfter === "number") {
|
|
15556
|
-
const
|
|
15648
|
+
const outcome2 = event.tokensAfter < event.tokensBefore ? "Reclaimed context" : "Context updated";
|
|
15557
15649
|
deps.notify(
|
|
15558
15650
|
"info",
|
|
15559
|
-
`[Compaction] ${
|
|
15651
|
+
`[Compaction] ${outcome2}: ${event.tokensBefore} -> ${event.tokensAfter} tokens (${event.stage})`,
|
|
15560
15652
|
"compaction-notice"
|
|
15561
15653
|
);
|
|
15562
15654
|
}
|
|
@@ -15960,7 +16052,7 @@ function createInteractiveInputRuntime(deps) {
|
|
|
15960
16052
|
},
|
|
15961
16053
|
cancelFocusedOwner: () => {
|
|
15962
16054
|
const state = deps.overlay.getState();
|
|
15963
|
-
if (["permission-confirm", "
|
|
16055
|
+
if (["permission-confirm", "usage", "context-view", "side-question", "dispatch-board", "auth"].includes(state))
|
|
15964
16056
|
deps.overlay.closeOverlay();
|
|
15965
16057
|
else if (focused()?.handleInput) {
|
|
15966
16058
|
focused()?.handleInput?.("\x1B");
|
|
@@ -16078,89 +16170,1041 @@ function createInteractiveInputRuntime(deps) {
|
|
|
16078
16170
|
deps.onShutdownArmedChange?.(armed);
|
|
16079
16171
|
deps.refreshFooter();
|
|
16080
16172
|
},
|
|
16081
|
-
closeOverlay: () => deps.overlay.closeOverlay(),
|
|
16082
|
-
listNotifications: () => deps.notifications.list(),
|
|
16083
|
-
dismissNotification: (id) => deps.notifications.dismiss(id),
|
|
16084
|
-
dismissAllNotifications: () => deps.notifications.dismissAll(),
|
|
16085
|
-
shutdownDisposers: [
|
|
16086
|
-
() => deps.shutdown.stopTickers(),
|
|
16087
|
-
() => leaderKeys.dispose(),
|
|
16088
|
-
() => removeReload?.(),
|
|
16089
|
-
() => deps.shutdown.disposeInteractiveTickers(),
|
|
16090
|
-
() => deps.shutdown.disposeBeforeStatus(),
|
|
16091
|
-
() => deps.shutdown.disposeProjectionPrimary(),
|
|
16092
|
-
() => deps.shutdown.disposeStatus(),
|
|
16093
|
-
() => deps.shutdown.disposeProjectionRemaining(),
|
|
16094
|
-
() => deps.shutdown.disposeOverlay(),
|
|
16095
|
-
() => deps.shutdown.stopAgentProgress(),
|
|
16096
|
-
() => deps.shutdown.disposeChat(),
|
|
16097
|
-
() => deps.shutdown.disposeSubscriptions()
|
|
16098
|
-
],
|
|
16099
|
-
stopUi: deps.stopUi,
|
|
16100
|
-
...deps.beforeStopUi ? { beforeStopUi: deps.beforeStopUi } : {},
|
|
16101
|
-
cancelParkedCalls: deps.cancelParkedCalls,
|
|
16102
|
-
onShutdown: deps.onShutdown,
|
|
16103
|
-
registerTerminalTeardown: deps.registerTerminalTeardown ?? ((teardown) => getTerminationCoordinator().onDrain(teardown)),
|
|
16104
|
-
// stderr for the same reason src/core/termination.ts uses it for a failed
|
|
16105
|
-
// hook: by the time teardown fails there is no UI left to carry a notice.
|
|
16106
|
-
reportShutdownFailure: deps.reportShutdownFailure ?? ((step, error) => {
|
|
16107
|
-
process.stderr.write(
|
|
16108
|
-
`[clio-coder:interactive] ${step} failed: ${error instanceof Error ? error.message : String(error)}
|
|
16109
|
-
`
|
|
16110
|
-
);
|
|
16111
|
-
})
|
|
16173
|
+
closeOverlay: () => deps.overlay.closeOverlay(),
|
|
16174
|
+
listNotifications: () => deps.notifications.list(),
|
|
16175
|
+
dismissNotification: (id) => deps.notifications.dismiss(id),
|
|
16176
|
+
dismissAllNotifications: () => deps.notifications.dismissAll(),
|
|
16177
|
+
shutdownDisposers: [
|
|
16178
|
+
() => deps.shutdown.stopTickers(),
|
|
16179
|
+
() => leaderKeys.dispose(),
|
|
16180
|
+
() => removeReload?.(),
|
|
16181
|
+
() => deps.shutdown.disposeInteractiveTickers(),
|
|
16182
|
+
() => deps.shutdown.disposeBeforeStatus(),
|
|
16183
|
+
() => deps.shutdown.disposeProjectionPrimary(),
|
|
16184
|
+
() => deps.shutdown.disposeStatus(),
|
|
16185
|
+
() => deps.shutdown.disposeProjectionRemaining(),
|
|
16186
|
+
() => deps.shutdown.disposeOverlay(),
|
|
16187
|
+
() => deps.shutdown.stopAgentProgress(),
|
|
16188
|
+
() => deps.shutdown.disposeChat(),
|
|
16189
|
+
() => deps.shutdown.disposeSubscriptions()
|
|
16190
|
+
],
|
|
16191
|
+
stopUi: deps.stopUi,
|
|
16192
|
+
...deps.beforeStopUi ? { beforeStopUi: deps.beforeStopUi } : {},
|
|
16193
|
+
cancelParkedCalls: deps.cancelParkedCalls,
|
|
16194
|
+
onShutdown: deps.onShutdown,
|
|
16195
|
+
registerTerminalTeardown: deps.registerTerminalTeardown ?? ((teardown) => getTerminationCoordinator().onDrain(teardown)),
|
|
16196
|
+
// stderr for the same reason src/core/termination.ts uses it for a failed
|
|
16197
|
+
// hook: by the time teardown fails there is no UI left to carry a notice.
|
|
16198
|
+
reportShutdownFailure: deps.reportShutdownFailure ?? ((step, error) => {
|
|
16199
|
+
process.stderr.write(
|
|
16200
|
+
`[clio-coder:interactive] ${step} failed: ${error instanceof Error ? error.message : String(error)}
|
|
16201
|
+
`
|
|
16202
|
+
);
|
|
16203
|
+
})
|
|
16204
|
+
});
|
|
16205
|
+
deps.registerInputListener((data) => {
|
|
16206
|
+
deps.onInputIngress?.(classifyInputAction(data, deps), data);
|
|
16207
|
+
return controller.handleInput(data);
|
|
16208
|
+
});
|
|
16209
|
+
return controller;
|
|
16210
|
+
}
|
|
16211
|
+
function classifyInputAction(data, deps) {
|
|
16212
|
+
if (data.length === 0 || isKeyRelease(data)) return "no-visual-change";
|
|
16213
|
+
if (deps.keybindings.matches(data, "tui.input.submit")) return "submit";
|
|
16214
|
+
const sgrMousePrefix = `${String.fromCharCode(27)}[<`;
|
|
16215
|
+
if (data.startsWith(`${sgrMousePrefix}64;`) || data.startsWith(`${sgrMousePrefix}65;`) || deps.keybindings.matches(data, "tui.altScreen.pageUp") || deps.keybindings.matches(data, "tui.altScreen.pageDown") || deps.keybindings.matches(data, "tui.editor.pageUp") || deps.keybindings.matches(data, "tui.editor.pageDown")) {
|
|
16216
|
+
return "scroll";
|
|
16217
|
+
}
|
|
16218
|
+
if (deps.overlay.getState() !== "closed") return "overlay";
|
|
16219
|
+
return "editor";
|
|
16220
|
+
}
|
|
16221
|
+
function overlayScopeLabel(state) {
|
|
16222
|
+
const names = {
|
|
16223
|
+
closed: "Composer",
|
|
16224
|
+
"permission-confirm": "Permission",
|
|
16225
|
+
"dispatch-board": "Workers",
|
|
16226
|
+
auth: "Authentication",
|
|
16227
|
+
usage: "Usage",
|
|
16228
|
+
"context-view": "Context",
|
|
16229
|
+
"context-reset": "Context reset",
|
|
16230
|
+
tasks: "Tasks",
|
|
16231
|
+
decisions: "Decisions",
|
|
16232
|
+
memory: "Memory",
|
|
16233
|
+
view: "View",
|
|
16234
|
+
model: "Model",
|
|
16235
|
+
"model-scope": "Model cycle set",
|
|
16236
|
+
settings: "Settings",
|
|
16237
|
+
resume: "Resume",
|
|
16238
|
+
tree: "Session tree",
|
|
16239
|
+
"message-picker": "Messages",
|
|
16240
|
+
"cwd-fallback": "Working directory",
|
|
16241
|
+
"ask-user": "Interview",
|
|
16242
|
+
help: "Help",
|
|
16243
|
+
extensions: "Extensions",
|
|
16244
|
+
interop: "Interop",
|
|
16245
|
+
"skills-hub": "Library",
|
|
16246
|
+
"side-question": "Side question",
|
|
16247
|
+
"handoff-review": "Handoff review",
|
|
16248
|
+
"fleet-run-approval": "Run approval"
|
|
16249
|
+
};
|
|
16250
|
+
return names[state];
|
|
16251
|
+
}
|
|
16252
|
+
|
|
16253
|
+
// src/interactive/interactive-presentation.ts
|
|
16254
|
+
init_esm_shims();
|
|
16255
|
+
import { performance as performance3 } from "node:perf_hooks";
|
|
16256
|
+
|
|
16257
|
+
// src/domains/quota/summary-feed.ts
|
|
16258
|
+
init_esm_shims();
|
|
16259
|
+
|
|
16260
|
+
// src/domains/quota/service.ts
|
|
16261
|
+
init_esm_shims();
|
|
16262
|
+
|
|
16263
|
+
// src/domains/quota/cache.ts
|
|
16264
|
+
init_esm_shims();
|
|
16265
|
+
var DEFAULT_QUOTA_CACHE_TTL_MS = 5 * 6e4;
|
|
16266
|
+
function createQuotaCache(options = {}) {
|
|
16267
|
+
const ttlMs = options.ttlMs ?? DEFAULT_QUOTA_CACHE_TTL_MS;
|
|
16268
|
+
const now = options.now ?? Date.now;
|
|
16269
|
+
const entries = /* @__PURE__ */ new Map();
|
|
16270
|
+
const fresh = (entry) => entry !== void 0 && now() - entry.storedAtMs < ttlMs;
|
|
16271
|
+
return {
|
|
16272
|
+
read(providerId) {
|
|
16273
|
+
const entry = entries.get(providerId);
|
|
16274
|
+
return fresh(entry) && entry !== void 0 ? entry.snapshot : null;
|
|
16275
|
+
},
|
|
16276
|
+
readLastGood(providerId) {
|
|
16277
|
+
const entry = entries.get(providerId);
|
|
16278
|
+
if (entry === void 0) return null;
|
|
16279
|
+
return fresh(entry) ? entry.snapshot : { ...entry.snapshot, stale: true };
|
|
16280
|
+
},
|
|
16281
|
+
write(snapshot) {
|
|
16282
|
+
entries.set(snapshot.providerId, { snapshot, storedAtMs: now() });
|
|
16283
|
+
},
|
|
16284
|
+
clear(providerId) {
|
|
16285
|
+
if (providerId === void 0) entries.clear();
|
|
16286
|
+
else entries.delete(providerId);
|
|
16287
|
+
},
|
|
16288
|
+
async resolve(provider) {
|
|
16289
|
+
const cached = entries.get(provider.id);
|
|
16290
|
+
if (fresh(cached) && cached !== void 0) return cached.snapshot;
|
|
16291
|
+
const snapshot = await provider.fetch();
|
|
16292
|
+
if (snapshot.status === "ok") {
|
|
16293
|
+
entries.set(provider.id, { snapshot, storedAtMs: now() });
|
|
16294
|
+
return snapshot;
|
|
16295
|
+
}
|
|
16296
|
+
if (cached === void 0) return snapshot;
|
|
16297
|
+
return {
|
|
16298
|
+
...cached.snapshot,
|
|
16299
|
+
stale: true,
|
|
16300
|
+
message: snapshot.message ?? cached.snapshot.message ?? null
|
|
16301
|
+
};
|
|
16302
|
+
}
|
|
16303
|
+
};
|
|
16304
|
+
}
|
|
16305
|
+
|
|
16306
|
+
// src/domains/quota/registry.ts
|
|
16307
|
+
init_esm_shims();
|
|
16308
|
+
|
|
16309
|
+
// src/domains/quota/anthropic-max-provider.ts
|
|
16310
|
+
init_esm_shims();
|
|
16311
|
+
|
|
16312
|
+
// src/domains/quota/anthropic-usage.ts
|
|
16313
|
+
init_esm_shims();
|
|
16314
|
+
|
|
16315
|
+
// src/domains/quota/types.ts
|
|
16316
|
+
init_esm_shims();
|
|
16317
|
+
function parseRetryAfterSeconds(value) {
|
|
16318
|
+
if (!value) return null;
|
|
16319
|
+
const seconds = Number.parseFloat(value.trim());
|
|
16320
|
+
if (!Number.isFinite(seconds)) return null;
|
|
16321
|
+
return Math.max(0, seconds);
|
|
16322
|
+
}
|
|
16323
|
+
function formatPlan(value) {
|
|
16324
|
+
if (!value) return null;
|
|
16325
|
+
const words = value.replace(/_/g, " ").trim();
|
|
16326
|
+
if (!words) return null;
|
|
16327
|
+
return words.replace(/\S+/g, (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());
|
|
16328
|
+
}
|
|
16329
|
+
|
|
16330
|
+
// src/domains/quota/anthropic-usage.ts
|
|
16331
|
+
var ANTHROPIC_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
16332
|
+
var ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
|
|
16333
|
+
var WINDOW_ORDER = { session: 0, weekly: 1 };
|
|
16334
|
+
function outcome(status, message, extra = {}) {
|
|
16335
|
+
return { status, message, windows: [], credits: null, retryAfterSeconds: null, ...extra };
|
|
16336
|
+
}
|
|
16337
|
+
async function fetchAnthropicUsage(token, dependencies) {
|
|
16338
|
+
const controller = new AbortController();
|
|
16339
|
+
const timer = setTimeout(() => controller.abort(), dependencies.timeoutMs);
|
|
16340
|
+
let response;
|
|
16341
|
+
try {
|
|
16342
|
+
response = await dependencies.fetch(ANTHROPIC_USAGE_URL, {
|
|
16343
|
+
method: "GET",
|
|
16344
|
+
headers: {
|
|
16345
|
+
Authorization: `Bearer ${token}`,
|
|
16346
|
+
"anthropic-beta": ANTHROPIC_OAUTH_BETA
|
|
16347
|
+
},
|
|
16348
|
+
signal: controller.signal
|
|
16349
|
+
});
|
|
16350
|
+
} catch (error) {
|
|
16351
|
+
return outcome("error", describeError2(error, dependencies.timeoutMs));
|
|
16352
|
+
} finally {
|
|
16353
|
+
clearTimeout(timer);
|
|
16354
|
+
}
|
|
16355
|
+
if (response.status === 401 || response.status === 403) {
|
|
16356
|
+
await discardBody(response);
|
|
16357
|
+
return outcome("expired", null);
|
|
16358
|
+
}
|
|
16359
|
+
if (response.status === 429) {
|
|
16360
|
+
await discardBody(response);
|
|
16361
|
+
return outcome("error", "HTTP 429", {
|
|
16362
|
+
retryAfterSeconds: parseRetryAfterSeconds(response.headers.get("Retry-After"))
|
|
16363
|
+
});
|
|
16364
|
+
}
|
|
16365
|
+
if (response.status !== 200) {
|
|
16366
|
+
await discardBody(response);
|
|
16367
|
+
return outcome("error", `HTTP ${response.status}`);
|
|
16368
|
+
}
|
|
16369
|
+
let body;
|
|
16370
|
+
try {
|
|
16371
|
+
body = await response.json();
|
|
16372
|
+
} catch {
|
|
16373
|
+
return outcome("error", "Unexpected usage response");
|
|
16374
|
+
}
|
|
16375
|
+
if (!isRecord(body)) {
|
|
16376
|
+
return outcome("error", "Unexpected usage response");
|
|
16377
|
+
}
|
|
16378
|
+
return outcome("ok", null, { windows: usageWindows(body), credits: usageCredits(body.spend) });
|
|
16379
|
+
}
|
|
16380
|
+
function usageWindows(body) {
|
|
16381
|
+
const fromLimits = windowsFromLimits(body.limits);
|
|
16382
|
+
const windows = fromLimits.length > 0 ? fromLimits : windowsFromFlat(body);
|
|
16383
|
+
return windows.sort((left, right) => {
|
|
16384
|
+
const order = (WINDOW_ORDER[left.key] ?? 2) - (WINDOW_ORDER[right.key] ?? 2);
|
|
16385
|
+
return order !== 0 ? order : left.label.localeCompare(right.label);
|
|
16386
|
+
});
|
|
16387
|
+
}
|
|
16388
|
+
function windowsFromLimits(raw) {
|
|
16389
|
+
if (!Array.isArray(raw)) return [];
|
|
16390
|
+
const windows = [];
|
|
16391
|
+
for (const entry of raw) {
|
|
16392
|
+
if (!isRecord(entry)) continue;
|
|
16393
|
+
const percent = asNumber(entry.percent);
|
|
16394
|
+
if (percent === null) continue;
|
|
16395
|
+
const common = {
|
|
16396
|
+
usedPct: percent,
|
|
16397
|
+
resetsAt: asIsoString(entry.resets_at)
|
|
16398
|
+
};
|
|
16399
|
+
if (typeof entry.severity === "string") common.severity = entry.severity;
|
|
16400
|
+
if (entry.is_active === true) common.active = true;
|
|
16401
|
+
if (entry.kind === "session") {
|
|
16402
|
+
windows.push({ key: "session", label: "5h", short: "5h", ...common });
|
|
16403
|
+
} else if (entry.kind === "weekly_all") {
|
|
16404
|
+
windows.push({ key: "weekly", label: "Weekly", short: "wk", ...common });
|
|
16405
|
+
} else if (entry.kind === "weekly_scoped") {
|
|
16406
|
+
const name = scopeName(entry.scope);
|
|
16407
|
+
if (name === null) continue;
|
|
16408
|
+
windows.push({ key: `weekly_scoped.${slug(name)}`, label: name, scope: name, ...common });
|
|
16409
|
+
}
|
|
16410
|
+
}
|
|
16411
|
+
return windows;
|
|
16412
|
+
}
|
|
16413
|
+
function windowsFromFlat(body) {
|
|
16414
|
+
const windows = [];
|
|
16415
|
+
const fiveHour = bucketWindow(body.five_hour, "session", "5h", "5h");
|
|
16416
|
+
if (fiveHour !== null) windows.push(fiveHour);
|
|
16417
|
+
const sevenDay = bucketWindow(body.seven_day, "weekly", "Weekly", "wk");
|
|
16418
|
+
if (sevenDay !== null) windows.push(sevenDay);
|
|
16419
|
+
return windows;
|
|
16420
|
+
}
|
|
16421
|
+
function bucketWindow(raw, key, label, short) {
|
|
16422
|
+
if (!isRecord(raw)) return null;
|
|
16423
|
+
const usedPct = asNumber(raw.utilization);
|
|
16424
|
+
if (usedPct === null) return null;
|
|
16425
|
+
return { key, label, short, usedPct, resetsAt: asIsoString(raw.resets_at) };
|
|
16426
|
+
}
|
|
16427
|
+
function scopeName(raw) {
|
|
16428
|
+
if (!isRecord(raw)) return null;
|
|
16429
|
+
const model = raw.model;
|
|
16430
|
+
if (!isRecord(model)) return null;
|
|
16431
|
+
const name = typeof model.display_name === "string" ? model.display_name.trim() : "";
|
|
16432
|
+
if (name.length === 0) return null;
|
|
16433
|
+
return slug(name) === "all-models" || slug(name) === "all" ? null : name;
|
|
16434
|
+
}
|
|
16435
|
+
function usageCredits(raw) {
|
|
16436
|
+
if (!isRecord(raw) || raw.enabled !== true) return null;
|
|
16437
|
+
const percent = asNumber(raw.percent);
|
|
16438
|
+
const limit = majorAmount(raw.limit);
|
|
16439
|
+
if (limit === null) {
|
|
16440
|
+
return percent === null ? null : { display: `${percent.toFixed(0)}% used`, usedPct: percent };
|
|
16441
|
+
}
|
|
16442
|
+
const used = majorAmount(raw.used);
|
|
16443
|
+
if (used === null) return null;
|
|
16444
|
+
const currency = isRecord(raw.limit) && typeof raw.limit.currency === "string" ? raw.limit.currency : "USD";
|
|
16445
|
+
const symbol = currency === "USD" ? "$" : "";
|
|
16446
|
+
const usedPct = percent ?? (limit > 0 ? used / limit * 100 : null);
|
|
16447
|
+
return { display: `${symbol}${used.toFixed(2)} / ${symbol}${limit.toFixed(2)}`, usedPct };
|
|
16448
|
+
}
|
|
16449
|
+
function majorAmount(raw) {
|
|
16450
|
+
if (!isRecord(raw)) return null;
|
|
16451
|
+
const minor = asNumber(raw.amount_minor);
|
|
16452
|
+
if (minor === null) return null;
|
|
16453
|
+
const exponent = asNumber(raw.exponent) ?? 0;
|
|
16454
|
+
return minor / 10 ** exponent;
|
|
16455
|
+
}
|
|
16456
|
+
function isRecord(value) {
|
|
16457
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16458
|
+
}
|
|
16459
|
+
function asNumber(value) {
|
|
16460
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
16461
|
+
if (typeof value === "string") {
|
|
16462
|
+
const parsed = Number.parseFloat(value.trim());
|
|
16463
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
16464
|
+
}
|
|
16465
|
+
return null;
|
|
16466
|
+
}
|
|
16467
|
+
function asIsoString(value) {
|
|
16468
|
+
if (typeof value !== "string" || value.trim().length === 0) return null;
|
|
16469
|
+
const parsed = new Date(value);
|
|
16470
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString();
|
|
16471
|
+
}
|
|
16472
|
+
function slug(value) {
|
|
16473
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
16474
|
+
}
|
|
16475
|
+
function describeError2(error, timeoutMs) {
|
|
16476
|
+
if (error instanceof Error) {
|
|
16477
|
+
return error.name === "AbortError" ? `timeout after ${timeoutMs}ms` : error.message;
|
|
16478
|
+
}
|
|
16479
|
+
return String(error);
|
|
16480
|
+
}
|
|
16481
|
+
async function discardBody(response) {
|
|
16482
|
+
try {
|
|
16483
|
+
await response.body?.cancel();
|
|
16484
|
+
} catch {
|
|
16485
|
+
return;
|
|
16486
|
+
}
|
|
16487
|
+
}
|
|
16488
|
+
|
|
16489
|
+
// src/domains/quota/anthropic-max-provider.ts
|
|
16490
|
+
var ANTHROPIC_MAX_QUOTA_PROVIDER_ID = "anthropic-max";
|
|
16491
|
+
var AUTH_PROVIDER_ID = "anthropic";
|
|
16492
|
+
var DISPLAY_NAME = "Anthropic Max";
|
|
16493
|
+
var REQUEST_TIMEOUT_MS = 15e3;
|
|
16494
|
+
function parseStoredAnthropicCredential(raw) {
|
|
16495
|
+
if (!isRecord2(raw) || raw.type !== "oauth") return null;
|
|
16496
|
+
const access = raw.access;
|
|
16497
|
+
if (typeof access !== "string" || access.length === 0) return null;
|
|
16498
|
+
const refresh = raw.refresh;
|
|
16499
|
+
return {
|
|
16500
|
+
accessToken: access,
|
|
16501
|
+
expiresAtMs: asNumber2(raw.expires),
|
|
16502
|
+
hasRefreshToken: typeof refresh === "string" && refresh.length > 0
|
|
16503
|
+
};
|
|
16504
|
+
}
|
|
16505
|
+
async function readStoredCredential() {
|
|
16506
|
+
try {
|
|
16507
|
+
return parseStoredAnthropicCredential(openAuthStorage().get(AUTH_PROVIDER_ID));
|
|
16508
|
+
} catch {
|
|
16509
|
+
return null;
|
|
16510
|
+
}
|
|
16511
|
+
}
|
|
16512
|
+
function createAnthropicMaxQuotaProvider(options = {}) {
|
|
16513
|
+
const readCredentials = options.readCredentials ?? readStoredCredential;
|
|
16514
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
16515
|
+
const timeoutMs = options.timeoutMs ?? REQUEST_TIMEOUT_MS;
|
|
16516
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
16517
|
+
const snapshot = (status, message, extra = {}) => ({
|
|
16518
|
+
providerId: ANTHROPIC_MAX_QUOTA_PROVIDER_ID,
|
|
16519
|
+
displayName: DISPLAY_NAME,
|
|
16520
|
+
status,
|
|
16521
|
+
windows: [],
|
|
16522
|
+
message,
|
|
16523
|
+
fetchedAt: now().toISOString(),
|
|
16524
|
+
...extra
|
|
16525
|
+
});
|
|
16526
|
+
return {
|
|
16527
|
+
id: ANTHROPIC_MAX_QUOTA_PROVIDER_ID,
|
|
16528
|
+
displayName: DISPLAY_NAME,
|
|
16529
|
+
async detect() {
|
|
16530
|
+
return await readCredentials() !== null;
|
|
16531
|
+
},
|
|
16532
|
+
async fetch() {
|
|
16533
|
+
const credentials = await readCredentials();
|
|
16534
|
+
if (credentials === null) {
|
|
16535
|
+
return snapshot("no_credentials", "Clio holds no Anthropic OAuth credential");
|
|
16536
|
+
}
|
|
16537
|
+
if (credentials.expiresAtMs !== null && credentials.expiresAtMs <= now().getTime()) {
|
|
16538
|
+
return snapshot(
|
|
16539
|
+
"expired",
|
|
16540
|
+
credentials.hasRefreshToken ? "Clio's Anthropic token expired; the next turn refreshes it" : "Run clio-coder auth login anthropic again"
|
|
16541
|
+
);
|
|
16542
|
+
}
|
|
16543
|
+
const usage = await fetchAnthropicUsage(credentials.accessToken, { fetch: doFetch, timeoutMs });
|
|
16544
|
+
if (usage.status !== "ok") {
|
|
16545
|
+
return snapshot(usage.status, usage.message ?? "Run clio-coder auth login anthropic again", {
|
|
16546
|
+
retryAfterSeconds: usage.retryAfterSeconds
|
|
16547
|
+
});
|
|
16548
|
+
}
|
|
16549
|
+
return snapshot("ok", null, { windows: usage.windows, credits: usage.credits });
|
|
16550
|
+
}
|
|
16551
|
+
};
|
|
16552
|
+
}
|
|
16553
|
+
function isRecord2(value) {
|
|
16554
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16555
|
+
}
|
|
16556
|
+
function asNumber2(value) {
|
|
16557
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
16558
|
+
if (typeof value === "string") {
|
|
16559
|
+
const parsed = Number.parseFloat(value.trim());
|
|
16560
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
16561
|
+
}
|
|
16562
|
+
return null;
|
|
16563
|
+
}
|
|
16564
|
+
|
|
16565
|
+
// src/domains/quota/antigravity-provider.ts
|
|
16566
|
+
init_esm_shims();
|
|
16567
|
+
import { readFile } from "node:fs/promises";
|
|
16568
|
+
import { homedir as homedir2 } from "node:os";
|
|
16569
|
+
import { join } from "node:path";
|
|
16570
|
+
var ANTIGRAVITY_QUOTA_PROVIDER_ID = "antigravity";
|
|
16571
|
+
var DISPLAY_NAME2 = "Antigravity";
|
|
16572
|
+
var USER_AGENT = "antigravity";
|
|
16573
|
+
var REQUEST_TIMEOUT_MS2 = 2e4;
|
|
16574
|
+
var EXPIRY_SKEW_MS = 6e4;
|
|
16575
|
+
var WINDOW_ORDER2 = { session: 0, weekly: 1 };
|
|
16576
|
+
var MODEL_PREFIXES = ["gemini", "claude", "gpt", "image", "imagen"];
|
|
16577
|
+
var ENDPOINTS = [
|
|
16578
|
+
"https://daily-cloudcode-pa.googleapis.com",
|
|
16579
|
+
"https://daily-cloudcode-pa.sandbox.googleapis.com",
|
|
16580
|
+
"https://cloudcode-pa.googleapis.com"
|
|
16581
|
+
];
|
|
16582
|
+
function defaultCredentialsPath() {
|
|
16583
|
+
return join(homedir2(), ".gemini", "antigravity-cli", "antigravity-oauth-token");
|
|
16584
|
+
}
|
|
16585
|
+
function parseCredentials(raw) {
|
|
16586
|
+
if (!isRecord3(raw)) return null;
|
|
16587
|
+
const token = raw.token;
|
|
16588
|
+
if (!isRecord3(token)) return null;
|
|
16589
|
+
const accessToken = token.access_token;
|
|
16590
|
+
if (typeof accessToken !== "string" || accessToken.length === 0) return null;
|
|
16591
|
+
const refreshToken = token.refresh_token;
|
|
16592
|
+
return {
|
|
16593
|
+
accessToken,
|
|
16594
|
+
refreshToken: typeof refreshToken === "string" && refreshToken.length > 0 ? refreshToken : null,
|
|
16595
|
+
expiryMs: asEpochMs(token.expiry)
|
|
16596
|
+
};
|
|
16597
|
+
}
|
|
16598
|
+
async function readCredentialsFile(path) {
|
|
16599
|
+
let text;
|
|
16600
|
+
try {
|
|
16601
|
+
text = await readFile(path, "utf8");
|
|
16602
|
+
} catch {
|
|
16603
|
+
return null;
|
|
16604
|
+
}
|
|
16605
|
+
try {
|
|
16606
|
+
return parseCredentials(JSON.parse(text));
|
|
16607
|
+
} catch {
|
|
16608
|
+
return null;
|
|
16609
|
+
}
|
|
16610
|
+
}
|
|
16611
|
+
function createAntigravityQuotaProvider(options = {}) {
|
|
16612
|
+
const credentialsPath = options.credentialsPath ?? defaultCredentialsPath();
|
|
16613
|
+
const readCredentials = options.readCredentials ?? (() => readCredentialsFile(credentialsPath));
|
|
16614
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
16615
|
+
const timeoutMs = options.timeoutMs ?? REQUEST_TIMEOUT_MS2;
|
|
16616
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
16617
|
+
const snapshot = (status, message, extra = {}) => ({
|
|
16618
|
+
providerId: ANTIGRAVITY_QUOTA_PROVIDER_ID,
|
|
16619
|
+
displayName: DISPLAY_NAME2,
|
|
16620
|
+
status,
|
|
16621
|
+
windows: [],
|
|
16622
|
+
message,
|
|
16623
|
+
fetchedAt: now().toISOString(),
|
|
16624
|
+
...extra
|
|
16625
|
+
});
|
|
16626
|
+
const post = async (url, token, body) => {
|
|
16627
|
+
const controller = new AbortController();
|
|
16628
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
16629
|
+
let response;
|
|
16630
|
+
try {
|
|
16631
|
+
response = await doFetch(url, {
|
|
16632
|
+
method: "POST",
|
|
16633
|
+
headers: {
|
|
16634
|
+
Authorization: `Bearer ${token}`,
|
|
16635
|
+
"Content-Type": "application/json",
|
|
16636
|
+
"User-Agent": USER_AGENT
|
|
16637
|
+
},
|
|
16638
|
+
body: JSON.stringify(body),
|
|
16639
|
+
signal: controller.signal
|
|
16640
|
+
});
|
|
16641
|
+
} catch (error) {
|
|
16642
|
+
return { kind: "error", message: describeError3(error, timeoutMs) };
|
|
16643
|
+
} finally {
|
|
16644
|
+
clearTimeout(timer);
|
|
16645
|
+
}
|
|
16646
|
+
if (response.status === 401 || response.status === 403) {
|
|
16647
|
+
await discardBody2(response);
|
|
16648
|
+
return { kind: "auth" };
|
|
16649
|
+
}
|
|
16650
|
+
if (response.status !== 200) {
|
|
16651
|
+
await discardBody2(response);
|
|
16652
|
+
return { kind: "error", message: `HTTP ${response.status}` };
|
|
16653
|
+
}
|
|
16654
|
+
let data;
|
|
16655
|
+
try {
|
|
16656
|
+
data = await response.json();
|
|
16657
|
+
} catch {
|
|
16658
|
+
return { kind: "error", message: "Unexpected usage response" };
|
|
16659
|
+
}
|
|
16660
|
+
return isRecord3(data) ? { kind: "ok", data } : { kind: "error", message: "Unexpected usage response" };
|
|
16661
|
+
};
|
|
16662
|
+
const collect = async (base, token) => {
|
|
16663
|
+
const load = await post(`${base}/v1internal:loadCodeAssist`, token, { metadata: { ideType: "ANTIGRAVITY" } });
|
|
16664
|
+
if (load.kind !== "ok") return load;
|
|
16665
|
+
const project = typeof load.data.cloudaicompanionProject === "string" ? load.data.cloudaicompanionProject : null;
|
|
16666
|
+
const plan = tierPlan(load.data.currentTier);
|
|
16667
|
+
if (project !== null) {
|
|
16668
|
+
const summary = await post(`${base}/v1internal:retrieveUserQuotaSummary`, token, { project });
|
|
16669
|
+
if (summary.kind === "auth") return summary;
|
|
16670
|
+
if (summary.kind === "ok") {
|
|
16671
|
+
const windows = windowsFromSummary(summary.data);
|
|
16672
|
+
if (windows.length > 0) return { kind: "ok", windows, plan };
|
|
16673
|
+
}
|
|
16674
|
+
}
|
|
16675
|
+
const models = await post(`${base}/v1internal:fetchAvailableModels`, token, project === null ? {} : { project });
|
|
16676
|
+
if (models.kind !== "ok") return models;
|
|
16677
|
+
return { kind: "ok", windows: modelWindows(models.data), plan };
|
|
16678
|
+
};
|
|
16679
|
+
return {
|
|
16680
|
+
id: ANTIGRAVITY_QUOTA_PROVIDER_ID,
|
|
16681
|
+
displayName: DISPLAY_NAME2,
|
|
16682
|
+
async detect() {
|
|
16683
|
+
return await readCredentials() !== null;
|
|
16684
|
+
},
|
|
16685
|
+
async fetch() {
|
|
16686
|
+
const credentials = await readCredentials();
|
|
16687
|
+
if (credentials === null) {
|
|
16688
|
+
return snapshot("no_credentials", "No Antigravity credentials found");
|
|
16689
|
+
}
|
|
16690
|
+
if (credentials.expiryMs !== null && credentials.expiryMs <= now().getTime() + EXPIRY_SKEW_MS) {
|
|
16691
|
+
return snapshot("expired", "Antigravity's stored token expired; run the agy command once to refresh it");
|
|
16692
|
+
}
|
|
16693
|
+
let sawAuthFailure = false;
|
|
16694
|
+
let lastError = null;
|
|
16695
|
+
for (const base of ENDPOINTS) {
|
|
16696
|
+
const result = await collect(base, credentials.accessToken);
|
|
16697
|
+
if (result.kind === "auth") {
|
|
16698
|
+
sawAuthFailure = true;
|
|
16699
|
+
continue;
|
|
16700
|
+
}
|
|
16701
|
+
if (result.kind === "error") {
|
|
16702
|
+
lastError = result.message;
|
|
16703
|
+
continue;
|
|
16704
|
+
}
|
|
16705
|
+
if (result.windows.length > 0) {
|
|
16706
|
+
return snapshot("ok", null, { windows: result.windows, plan: result.plan });
|
|
16707
|
+
}
|
|
16708
|
+
}
|
|
16709
|
+
if (sawAuthFailure) return snapshot("expired", "Sign in with Antigravity again");
|
|
16710
|
+
if (lastError !== null) return snapshot("error", lastError);
|
|
16711
|
+
return snapshot("error", "Antigravity returned no quota");
|
|
16712
|
+
}
|
|
16713
|
+
};
|
|
16714
|
+
}
|
|
16715
|
+
function tierPlan(raw) {
|
|
16716
|
+
if (!isRecord3(raw)) return null;
|
|
16717
|
+
const plan = formatPlan(typeof raw.name === "string" ? raw.name : null);
|
|
16718
|
+
if (plan === null) return null;
|
|
16719
|
+
return plan.toLowerCase() === DISPLAY_NAME2.toLowerCase() ? null : plan;
|
|
16720
|
+
}
|
|
16721
|
+
function windowsFromSummary(data) {
|
|
16722
|
+
if (!Array.isArray(data.groups)) return [];
|
|
16723
|
+
const groups = data.groups.filter(isRecord3).sort((a, b) => Number(isGeminiGroup(b)) - Number(isGeminiGroup(a)));
|
|
16724
|
+
return groups.flatMap((group, index) => {
|
|
16725
|
+
const scope = typeof group.displayName === "string" && group.displayName.trim() ? group.displayName.trim() : `Model group ${index + 1}`;
|
|
16726
|
+
return windowsFromGroup(group).map((window) => ({
|
|
16727
|
+
...window,
|
|
16728
|
+
key: index === 0 ? window.key : `group.${index}.${window.key}`,
|
|
16729
|
+
scope
|
|
16730
|
+
}));
|
|
16112
16731
|
});
|
|
16113
|
-
|
|
16114
|
-
|
|
16115
|
-
|
|
16732
|
+
}
|
|
16733
|
+
function windowsFromGroup(group) {
|
|
16734
|
+
if (!Array.isArray(group.buckets)) return [];
|
|
16735
|
+
const windows = [];
|
|
16736
|
+
for (const bucket of group.buckets) {
|
|
16737
|
+
if (!isRecord3(bucket)) continue;
|
|
16738
|
+
const window = typeof bucket.window === "string" ? bucket.window.toLowerCase() : null;
|
|
16739
|
+
const usedPct = usedFromRemaining(bucket.remainingFraction);
|
|
16740
|
+
if (usedPct === null) continue;
|
|
16741
|
+
if (window === "5h") {
|
|
16742
|
+
windows.push({ key: "session", label: "5h", short: "5h", usedPct, resetsAt: asIsoString2(bucket.resetTime) });
|
|
16743
|
+
} else if (window === "weekly") {
|
|
16744
|
+
windows.push({ key: "weekly", label: "Weekly", short: "wk", usedPct, resetsAt: asIsoString2(bucket.resetTime) });
|
|
16745
|
+
}
|
|
16746
|
+
}
|
|
16747
|
+
return windows.sort((left, right) => (WINDOW_ORDER2[left.key] ?? 2) - (WINDOW_ORDER2[right.key] ?? 2));
|
|
16748
|
+
}
|
|
16749
|
+
function isGeminiGroup(group) {
|
|
16750
|
+
if (mentionsGemini(group.displayName) || mentionsGemini(group.description)) return true;
|
|
16751
|
+
if (!Array.isArray(group.buckets)) return false;
|
|
16752
|
+
return group.buckets.some((bucket) => {
|
|
16753
|
+
if (!isRecord3(bucket)) return false;
|
|
16754
|
+
const id = typeof bucket.bucketId === "string" ? bucket.bucketId.toLowerCase() : "";
|
|
16755
|
+
return id.startsWith("gemini-") || mentionsGemini(bucket.displayName);
|
|
16116
16756
|
});
|
|
16117
|
-
return controller;
|
|
16118
16757
|
}
|
|
16119
|
-
function
|
|
16120
|
-
|
|
16121
|
-
|
|
16122
|
-
|
|
16123
|
-
if (
|
|
16124
|
-
|
|
16758
|
+
function mentionsGemini(value) {
|
|
16759
|
+
return typeof value === "string" && value.toLowerCase().includes("gemini");
|
|
16760
|
+
}
|
|
16761
|
+
function modelWindows(data) {
|
|
16762
|
+
if (!isRecord3(data.models)) return [];
|
|
16763
|
+
const windows = [];
|
|
16764
|
+
for (const [name, info] of Object.entries(data.models)) {
|
|
16765
|
+
if (!isRecord3(info) || !isRecord3(info.quotaInfo)) continue;
|
|
16766
|
+
if (!MODEL_PREFIXES.some((prefix) => name.toLowerCase().startsWith(prefix))) continue;
|
|
16767
|
+
const usedPct = usedFromRemaining(info.quotaInfo.remainingFraction);
|
|
16768
|
+
if (usedPct === null) continue;
|
|
16769
|
+
windows.push({
|
|
16770
|
+
key: `model.${name}`,
|
|
16771
|
+
label: "Model quota",
|
|
16772
|
+
short: name,
|
|
16773
|
+
scope: name,
|
|
16774
|
+
usedPct,
|
|
16775
|
+
resetsAt: asIsoString2(info.quotaInfo.resetTime)
|
|
16776
|
+
});
|
|
16125
16777
|
}
|
|
16126
|
-
|
|
16127
|
-
return "editor";
|
|
16778
|
+
return windows;
|
|
16128
16779
|
}
|
|
16129
|
-
function
|
|
16130
|
-
const
|
|
16131
|
-
|
|
16132
|
-
|
|
16133
|
-
|
|
16134
|
-
|
|
16135
|
-
|
|
16136
|
-
|
|
16137
|
-
|
|
16138
|
-
|
|
16139
|
-
|
|
16140
|
-
|
|
16141
|
-
|
|
16142
|
-
|
|
16143
|
-
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
|
|
16147
|
-
|
|
16148
|
-
|
|
16149
|
-
|
|
16150
|
-
|
|
16151
|
-
|
|
16152
|
-
|
|
16153
|
-
|
|
16154
|
-
|
|
16155
|
-
|
|
16156
|
-
|
|
16780
|
+
function usedFromRemaining(raw) {
|
|
16781
|
+
const remaining = asNumber3(raw);
|
|
16782
|
+
if (remaining === null) return null;
|
|
16783
|
+
return (1 - Math.min(1, Math.max(0, remaining))) * 100;
|
|
16784
|
+
}
|
|
16785
|
+
function isRecord3(value) {
|
|
16786
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16787
|
+
}
|
|
16788
|
+
function asNumber3(value) {
|
|
16789
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
16790
|
+
if (typeof value === "string") {
|
|
16791
|
+
const parsed = Number.parseFloat(value.trim());
|
|
16792
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
16793
|
+
}
|
|
16794
|
+
return null;
|
|
16795
|
+
}
|
|
16796
|
+
function asIsoString2(value) {
|
|
16797
|
+
if (typeof value !== "string" || value.trim().length === 0) return null;
|
|
16798
|
+
const parsed = new Date(value);
|
|
16799
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString();
|
|
16800
|
+
}
|
|
16801
|
+
function asEpochMs(value) {
|
|
16802
|
+
if (typeof value !== "string" || value.trim().length === 0) return null;
|
|
16803
|
+
const parsed = Date.parse(value);
|
|
16804
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
16805
|
+
}
|
|
16806
|
+
function describeError3(error, timeoutMs) {
|
|
16807
|
+
if (error instanceof Error) {
|
|
16808
|
+
return error.name === "AbortError" ? `timeout after ${timeoutMs}ms` : error.message;
|
|
16809
|
+
}
|
|
16810
|
+
return String(error);
|
|
16811
|
+
}
|
|
16812
|
+
async function discardBody2(response) {
|
|
16813
|
+
try {
|
|
16814
|
+
await response.body?.cancel();
|
|
16815
|
+
} catch {
|
|
16816
|
+
return;
|
|
16817
|
+
}
|
|
16818
|
+
}
|
|
16819
|
+
|
|
16820
|
+
// src/domains/quota/claude-code-provider.ts
|
|
16821
|
+
init_esm_shims();
|
|
16822
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
16823
|
+
import { homedir as homedir3 } from "node:os";
|
|
16824
|
+
import { join as join2 } from "node:path";
|
|
16825
|
+
var CLAUDE_CODE_QUOTA_PROVIDER_ID = "claude-code";
|
|
16826
|
+
var DISPLAY_NAME3 = "Claude Code";
|
|
16827
|
+
var REQUEST_TIMEOUT_MS3 = 15e3;
|
|
16828
|
+
function defaultClaudeCredentialsPath() {
|
|
16829
|
+
return join2(homedir3(), ".claude", ".credentials.json");
|
|
16830
|
+
}
|
|
16831
|
+
function parseClaudeCredentials(raw) {
|
|
16832
|
+
if (!isRecord4(raw)) return null;
|
|
16833
|
+
const oauth = raw.claudeAiOauth;
|
|
16834
|
+
if (!isRecord4(oauth)) return null;
|
|
16835
|
+
const token = oauth.accessToken;
|
|
16836
|
+
if (typeof token !== "string" || token.length === 0) return null;
|
|
16837
|
+
const subscription = oauth.subscriptionType;
|
|
16838
|
+
const refreshToken = oauth.refreshToken;
|
|
16839
|
+
return {
|
|
16840
|
+
accessToken: token,
|
|
16841
|
+
subscriptionType: typeof subscription === "string" ? subscription : null,
|
|
16842
|
+
expiresAtMs: asNumber4(oauth.expiresAt),
|
|
16843
|
+
refreshTokenExpiresAtMs: asNumber4(oauth.refreshTokenExpiresAt),
|
|
16844
|
+
hasRefreshToken: typeof refreshToken === "string" && refreshToken.length > 0
|
|
16157
16845
|
};
|
|
16158
|
-
|
|
16846
|
+
}
|
|
16847
|
+
function expiryMessage(credentials, nowMs) {
|
|
16848
|
+
const { expiresAtMs, refreshTokenExpiresAtMs, hasRefreshToken } = credentials;
|
|
16849
|
+
if (expiresAtMs === null || expiresAtMs > nowMs) return null;
|
|
16850
|
+
const refreshUsable = hasRefreshToken && (refreshTokenExpiresAtMs === null || refreshTokenExpiresAtMs > nowMs);
|
|
16851
|
+
return refreshUsable ? "Claude Code's stored token expired; run the claude command once to refresh it" : "Sign in with Claude Code again";
|
|
16852
|
+
}
|
|
16853
|
+
async function readCredentialsFile2(path) {
|
|
16854
|
+
let text;
|
|
16855
|
+
try {
|
|
16856
|
+
text = await readFile2(path, "utf8");
|
|
16857
|
+
} catch {
|
|
16858
|
+
return null;
|
|
16859
|
+
}
|
|
16860
|
+
try {
|
|
16861
|
+
return parseClaudeCredentials(JSON.parse(text));
|
|
16862
|
+
} catch {
|
|
16863
|
+
return null;
|
|
16864
|
+
}
|
|
16865
|
+
}
|
|
16866
|
+
function createClaudeCodeQuotaProvider(options = {}) {
|
|
16867
|
+
const credentialsPath = options.credentialsPath ?? defaultClaudeCredentialsPath();
|
|
16868
|
+
const readCredentials = options.readCredentials ?? (() => readCredentialsFile2(credentialsPath));
|
|
16869
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
16870
|
+
const timeoutMs = options.timeoutMs ?? REQUEST_TIMEOUT_MS3;
|
|
16871
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
16872
|
+
const snapshot = (status, message, extra = {}) => ({
|
|
16873
|
+
providerId: CLAUDE_CODE_QUOTA_PROVIDER_ID,
|
|
16874
|
+
displayName: DISPLAY_NAME3,
|
|
16875
|
+
status,
|
|
16876
|
+
windows: [],
|
|
16877
|
+
message,
|
|
16878
|
+
fetchedAt: now().toISOString(),
|
|
16879
|
+
...extra
|
|
16880
|
+
});
|
|
16881
|
+
return {
|
|
16882
|
+
id: CLAUDE_CODE_QUOTA_PROVIDER_ID,
|
|
16883
|
+
displayName: DISPLAY_NAME3,
|
|
16884
|
+
async detect() {
|
|
16885
|
+
return await readCredentials() !== null;
|
|
16886
|
+
},
|
|
16887
|
+
async fetch() {
|
|
16888
|
+
const credentials = await readCredentials();
|
|
16889
|
+
if (credentials === null) {
|
|
16890
|
+
return snapshot("no_credentials", "No Claude Code credentials found");
|
|
16891
|
+
}
|
|
16892
|
+
const expired = expiryMessage(credentials, now().getTime());
|
|
16893
|
+
if (expired !== null) {
|
|
16894
|
+
return snapshot("expired", expired);
|
|
16895
|
+
}
|
|
16896
|
+
const usage = await fetchAnthropicUsage(credentials.accessToken, { fetch: doFetch, timeoutMs });
|
|
16897
|
+
if (usage.status !== "ok") {
|
|
16898
|
+
return snapshot(usage.status, usage.message ?? "Sign in with Claude Code again", {
|
|
16899
|
+
retryAfterSeconds: usage.retryAfterSeconds
|
|
16900
|
+
});
|
|
16901
|
+
}
|
|
16902
|
+
return snapshot("ok", null, {
|
|
16903
|
+
windows: usage.windows,
|
|
16904
|
+
credits: usage.credits,
|
|
16905
|
+
plan: formatPlan(credentials.subscriptionType)
|
|
16906
|
+
});
|
|
16907
|
+
}
|
|
16908
|
+
};
|
|
16909
|
+
}
|
|
16910
|
+
function isRecord4(value) {
|
|
16911
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16912
|
+
}
|
|
16913
|
+
function asNumber4(value) {
|
|
16914
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
16915
|
+
if (typeof value === "string") {
|
|
16916
|
+
const parsed = Number.parseFloat(value.trim());
|
|
16917
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
16918
|
+
}
|
|
16919
|
+
return null;
|
|
16159
16920
|
}
|
|
16160
16921
|
|
|
16161
|
-
// src/
|
|
16922
|
+
// src/domains/quota/codex-provider.ts
|
|
16162
16923
|
init_esm_shims();
|
|
16163
|
-
import {
|
|
16924
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
16925
|
+
import { homedir as homedir4 } from "node:os";
|
|
16926
|
+
import { join as join3 } from "node:path";
|
|
16927
|
+
var CODEX_QUOTA_PROVIDER_ID = "codex";
|
|
16928
|
+
var CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
16929
|
+
var DISPLAY_NAME4 = "Codex";
|
|
16930
|
+
var REQUEST_TIMEOUT_MS4 = 15e3;
|
|
16931
|
+
var WINDOW_ORDER3 = { session: 0, weekly: 1 };
|
|
16932
|
+
var WEEKLY_THRESHOLD_SECONDS = 86400;
|
|
16933
|
+
function defaultCodexCredentialsPath() {
|
|
16934
|
+
const codexHome = process.env.CODEX_HOME;
|
|
16935
|
+
return codexHome ? join3(codexHome, "auth.json") : join3(homedir4(), ".codex", "auth.json");
|
|
16936
|
+
}
|
|
16937
|
+
function parseCodexCredentials(raw) {
|
|
16938
|
+
if (!isRecord5(raw)) return null;
|
|
16939
|
+
const tokens = raw.tokens;
|
|
16940
|
+
if (!isRecord5(tokens)) return null;
|
|
16941
|
+
const accessToken = tokens.access_token;
|
|
16942
|
+
if (typeof accessToken !== "string" || accessToken.length === 0) return null;
|
|
16943
|
+
const accountId = tokens.account_id;
|
|
16944
|
+
return {
|
|
16945
|
+
accessToken,
|
|
16946
|
+
accountId: typeof accountId === "string" ? accountId : null
|
|
16947
|
+
};
|
|
16948
|
+
}
|
|
16949
|
+
async function readCredentialsFile3(path) {
|
|
16950
|
+
let text;
|
|
16951
|
+
try {
|
|
16952
|
+
text = await readFile3(path, "utf8");
|
|
16953
|
+
} catch {
|
|
16954
|
+
return null;
|
|
16955
|
+
}
|
|
16956
|
+
try {
|
|
16957
|
+
return parseCodexCredentials(JSON.parse(text));
|
|
16958
|
+
} catch {
|
|
16959
|
+
return null;
|
|
16960
|
+
}
|
|
16961
|
+
}
|
|
16962
|
+
function createCodexQuotaProvider(options = {}) {
|
|
16963
|
+
const credentialsPath = options.credentialsPath ?? defaultCodexCredentialsPath();
|
|
16964
|
+
const readCredentials = options.readCredentials ?? (() => readCredentialsFile3(credentialsPath));
|
|
16965
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
16966
|
+
const timeoutMs = options.timeoutMs ?? REQUEST_TIMEOUT_MS4;
|
|
16967
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
16968
|
+
const snapshot = (status, message, extra = {}) => ({
|
|
16969
|
+
providerId: CODEX_QUOTA_PROVIDER_ID,
|
|
16970
|
+
displayName: DISPLAY_NAME4,
|
|
16971
|
+
status,
|
|
16972
|
+
windows: [],
|
|
16973
|
+
message,
|
|
16974
|
+
fetchedAt: now().toISOString(),
|
|
16975
|
+
...extra
|
|
16976
|
+
});
|
|
16977
|
+
return {
|
|
16978
|
+
id: CODEX_QUOTA_PROVIDER_ID,
|
|
16979
|
+
displayName: DISPLAY_NAME4,
|
|
16980
|
+
async detect() {
|
|
16981
|
+
return await readCredentials() !== null;
|
|
16982
|
+
},
|
|
16983
|
+
async fetch() {
|
|
16984
|
+
const credentials = await readCredentials();
|
|
16985
|
+
if (credentials === null) {
|
|
16986
|
+
return snapshot("no_credentials", "No Codex credentials found");
|
|
16987
|
+
}
|
|
16988
|
+
const headers = {
|
|
16989
|
+
Authorization: `Bearer ${credentials.accessToken}`,
|
|
16990
|
+
"User-Agent": "codex-cli"
|
|
16991
|
+
};
|
|
16992
|
+
if (credentials.accountId !== null) {
|
|
16993
|
+
headers["ChatGPT-Account-Id"] = credentials.accountId;
|
|
16994
|
+
}
|
|
16995
|
+
const controller = new AbortController();
|
|
16996
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
16997
|
+
let response;
|
|
16998
|
+
try {
|
|
16999
|
+
response = await doFetch(CODEX_USAGE_URL, {
|
|
17000
|
+
method: "GET",
|
|
17001
|
+
headers,
|
|
17002
|
+
signal: controller.signal
|
|
17003
|
+
});
|
|
17004
|
+
} catch (error) {
|
|
17005
|
+
return snapshot("error", describeError4(error, timeoutMs));
|
|
17006
|
+
} finally {
|
|
17007
|
+
clearTimeout(timer);
|
|
17008
|
+
}
|
|
17009
|
+
if (response.status === 401 || response.status === 403) {
|
|
17010
|
+
await discardBody3(response);
|
|
17011
|
+
return snapshot("expired", "Sign in with Codex again");
|
|
17012
|
+
}
|
|
17013
|
+
if (response.status !== 200) {
|
|
17014
|
+
await discardBody3(response);
|
|
17015
|
+
return snapshot("error", `HTTP ${response.status}`);
|
|
17016
|
+
}
|
|
17017
|
+
let body;
|
|
17018
|
+
try {
|
|
17019
|
+
body = await response.json();
|
|
17020
|
+
} catch {
|
|
17021
|
+
return snapshot("error", "Unexpected usage response");
|
|
17022
|
+
}
|
|
17023
|
+
if (!isRecord5(body)) {
|
|
17024
|
+
return snapshot("error", "Unexpected usage response");
|
|
17025
|
+
}
|
|
17026
|
+
const planType = typeof body.plan_type === "string" ? body.plan_type : null;
|
|
17027
|
+
return snapshot("ok", null, {
|
|
17028
|
+
windows: usageWindows2(body.rate_limit),
|
|
17029
|
+
credits: usageCredits2(body.credits),
|
|
17030
|
+
plan: formatPlan(planType)
|
|
17031
|
+
});
|
|
17032
|
+
}
|
|
17033
|
+
};
|
|
17034
|
+
}
|
|
17035
|
+
function usageWindows2(raw) {
|
|
17036
|
+
if (!isRecord5(raw)) return [];
|
|
17037
|
+
const windows = [];
|
|
17038
|
+
const primary = buildWindow(raw.primary_window, "session");
|
|
17039
|
+
if (primary !== null) windows.push(primary);
|
|
17040
|
+
const secondary = buildWindow(raw.secondary_window, "weekly");
|
|
17041
|
+
if (secondary !== null) windows.push(secondary);
|
|
17042
|
+
return windows.sort((left, right) => {
|
|
17043
|
+
const order = (WINDOW_ORDER3[left.key] ?? 2) - (WINDOW_ORDER3[right.key] ?? 2);
|
|
17044
|
+
return order !== 0 ? order : left.label.localeCompare(right.label);
|
|
17045
|
+
});
|
|
17046
|
+
}
|
|
17047
|
+
function buildWindow(raw, defaultKey) {
|
|
17048
|
+
if (!isRecord5(raw)) return null;
|
|
17049
|
+
const usedPct = asNumber5(raw.used_percent);
|
|
17050
|
+
if (usedPct === null) return null;
|
|
17051
|
+
const key = windowKey(raw.limit_window_seconds, defaultKey);
|
|
17052
|
+
const label = key === "weekly" ? "Weekly" : "5h";
|
|
17053
|
+
const short = key === "weekly" ? "wk" : "5h";
|
|
17054
|
+
return { key, label, short, usedPct, resetsAt: epochSecondsToIso(raw.reset_at) };
|
|
17055
|
+
}
|
|
17056
|
+
function windowKey(limitWindowSeconds, defaultKey) {
|
|
17057
|
+
const seconds = asNumber5(limitWindowSeconds);
|
|
17058
|
+
if (seconds === null) return defaultKey;
|
|
17059
|
+
return seconds >= WEEKLY_THRESHOLD_SECONDS ? "weekly" : "session";
|
|
17060
|
+
}
|
|
17061
|
+
function usageCredits2(raw) {
|
|
17062
|
+
if (!isRecord5(raw)) return null;
|
|
17063
|
+
if (raw.unlimited === true) return { display: "Unlimited", usedPct: null };
|
|
17064
|
+
if (raw.has_credits === true && typeof raw.balance === "string" && raw.balance.length > 0) {
|
|
17065
|
+
return { display: raw.balance, usedPct: null };
|
|
17066
|
+
}
|
|
17067
|
+
return null;
|
|
17068
|
+
}
|
|
17069
|
+
function isRecord5(value) {
|
|
17070
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17071
|
+
}
|
|
17072
|
+
function asNumber5(value) {
|
|
17073
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
17074
|
+
if (typeof value === "string") {
|
|
17075
|
+
const parsed = Number.parseFloat(value.trim());
|
|
17076
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
17077
|
+
}
|
|
17078
|
+
return null;
|
|
17079
|
+
}
|
|
17080
|
+
function epochSecondsToIso(value) {
|
|
17081
|
+
const seconds = asNumber5(value);
|
|
17082
|
+
if (seconds === null) return null;
|
|
17083
|
+
const date = new Date(seconds * 1e3);
|
|
17084
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
17085
|
+
}
|
|
17086
|
+
function describeError4(error, timeoutMs) {
|
|
17087
|
+
if (error instanceof Error) {
|
|
17088
|
+
return error.name === "AbortError" ? `timeout after ${timeoutMs}ms` : error.message;
|
|
17089
|
+
}
|
|
17090
|
+
return String(error);
|
|
17091
|
+
}
|
|
17092
|
+
async function discardBody3(response) {
|
|
17093
|
+
try {
|
|
17094
|
+
await response.body?.cancel();
|
|
17095
|
+
} catch {
|
|
17096
|
+
return;
|
|
17097
|
+
}
|
|
17098
|
+
}
|
|
17099
|
+
|
|
17100
|
+
// src/domains/quota/registry.ts
|
|
17101
|
+
function buildQuotaProviders() {
|
|
17102
|
+
return [
|
|
17103
|
+
createAnthropicMaxQuotaProvider(),
|
|
17104
|
+
createClaudeCodeQuotaProvider(),
|
|
17105
|
+
createCodexQuotaProvider(),
|
|
17106
|
+
createAntigravityQuotaProvider()
|
|
17107
|
+
// The copilot adapter attaches here once its credential shape is confirmed.
|
|
17108
|
+
];
|
|
17109
|
+
}
|
|
17110
|
+
|
|
17111
|
+
// src/domains/quota/service.ts
|
|
17112
|
+
function createQuotaService(options = {}) {
|
|
17113
|
+
const providers = options.providers ?? buildQuotaProviders();
|
|
17114
|
+
const cache = options.cache ?? createQuotaCache({ ttlMs: options.ttlMs ?? DEFAULT_QUOTA_CACHE_TTL_MS });
|
|
17115
|
+
const includeLocal = options.includeLocal ?? true;
|
|
17116
|
+
const withLocal = (snapshots) => {
|
|
17117
|
+
if (!includeLocal) return snapshots;
|
|
17118
|
+
const local = options.localRuntimeLabel ? localQuotaSnapshot({ runtimeLabel: options.localRuntimeLabel }) : localQuotaSnapshot();
|
|
17119
|
+
return [...snapshots, local];
|
|
17120
|
+
};
|
|
17121
|
+
return {
|
|
17122
|
+
peek() {
|
|
17123
|
+
const cached = [];
|
|
17124
|
+
for (const provider of providers) {
|
|
17125
|
+
const snapshot = cache.readLastGood(provider.id);
|
|
17126
|
+
if (snapshot !== null) cached.push(snapshot);
|
|
17127
|
+
}
|
|
17128
|
+
return withLocal(cached);
|
|
17129
|
+
},
|
|
17130
|
+
async read() {
|
|
17131
|
+
const settled = await Promise.all(
|
|
17132
|
+
providers.map(async (provider) => {
|
|
17133
|
+
try {
|
|
17134
|
+
return await cache.resolve(provider);
|
|
17135
|
+
} catch (error) {
|
|
17136
|
+
return {
|
|
17137
|
+
providerId: provider.id,
|
|
17138
|
+
displayName: provider.displayName,
|
|
17139
|
+
status: "error",
|
|
17140
|
+
windows: [],
|
|
17141
|
+
message: error instanceof Error ? error.message : String(error),
|
|
17142
|
+
fetchedAt: null
|
|
17143
|
+
};
|
|
17144
|
+
}
|
|
17145
|
+
})
|
|
17146
|
+
);
|
|
17147
|
+
return withLocal(settled.filter((snapshot) => snapshot.status !== "no_credentials"));
|
|
17148
|
+
},
|
|
17149
|
+
async detected() {
|
|
17150
|
+
const flags = await Promise.all(
|
|
17151
|
+
providers.map(async (provider) => {
|
|
17152
|
+
try {
|
|
17153
|
+
return await provider.detect();
|
|
17154
|
+
} catch {
|
|
17155
|
+
return false;
|
|
17156
|
+
}
|
|
17157
|
+
})
|
|
17158
|
+
);
|
|
17159
|
+
return providers.filter((_, index) => flags[index] === true);
|
|
17160
|
+
}
|
|
17161
|
+
};
|
|
17162
|
+
}
|
|
17163
|
+
|
|
17164
|
+
// src/domains/quota/summary-feed.ts
|
|
17165
|
+
var QUOTA_SUMMARY_TTL_MS = 5 * 6e4;
|
|
17166
|
+
function createQuotaSummaryFeed(options = {}) {
|
|
17167
|
+
const service = options.service ?? createQuotaService();
|
|
17168
|
+
const ttlMs = options.ttlMs ?? QUOTA_SUMMARY_TTL_MS;
|
|
17169
|
+
const now = options.now ?? Date.now;
|
|
17170
|
+
let line = null;
|
|
17171
|
+
let snapshots = [];
|
|
17172
|
+
let readAt = null;
|
|
17173
|
+
let inFlight = false;
|
|
17174
|
+
let disposed = false;
|
|
17175
|
+
const refresh = () => {
|
|
17176
|
+
if (inFlight || disposed) return;
|
|
17177
|
+
inFlight = true;
|
|
17178
|
+
void Promise.resolve().then(() => disposed ? snapshots : service.read()).then((nextSnapshots) => {
|
|
17179
|
+
if (disposed) return;
|
|
17180
|
+
const next = quotaSummaryLine(nextSnapshots);
|
|
17181
|
+
const changed = JSON.stringify(nextSnapshots) !== JSON.stringify(snapshots);
|
|
17182
|
+
snapshots = nextSnapshots;
|
|
17183
|
+
readAt = now();
|
|
17184
|
+
if (changed) {
|
|
17185
|
+
line = next;
|
|
17186
|
+
options.onUpdate?.();
|
|
17187
|
+
}
|
|
17188
|
+
}).catch(() => {
|
|
17189
|
+
readAt = now();
|
|
17190
|
+
}).finally(() => {
|
|
17191
|
+
inFlight = false;
|
|
17192
|
+
});
|
|
17193
|
+
};
|
|
17194
|
+
return {
|
|
17195
|
+
peek() {
|
|
17196
|
+
if (!disposed && (readAt === null || now() - readAt >= ttlMs)) refresh();
|
|
17197
|
+
return line;
|
|
17198
|
+
},
|
|
17199
|
+
peekSnapshots() {
|
|
17200
|
+
if (!disposed && (readAt === null || now() - readAt >= ttlMs)) refresh();
|
|
17201
|
+
return snapshots;
|
|
17202
|
+
},
|
|
17203
|
+
dispose() {
|
|
17204
|
+
disposed = true;
|
|
17205
|
+
}
|
|
17206
|
+
};
|
|
17207
|
+
}
|
|
16164
17208
|
|
|
16165
17209
|
// src/interactive/chat-panel.ts
|
|
16166
17210
|
init_esm_shims();
|
|
@@ -18041,8 +19085,8 @@ function footerKeyHint(now, narrow = false) {
|
|
|
18041
19085
|
})
|
|
18042
19086
|
];
|
|
18043
19087
|
const count = narrow ? 1 : 2;
|
|
18044
|
-
|
|
18045
|
-
|
|
19088
|
+
if (!entries.length) return null;
|
|
19089
|
+
const page = Math.floor(now / 12e3) % Math.ceil(entries.length / count);
|
|
18046
19090
|
const start = page * count;
|
|
18047
19091
|
return entries.slice(start, start + count).join(" \xB7 ");
|
|
18048
19092
|
}
|
|
@@ -18051,7 +19095,7 @@ function footerKeyHint(now, narrow = false) {
|
|
|
18051
19095
|
var DASHBOARD_PAGES = ["Activity", "Context", "Status"];
|
|
18052
19096
|
var ACTIVE_AGENT_STATUSES = /* @__PURE__ */ new Set(["running", "enqueued", "cancelling", "retrying", "stale"]);
|
|
18053
19097
|
var clean = (value) => sanitizeCallTargetText(redactSecretString(value));
|
|
18054
|
-
function agentCard(row2, width, compact = false) {
|
|
19098
|
+
function agentCard(row2, width, compact = false, quota = []) {
|
|
18055
19099
|
const theme2 = clioTheme();
|
|
18056
19100
|
const presentation = dispatchStatusPresentation(row2.status, { compact: false });
|
|
18057
19101
|
const name = clean(row2.agentId).replace(
|
|
@@ -18066,6 +19110,9 @@ function agentCard(row2, width, compact = false) {
|
|
|
18066
19110
|
);
|
|
18067
19111
|
const field = (label, value) => wrapTextWithAnsi(`${theme2.fg("dim", `${label} `)}${clean(value)}`, width);
|
|
18068
19112
|
lines.push(...previewRows(field("Route", `${row2.targetId}/${row2.wireModelId}`), 2, width));
|
|
19113
|
+
const weekly = routeWeeklyQuota(row2, quota);
|
|
19114
|
+
if (weekly)
|
|
19115
|
+
lines.push(...previewRows(field("Account", `Shared ${weekly.account} \xB7 ${weekly.label}`), compact ? 1 : 2, width));
|
|
18069
19116
|
if (row2.taskSummary)
|
|
18070
19117
|
lines.push(...compact ? previewRows(field("Task", row2.taskSummary), 2, width) : field("Task", row2.taskSummary));
|
|
18071
19118
|
const activity = renderDispatchActivity(row2, width);
|
|
@@ -18136,8 +19183,8 @@ function activityPage(state, width, budget) {
|
|
|
18136
19183
|
if (first && second) {
|
|
18137
19184
|
cards.push(
|
|
18138
19185
|
...zipColumns(
|
|
18139
|
-
agentCard(first, col, true),
|
|
18140
|
-
agentCard(second, width - col - 5, true),
|
|
19186
|
+
agentCard(first, col, true, state.quota),
|
|
19187
|
+
agentCard(second, width - col - 5, true, state.quota),
|
|
18141
19188
|
col,
|
|
18142
19189
|
width - col - 5,
|
|
18143
19190
|
` ${theme2.fg("frame", "\u2502")} `
|
|
@@ -18148,7 +19195,7 @@ function activityPage(state, width, budget) {
|
|
|
18148
19195
|
}
|
|
18149
19196
|
} else
|
|
18150
19197
|
for (const row2 of active) {
|
|
18151
|
-
const card = agentCard(row2, width, active.length > 1 || cardBudget < 18);
|
|
19198
|
+
const card = agentCard(row2, width, active.length > 1 || cardBudget < 18, state.quota);
|
|
18152
19199
|
if (cards.length + card.length + 2 > cardBudget && shown > 0) break;
|
|
18153
19200
|
cards.push(...card, "");
|
|
18154
19201
|
shown++;
|
|
@@ -18208,6 +19255,7 @@ function contextPage(state, width) {
|
|
|
18208
19255
|
width
|
|
18209
19256
|
)
|
|
18210
19257
|
];
|
|
19258
|
+
out.push(theme2.fg("dim", "Context occupancy is per request; subscription limits are account-wide \xB7 /usage"));
|
|
18211
19259
|
const gridWidth = Math.max(12, Math.min(64, width >= 76 ? Math.floor(width * 0.38) : width));
|
|
18212
19260
|
const gridHeight = width >= 76 ? 8 : 3;
|
|
18213
19261
|
const grid = renderContextMeterGrid(ledger, gridWidth, gridHeight, theme2);
|
|
@@ -18281,8 +19329,17 @@ function renderCompactDashboard(state, width) {
|
|
|
18281
19329
|
const usage = `${formatFooterTokens(ledger?.usedTokens ?? state.context.used ?? 0)} / ${ledger?.contextWindow ?? state.context.contextWindow ? formatFooterTokens(ledger?.contextWindow ?? state.context.contextWindow ?? 0) : "?"}`;
|
|
18282
19330
|
const context = `${ledger ? renderContextMeterBar(ledger, w >= 100 ? 14 : 8, theme2) : ""} ${usage}`;
|
|
18283
19331
|
const rightWidth = Math.min(Math.floor(w * 0.48), visibleWidth(context));
|
|
18284
|
-
const
|
|
18285
|
-
const
|
|
19332
|
+
const weekly = state.quotaRoute ? routeWeeklyQuota(state.quotaRoute, state.quota ?? []) : null;
|
|
19333
|
+
const leftRoom = Math.max(1, w - rightWidth - 3);
|
|
19334
|
+
const activityLabel = fit(activity, Math.min(visibleWidth(activity), Math.max(5, Math.floor(leftRoom / 3))));
|
|
19335
|
+
const badge = weekly && leftRoom - visibleWidth(activityLabel) - visibleWidth(weekly.label) >= 16 ? theme2.fg(
|
|
19336
|
+
weekly.severity === "critical" ? "error" : weekly.severity === "normal" ? "muted" : "warning",
|
|
19337
|
+
weekly.label
|
|
19338
|
+
) : "";
|
|
19339
|
+
const baseRoom = leftRoom - visibleWidth(activityLabel) - (badge ? visibleWidth(badge) + 3 : 0) - 5;
|
|
19340
|
+
const showThinking = baseRoom - visibleWidth(thinking) - 3 >= 12;
|
|
19341
|
+
const identityRoom = Math.max(1, baseRoom - (showThinking ? visibleWidth(thinking) + 3 : 0));
|
|
19342
|
+
const left = `${activityLabel} \xB7 ${theme2.fg("muted", fitIdentityLabel(identity, identityRoom))}${badge ? ` \xB7 ${badge}` : ""}${showThinking ? ` \xB7 ${thinking}` : ""}`;
|
|
18286
19343
|
const pair = (l, r, rw) => `${fit(l, w - rw - 3)} ${fit(r, rw)}`;
|
|
18287
19344
|
const notice = [...state.notices].filter((n) => n.expiresAt === null || n.expiresAt > state.now).sort((a, b) => b.addedAt - a.addedAt)[0];
|
|
18288
19345
|
const key = getKeybindings().getKeys("clio-coder.status.toggle").join("/") || "Dashboard";
|
|
@@ -18290,18 +19347,28 @@ function renderCompactDashboard(state, width) {
|
|
|
18290
19347
|
const foot = urgent ? theme2.fg("warning", urgent) : notice ? theme2.fg(
|
|
18291
19348
|
notice.level === "error" ? "error" : notice.level === "warning" ? "warning" : "muted",
|
|
18292
19349
|
`\u2022 ${clean(notice.text)}`
|
|
18293
|
-
) : state.demoHint ? `${theme2.fg("accent", "Tip")} ${theme2.fg("muted", clean(state.demoHint))}` : theme2.fg(
|
|
19350
|
+
) : state.demoHint ? `${theme2.fg("accent", "Tip")} ${theme2.fg("muted", clean(state.demoHint))}` : theme2.fg("dim", footerKeyHint(state.now, w < 120) ?? `${key} Dashboard`);
|
|
19351
|
+
const hintWidth = Math.min(Math.floor(w * 0.48), visibleWidth(foot));
|
|
19352
|
+
const workspaceWidth = Math.max(1, w - hintWidth - 3);
|
|
19353
|
+
const git = `${clean(state.workspace.branch ?? "no Git branch")}${state.workspace.dirty ? " *" : ""}`;
|
|
19354
|
+
const gitWidth = Math.min(visibleWidth(git), Math.max(4, Math.floor(workspaceWidth * 0.45)));
|
|
19355
|
+
const cwdWidth = Math.max(1, workspaceWidth - gitWidth - 3);
|
|
19356
|
+
const workspace = theme2.fg(
|
|
18294
19357
|
"dim",
|
|
18295
|
-
|
|
19358
|
+
`${fitIdentityLabel(clean(state.workspace.cwd), cwdWidth)} \xB7 ${fitIdentityLabel(git, gitWidth)}`
|
|
18296
19359
|
);
|
|
18297
|
-
|
|
18298
|
-
return [
|
|
18299
|
-
fit(pair(left, context, rightWidth)),
|
|
18300
|
-
fit(pair(foot, hint, Math.min(Math.floor(w * 0.4), visibleWidth(hint))))
|
|
18301
|
-
];
|
|
19360
|
+
return [fit(pair(left, context, rightWidth)), fit(pair(workspace, foot, hintWidth))];
|
|
18302
19361
|
}
|
|
18303
19362
|
function statusPage(state, width) {
|
|
18304
19363
|
const theme2 = clioTheme();
|
|
19364
|
+
const quotaRows = [
|
|
19365
|
+
theme2.style(
|
|
19366
|
+
"accent",
|
|
19367
|
+
`SESSION \xB7 ${formatFooterTokens(state.sessionTokens?.totalTokens ?? 0)} recorded tokens \xB7 ${formatCostAggregate(state.sessionCost) ?? "cost not yet priced"}`,
|
|
19368
|
+
{ bold: true }
|
|
19369
|
+
),
|
|
19370
|
+
...renderQuotaAccounts(state.quota ?? [], width, { compact: true, now: state.now })
|
|
19371
|
+
];
|
|
18305
19372
|
const active = state.dispatchRows.filter((row2) => ACTIVE_AGENT_STATUSES.has(row2.status));
|
|
18306
19373
|
const completed = state.dispatchRows.filter((row2) => row2.status === "completed").length;
|
|
18307
19374
|
const failed = state.dispatchRows.filter((row2) => ["failed", "dead", "aborted"].includes(row2.status)).length;
|
|
@@ -18331,7 +19398,6 @@ function statusPage(state, width) {
|
|
|
18331
19398
|
["Target", state.session.target ?? "not selected"],
|
|
18332
19399
|
["Tracked cost", formatCostAggregate(state.sessionCost) ?? "not yet priced"],
|
|
18333
19400
|
["Clio ceiling", state.costCeilingUsd === void 0 ? "unknown" : `$${state.costCeilingUsd} \xB7 tracked pricing only`],
|
|
18334
|
-
["Provider quota", "not reported by provider"],
|
|
18335
19401
|
["MCP connected", names(state.connections?.mcp)],
|
|
18336
19402
|
["Plugins active", names(state.connections?.plugins)],
|
|
18337
19403
|
[
|
|
@@ -18385,6 +19451,7 @@ function statusPage(state, width) {
|
|
|
18385
19451
|
];
|
|
18386
19452
|
if (width < 76)
|
|
18387
19453
|
return [
|
|
19454
|
+
...quotaRows,
|
|
18388
19455
|
...section("COST & CONNECTIONS", left, width),
|
|
18389
19456
|
...section("LOCAL MACHINE", right, width),
|
|
18390
19457
|
theme2.fg("accent", "CONTEXT ENGINE"),
|
|
@@ -18395,6 +19462,7 @@ function statusPage(state, width) {
|
|
|
18395
19462
|
];
|
|
18396
19463
|
const col = Math.floor((width - 5) / 2);
|
|
18397
19464
|
return [
|
|
19465
|
+
...quotaRows,
|
|
18398
19466
|
...zipColumns(
|
|
18399
19467
|
section("COST & CONNECTIONS", left, col),
|
|
18400
19468
|
section("LOCAL MACHINE", right, width - col - 5),
|
|
@@ -18428,7 +19496,7 @@ function renderDashboardPage(state, page, width, terminalRows, cycleKey) {
|
|
|
18428
19496
|
const hint = truncateToWidth(
|
|
18429
19497
|
theme2.fg(
|
|
18430
19498
|
"muted",
|
|
18431
|
-
`${cycleKey || "Dashboard"} \u2192 ${next} \xB7 ${page === "Status" ? "/
|
|
19499
|
+
`${cycleKey || "Dashboard"} \u2192 ${next} \xB7 ${page === "Status" ? "/usage \xB7 /mcp \xB7 /library" : "composer stays active"}`
|
|
18432
19500
|
),
|
|
18433
19501
|
safeWidth,
|
|
18434
19502
|
"\u2026",
|
|
@@ -18441,7 +19509,7 @@ function renderDashboardPage(state, page, width, terminalRows, cycleKey) {
|
|
|
18441
19509
|
else content = statusPage(state, safeWidth);
|
|
18442
19510
|
content = content.flatMap((line) => visibleWidth(line) > safeWidth ? wrapTextWithAnsi(line, safeWidth) : [line]);
|
|
18443
19511
|
if (content.length > available) {
|
|
18444
|
-
const detail = page === "Activity" ? `${getKeybindings().getKeys("clio-coder.dispatchBoard.toggle").join("/") || "Fleet Runs"} \xB7 /view` : page === "Context" ? "/context" : "/
|
|
19512
|
+
const detail = page === "Activity" ? `${getKeybindings().getKeys("clio-coder.dispatchBoard.toggle").join("/") || "Fleet Runs"} \xB7 /view` : page === "Context" ? "/context" : "/usage \xB7 /context";
|
|
18445
19513
|
content = [
|
|
18446
19514
|
...content.slice(0, available - 1),
|
|
18447
19515
|
truncateToWidth(theme2.fg("dim", `More detail: ${detail}`), safeWidth, "\u2026", true)
|
|
@@ -18453,7 +19521,7 @@ function renderDashboardPage(state, page, width, terminalRows, cycleKey) {
|
|
|
18453
19521
|
|
|
18454
19522
|
// src/interactive/footer/system-metrics.ts
|
|
18455
19523
|
init_esm_shims();
|
|
18456
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
19524
|
+
import { readdir, readFile as readFile4 } from "node:fs/promises";
|
|
18457
19525
|
import { cpus, freemem, platform, release, totalmem } from "node:os";
|
|
18458
19526
|
function counterRate(current, previous, elapsedMs) {
|
|
18459
19527
|
return elapsedMs > 0 && current >= previous && Number.isFinite(current) && Number.isFinite(previous) ? (current - previous) * 1e3 / elapsedMs : null;
|
|
@@ -18492,7 +19560,7 @@ function busiest(current, previous, elapsed) {
|
|
|
18492
19560
|
});
|
|
18493
19561
|
return rates.sort((a, b) => b.first + b.second - a.first - a.second)[0] ?? null;
|
|
18494
19562
|
}
|
|
18495
|
-
var read = (path) =>
|
|
19563
|
+
var read = (path) => readFile4(path, "utf8").catch(() => null);
|
|
18496
19564
|
var numeric = async (path) => {
|
|
18497
19565
|
const raw = await read(path);
|
|
18498
19566
|
if (raw === null || raw.trim() === "") return null;
|
|
@@ -18795,6 +19863,13 @@ function buildFooterDashboard(deps) {
|
|
|
18795
19863
|
const toolProfile = settings?.integrations.externalAgents?.defaults?.toolGovernance ?? "clio-coder-policy";
|
|
18796
19864
|
return {
|
|
18797
19865
|
resources: machine.snapshot(),
|
|
19866
|
+
quota: deps.getQuotaSnapshots?.() ?? [],
|
|
19867
|
+
...current?.runtime && wireModelId ? {
|
|
19868
|
+
quotaRoute: {
|
|
19869
|
+
runtimeId: current.runtime.id,
|
|
19870
|
+
wireModelId
|
|
19871
|
+
}
|
|
19872
|
+
} : {},
|
|
18798
19873
|
...deps.getConnections ? { connections: deps.getConnections() } : {},
|
|
18799
19874
|
...settings ? { costCeilingUsd: settings.safety.limits.sessionCostUsd } : {},
|
|
18800
19875
|
workspace: workspaceFacts(deps, branchSlot),
|
|
@@ -19005,8 +20080,8 @@ init_esm_shims();
|
|
|
19005
20080
|
init_esm_shims();
|
|
19006
20081
|
import { execFile as execFile2 } from "node:child_process";
|
|
19007
20082
|
import { open, readdir as readdir2, stat } from "node:fs/promises";
|
|
19008
|
-
import { homedir as
|
|
19009
|
-
import { basename as basename2, dirname, join, relative as relative4, resolve as resolve3, sep } from "node:path";
|
|
20083
|
+
import { homedir as homedir5 } from "node:os";
|
|
20084
|
+
import { basename as basename2, dirname, join as join4, relative as relative4, resolve as resolve3, sep } from "node:path";
|
|
19010
20085
|
var MAX_COMPLETION_ROWS = 40;
|
|
19011
20086
|
var PREVIEW_BYTES = 2048;
|
|
19012
20087
|
var PREVIEW_CHARS = 120;
|
|
@@ -19054,7 +20129,7 @@ function buildWorkspaceSnapshot(basePath, visibleFiles, trackedFiles) {
|
|
|
19054
20129
|
const tracked = trackedFiles.has(path);
|
|
19055
20130
|
files.push({
|
|
19056
20131
|
path,
|
|
19057
|
-
absolutePath:
|
|
20132
|
+
absolutePath: join4(basePath, path),
|
|
19058
20133
|
isDirectory: false,
|
|
19059
20134
|
tracked,
|
|
19060
20135
|
descendantFiles: 0,
|
|
@@ -19072,7 +20147,7 @@ function buildWorkspaceSnapshot(basePath, visibleFiles, trackedFiles) {
|
|
|
19072
20147
|
const directories = [...directoryFacts].map(
|
|
19073
20148
|
([path, facts]) => ({
|
|
19074
20149
|
path,
|
|
19075
|
-
absolutePath:
|
|
20150
|
+
absolutePath: join4(basePath, path),
|
|
19076
20151
|
isDirectory: true,
|
|
19077
20152
|
tracked: facts.tracked,
|
|
19078
20153
|
descendantFiles: facts.descendantFiles,
|
|
@@ -19129,7 +20204,7 @@ function explicitPathQuery(query) {
|
|
|
19129
20204
|
return query.startsWith("../") || query.startsWith("~/") || query.startsWith("/");
|
|
19130
20205
|
}
|
|
19131
20206
|
function resolveDisplayBase(basePath, displayBase) {
|
|
19132
|
-
if (displayBase.startsWith("~/")) return resolve3(
|
|
20207
|
+
if (displayBase.startsWith("~/")) return resolve3(homedir5(), displayBase.slice(2));
|
|
19133
20208
|
if (displayBase.startsWith("/")) return resolve3(displayBase);
|
|
19134
20209
|
return resolve3(basePath, displayBase);
|
|
19135
20210
|
}
|
|
@@ -19147,7 +20222,7 @@ async function explicitCandidates(basePath, query, trackedFiles) {
|
|
|
19147
20222
|
const candidates = [];
|
|
19148
20223
|
for (const entry of await readdir2(directory, { withFileTypes: true })) {
|
|
19149
20224
|
if (entry.name === ".git" || /[\r\n\0]/u.test(entry.name)) continue;
|
|
19150
|
-
const absolutePath =
|
|
20225
|
+
const absolutePath = join4(directory, entry.name);
|
|
19151
20226
|
let isDirectory = entry.isDirectory();
|
|
19152
20227
|
let isFile = entry.isFile();
|
|
19153
20228
|
if (!isDirectory && !isFile && entry.isSymbolicLink()) {
|
|
@@ -19799,6 +20874,12 @@ function createInteractivePresentation(deps) {
|
|
|
19799
20874
|
const key = keybindings.getKeys("tui.input.submit")[0];
|
|
19800
20875
|
return key === void 0 || key.length === 0 ? null : formatKeyLabel(key, "");
|
|
19801
20876
|
};
|
|
20877
|
+
const quotaSummary = createQuotaSummaryFeed({
|
|
20878
|
+
onUpdate: () => {
|
|
20879
|
+
footer?.refresh();
|
|
20880
|
+
requestRender();
|
|
20881
|
+
}
|
|
20882
|
+
});
|
|
19802
20883
|
const banner = factories.createBanner({
|
|
19803
20884
|
providers: deps.providers,
|
|
19804
20885
|
...deps.agents ? { getAgentCount: () => deps.agents?.listSpecs().filter(isUserVisibleAgent).length ?? 0 } : {},
|
|
@@ -19813,6 +20894,9 @@ function createInteractivePresentation(deps) {
|
|
|
19813
20894
|
return key ? formatKeyLabel(key, "") : null;
|
|
19814
20895
|
},
|
|
19815
20896
|
onFactsRefreshed: requestRender,
|
|
20897
|
+
// Subscription usage is read lazily off the render path and cached, so the
|
|
20898
|
+
// banner reads a string and never awaits a provider.
|
|
20899
|
+
getQuotaSummary: () => quotaSummary.peek(),
|
|
19816
20900
|
...deps.getSettings ? { getSettings: deps.getSettings } : {}
|
|
19817
20901
|
});
|
|
19818
20902
|
if (deps.startsResumed === true) banner.collapseToSessionHeader();
|
|
@@ -19945,6 +21029,7 @@ function createInteractivePresentation(deps) {
|
|
|
19945
21029
|
getSessionTokens: () => observabilitySnapshot.session.tokens,
|
|
19946
21030
|
getTokenThroughput: () => liveThroughput === null ? observabilitySnapshot.session.latestThroughput : currentLiveThroughput(),
|
|
19947
21031
|
getSessionCost: () => observabilitySnapshot.session.cost,
|
|
21032
|
+
getQuotaSnapshots: () => quotaSummary.peekSnapshots(),
|
|
19948
21033
|
getContextUsage: () => deps.chat.contextUsage(),
|
|
19949
21034
|
getContextLedger: () => deps.chat.contextLedger(),
|
|
19950
21035
|
getDispatchRows: () => dispatchBoardStore.rows(),
|
|
@@ -20145,6 +21230,7 @@ function createInteractivePresentation(deps) {
|
|
|
20145
21230
|
const disposeBeforeStatus = () => {
|
|
20146
21231
|
if (beforeStatusDisposed) return;
|
|
20147
21232
|
beforeStatusDisposed = true;
|
|
21233
|
+
quotaSummary.dispose();
|
|
20148
21234
|
banner.dispose();
|
|
20149
21235
|
footer.dispose();
|
|
20150
21236
|
unsubscribeObservability();
|
|
@@ -20186,6 +21272,7 @@ function createInteractivePresentation(deps) {
|
|
|
20186
21272
|
},
|
|
20187
21273
|
changeOutputStyle: (mutation) => chatRenderer.mutate(() => preserveTranscriptScroll(transcriptView, deps.terminal.columns, mutation), "output-style"),
|
|
20188
21274
|
getObservabilitySnapshot: () => observabilitySnapshot,
|
|
21275
|
+
getQuotaSnapshots: () => quotaSummary.peekSnapshots(),
|
|
20189
21276
|
recordChatEvent,
|
|
20190
21277
|
recordToolStart: (toolCallId, toolName) => {
|
|
20191
21278
|
footerActiveTools.add(toolCallId);
|
|
@@ -20223,7 +21310,7 @@ function createInteractivePresentation(deps) {
|
|
|
20223
21310
|
// src/interactive/interactive-slash-runtime.ts
|
|
20224
21311
|
init_esm_shims();
|
|
20225
21312
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
20226
|
-
import { join as
|
|
21313
|
+
import { join as join5, resolve as resolve4 } from "node:path";
|
|
20227
21314
|
|
|
20228
21315
|
// src/interactive/context-recall-command.ts
|
|
20229
21316
|
init_esm_shims();
|
|
@@ -20754,7 +21841,7 @@ function createInteractiveSlashRuntime(deps) {
|
|
|
20754
21841
|
deps.requestRender();
|
|
20755
21842
|
},
|
|
20756
21843
|
runDoctor: async ({ deep }) => {
|
|
20757
|
-
const { collectDoctorFindings, doctorNotice } = await import("./doctor-
|
|
21844
|
+
const { collectDoctorFindings, doctorNotice } = await import("./doctor-2U6YR5NH.js");
|
|
20758
21845
|
const autonomy = deps.getSettings?.().safety.autonomy;
|
|
20759
21846
|
const findings = await collectDoctorFindings({
|
|
20760
21847
|
workspaceRoot: cwd(),
|
|
@@ -20792,7 +21879,7 @@ function createInteractiveSlashRuntime(deps) {
|
|
|
20792
21879
|
};
|
|
20793
21880
|
return options.dryRun ? deps.share.planImport(resolve4(archivePath), importOptions) : deps.share.importArchive(resolve4(archivePath), importOptions);
|
|
20794
21881
|
},
|
|
20795
|
-
|
|
21882
|
+
openUsage: deps.openUsage,
|
|
20796
21883
|
openSideQuestion: deps.openSideQuestion,
|
|
20797
21884
|
// `/oracle` is refused, never queued, while a turn streams: the digest it
|
|
20798
21885
|
// packs describes the record as it stands.
|
|
@@ -20921,7 +22008,7 @@ function createInteractiveSlashRuntime(deps) {
|
|
|
20921
22008
|
const requestedPath = pathArg?.trim() ?? "";
|
|
20922
22009
|
const markdown = requestedPath.toLowerCase().endsWith(".md");
|
|
20923
22010
|
const target = resolve4(
|
|
20924
|
-
requestedPath.length > 0 ? requestedPath :
|
|
22011
|
+
requestedPath.length > 0 ? requestedPath : join5(".clio-coder", "exports", `${sessionId}-${date}.html`)
|
|
20925
22012
|
);
|
|
20926
22013
|
mkdirSync(resolve4(target, ".."), { recursive: true });
|
|
20927
22014
|
if (markdown) {
|
|
@@ -20970,7 +22057,7 @@ function createInteractiveSlashRuntime(deps) {
|
|
|
20970
22057
|
runContextRecall: (ref) => {
|
|
20971
22058
|
const session = deps.session;
|
|
20972
22059
|
if (!session) return;
|
|
20973
|
-
const
|
|
22060
|
+
const outcome2 = runOperatorRecall(ref, {
|
|
20974
22061
|
hasSession: () => session.current() !== null,
|
|
20975
22062
|
readEntries: () => {
|
|
20976
22063
|
const sessionId = deps.chat.getSessionId();
|
|
@@ -20984,12 +22071,12 @@ function createInteractiveSlashRuntime(deps) {
|
|
|
20984
22071
|
onRecalled: (payload) => deps.bus.emit(BusChannels.ContextRecalled, payload),
|
|
20985
22072
|
...deps.now ? { now: () => (deps.now?.() ?? /* @__PURE__ */ new Date()).getTime() } : {}
|
|
20986
22073
|
});
|
|
20987
|
-
if (!
|
|
20988
|
-
appendCommandNotice("error",
|
|
22074
|
+
if (!outcome2.ok) {
|
|
22075
|
+
appendCommandNotice("error", outcome2.message);
|
|
20989
22076
|
return;
|
|
20990
22077
|
}
|
|
20991
|
-
appendCommandNotice("success",
|
|
20992
|
-
deps.io.stdout(`${
|
|
22078
|
+
appendCommandNotice("success", outcome2.headline);
|
|
22079
|
+
deps.io.stdout(`${outcome2.body}
|
|
20993
22080
|
`);
|
|
20994
22081
|
deps.requestRender();
|
|
20995
22082
|
}
|
|
@@ -21188,7 +22275,11 @@ function createInteractiveTickers(deps) {
|
|
|
21188
22275
|
taskIslandHidden = hidden;
|
|
21189
22276
|
if (rows.length > 0)
|
|
21190
22277
|
taskIsland.setText(
|
|
21191
|
-
formatTaskIslandLines(
|
|
22278
|
+
formatTaskIslandLines(
|
|
22279
|
+
rows,
|
|
22280
|
+
Math.max(1, Math.min(4, Math.floor((taskIslandHeight - 8) / 10))),
|
|
22281
|
+
deps.getQuotaSnapshots?.() ?? []
|
|
22282
|
+
).join("\n")
|
|
21192
22283
|
);
|
|
21193
22284
|
else if (board) taskIsland.setText(formatTaskBoardIslandLines(board).join("\n"));
|
|
21194
22285
|
taskIsland.invalidate();
|
|
@@ -21823,7 +22914,7 @@ async function createInteractiveApplication(deps) {
|
|
|
21823
22914
|
expandSubmit: (text) => expandInteractiveSubmitAsync(text, deps.resources),
|
|
21824
22915
|
openAskUser: (questions, options) => openAskUserOverlayState(questions, options),
|
|
21825
22916
|
openSkillsHub: (request) => openSkillsHubState(request),
|
|
21826
|
-
|
|
22917
|
+
openUsage: () => openUsageOverlayState(),
|
|
21827
22918
|
openSideQuestion: (question) => openSideQuestionOverlayState(question),
|
|
21828
22919
|
startHandoff: (goal) => startHandoffState(goal),
|
|
21829
22920
|
startFleetRun: (name, vars) => startFleetRunState(name, vars),
|
|
@@ -21879,6 +22970,7 @@ async function createInteractiveApplication(deps) {
|
|
|
21879
22970
|
getTerminationCoordinator().onDrain(() => editorSubmit.shutdownEditorBash(), { timeoutMs: EDITOR_BASH_SHUTDOWN_MS });
|
|
21880
22971
|
editor.onSubmit = editorSubmit.submitEditorText;
|
|
21881
22972
|
const interactiveTickers = createInteractiveTickers({
|
|
22973
|
+
getQuotaSnapshots: presentation.getQuotaSnapshots,
|
|
21882
22974
|
tui,
|
|
21883
22975
|
dispatchBoardStore,
|
|
21884
22976
|
contextActivityStore,
|
|
@@ -21897,6 +22989,8 @@ async function createInteractiveApplication(deps) {
|
|
|
21897
22989
|
resetTranscript();
|
|
21898
22990
|
};
|
|
21899
22991
|
overlayLifecycle = createOverlayLifecycle({
|
|
22992
|
+
getQuotaSnapshots: presentation.getQuotaSnapshots,
|
|
22993
|
+
getDispatchRows: () => dispatchBoardStore.rows(),
|
|
21900
22994
|
app: deps,
|
|
21901
22995
|
tui,
|
|
21902
22996
|
footer,
|
|
@@ -21932,7 +23026,7 @@ async function createInteractiveApplication(deps) {
|
|
|
21932
23026
|
const {
|
|
21933
23027
|
closeOverlay,
|
|
21934
23028
|
openAskUserOverlayState,
|
|
21935
|
-
|
|
23029
|
+
openUsageOverlayState,
|
|
21936
23030
|
openSideQuestionOverlayState,
|
|
21937
23031
|
startHandoffState,
|
|
21938
23032
|
startFleetRunState,
|
|
@@ -21999,11 +23093,11 @@ async function createInteractiveApplication(deps) {
|
|
|
21999
23093
|
);
|
|
22000
23094
|
};
|
|
22001
23095
|
const backgroundActiveDispatch = () => {
|
|
22002
|
-
const
|
|
23096
|
+
const outcome2 = deps.onBackgroundDispatch?.() ?? {
|
|
22003
23097
|
ok: false,
|
|
22004
23098
|
message: "background: dispatch backgrounding is unavailable in this session"
|
|
22005
23099
|
};
|
|
22006
|
-
notify(
|
|
23100
|
+
notify(outcome2.ok ? "success" : "info", outcome2.message, "dispatch:background");
|
|
22007
23101
|
};
|
|
22008
23102
|
const mux = deps.mux;
|
|
22009
23103
|
const muxBridge = mux && mux.mode !== "none" && deps.createMuxBridge ? deps.createMuxBridge({
|
|
@@ -22285,7 +23379,7 @@ async function createInteractiveApplication(deps) {
|
|
|
22285
23379
|
});
|
|
22286
23380
|
const settings = deps.getSettings?.();
|
|
22287
23381
|
if (settings)
|
|
22288
|
-
void import("./startup-background-
|
|
23382
|
+
void import("./startup-background-PJMWTKVO.js").then(
|
|
22289
23383
|
({ prepareWorkerTargets }) => prepareWorkerTargets({
|
|
22290
23384
|
settings,
|
|
22291
23385
|
providers: deps.providers,
|
|
@@ -22378,4 +23472,4 @@ export {
|
|
|
22378
23472
|
routeOverlayKey,
|
|
22379
23473
|
startInteractive
|
|
22380
23474
|
};
|
|
22381
|
-
//# sourceMappingURL=interactive-
|
|
23475
|
+
//# sourceMappingURL=interactive-JCVBPVYT.js.map
|