zidane 6.6.1 → 6.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-EFzMJPxa.js → acp-BQqhozmt.js} +2 -2
- package/dist/{acp-EFzMJPxa.js.map → acp-BQqhozmt.js.map} +1 -1
- package/dist/acp-cli.js +3 -3
- package/dist/acp.d.ts +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-ut6nQRnI.js → agent-DTxH7RHW.js} +40 -7
- package/dist/agent-DTxH7RHW.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/{auth-BaLBiz0r.js → auth-7rv6ZfWk.js} +2 -2
- package/dist/{auth-BaLBiz0r.js.map → auth-7rv6ZfWk.js.map} +1 -1
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat/pure.js +1 -1
- package/dist/chat.d.ts +88 -5
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +5 -5
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/{index-BGiCne_W.d.ts → index-D0O-QO7Z.d.ts} +60 -1
- package/dist/index-D0O-QO7Z.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/restate.d.ts +1 -1
- package/dist/restate.js +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/skills.d.ts +1 -1
- package/dist/{tool-formatters-5zq0YCK0.d.ts → tool-formatters-NrzD6Fkk.d.ts} +20 -3
- package/dist/{tool-formatters-5zq0YCK0.d.ts.map → tool-formatters-NrzD6Fkk.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-BfV8T2us.js → transcript-anchors-BjH-68k2.js} +654 -35
- package/dist/transcript-anchors-BjH-68k2.js.map +1 -0
- package/dist/{transcript-anchors-BaAzmW1d.d.ts → transcript-anchors-UhC58Tmj.d.ts} +3 -3
- package/dist/transcript-anchors-UhC58Tmj.d.ts.map +1 -0
- package/dist/tui.d.ts +33 -5
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +859 -191
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-DXiTV38z.d.ts → turn-operations-Cuc9pwiB.d.ts} +3 -3
- package/dist/{turn-operations-DXiTV38z.d.ts.map → turn-operations-Cuc9pwiB.d.ts.map} +1 -1
- package/dist/{turn-operations-7_Ff7INF.js → turn-operations-mMxDRgLY.js} +65 -3
- package/dist/{turn-operations-7_Ff7INF.js.map → turn-operations-mMxDRgLY.js.map} +1 -1
- package/dist/types.d.ts +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/CHAT.md +30 -5
- package/docs/TUI.md +22 -2
- package/package.json +1 -1
- package/dist/agent-ut6nQRnI.js.map +0 -1
- package/dist/index-BGiCne_W.d.ts.map +0 -1
- package/dist/transcript-anchors-BaAzmW1d.d.ts.map +0 -1
- package/dist/transcript-anchors-BfV8T2us.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __require } from "./rolldown-runtime-BhDjJH2R.js";
|
|
2
2
|
import { l as toolResultToText } from "./types-BIarq1qE.js";
|
|
3
|
-
import { Ct as waitTask, Ft as readFile$1, Gt as edit, Lt as multiEdit, Nt as shell, Tt as createSpawnTool, Ut as grep, Vt as listFiles, Wt as glob, Y as EMPTY_EXTENSION_REGISTRY, _t as composePresets, an as readKeybindingsRaw, b as migrateSessionArtifactDirs, jt as shellKill, nn as mergeKeybindings, on as resolveExtensionKeybindings, st as buildExtensionPreset, vt as definePreset, xt as writeFile$1 } from "./agent-
|
|
3
|
+
import { Ct as waitTask, Ft as readFile$1, Gt as edit, Lt as multiEdit, Nt as shell, Tt as createSpawnTool, Ut as grep, Vt as listFiles, Wt as glob, Y as EMPTY_EXTENSION_REGISTRY, _t as composePresets, an as readKeybindingsRaw, b as migrateSessionArtifactDirs, jt as shellKill, nn as mergeKeybindings, on as resolveExtensionKeybindings, st as buildExtensionPreset, vt as definePreset, xt as writeFile$1 } from "./agent-DTxH7RHW.js";
|
|
4
4
|
import { a as formatDuration, c as previewLine, i as fmtTokens, l as shortId } from "./format-BF745pLL.js";
|
|
5
5
|
import { u as errorMessage } from "./errors-DKiMrOKq.js";
|
|
6
6
|
import { R as projectUserPaths, x as discoverSkills, z as findGitRoot$1 } from "./skills-BMGH_KK1.js";
|
|
@@ -12,8 +12,8 @@ import { r as formatTokenUsage } from "./stats-DAKBEKjc.js";
|
|
|
12
12
|
import { i as findActiveTriggers, n as collectReferences, o as mergeSuggestions, t as applyInsert } from "./completion-core-BRo86R_L.js";
|
|
13
13
|
import { r as fetchUrl } from "./fetch-url-ma-5_p0p.js";
|
|
14
14
|
import { webSearch } from "./tools/web-search.js";
|
|
15
|
-
import { T as restoreModelOptions, b as modelSupportsReasoning, d as BUILTIN_PROVIDERS, n as applyApiKeyEnv, r as credentialsPath, t as detectAuth, x as modelsForDescriptor } from "./auth-
|
|
16
|
-
import {
|
|
15
|
+
import { T as restoreModelOptions, b as modelSupportsReasoning, d as BUILTIN_PROVIDERS, i as readCredentials, m as credKeyOf, n as applyApiKeyEnv, r as credentialsPath, t as detectAuth, x as modelsForDescriptor } from "./auth-7rv6ZfWk.js";
|
|
16
|
+
import { at as extractEditPayload, bt as chatReferenceTokensFromText, m as ownerOf, mt as parseEditOutcomesFromResult, u as capTranscriptEvents, w as chunkMarkdownForRender } from "./turn-operations-mMxDRgLY.js";
|
|
17
17
|
import { basename, dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
18
18
|
import { Buffer } from "node:buffer";
|
|
19
19
|
import { chmodSync, closeSync, cpSync, createReadStream, createWriteStream, existsSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -189,6 +189,64 @@ function base64DecodedSize(data) {
|
|
|
189
189
|
const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
|
|
190
190
|
return Math.floor(data.length * 3 / 4) - padding;
|
|
191
191
|
}
|
|
192
|
+
/** Severity values a persisted notice may carry. Anything else is ignored. */
|
|
193
|
+
const NOTICE_SEVERITIES = /* @__PURE__ */ new Set([
|
|
194
|
+
"info",
|
|
195
|
+
"warn",
|
|
196
|
+
"error"
|
|
197
|
+
]);
|
|
198
|
+
/**
|
|
199
|
+
* Read a persisted `metadata.command.notice` back into the event shape, or
|
|
200
|
+
* `null` when the block is an ordinary command detail. Defensive because the
|
|
201
|
+
* value round-tripped through JSON written by a possibly older build.
|
|
202
|
+
*/
|
|
203
|
+
function readNoticeMeta(value) {
|
|
204
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
205
|
+
const { severity, label, childId, depth } = value;
|
|
206
|
+
if (typeof severity !== "string" || !NOTICE_SEVERITIES.has(severity)) return null;
|
|
207
|
+
if (typeof label !== "string" || label === "") return null;
|
|
208
|
+
return {
|
|
209
|
+
notice: {
|
|
210
|
+
severity,
|
|
211
|
+
label
|
|
212
|
+
},
|
|
213
|
+
...typeof childId === "string" && childId !== "" ? { childId } : {},
|
|
214
|
+
...typeof depth === "number" && Number.isFinite(depth) && depth >= 0 ? { depth: Math.floor(depth) } : {}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/** The notice a command block carries, or `null` for an ordinary detail block. */
|
|
218
|
+
function noticeFromBlock(block) {
|
|
219
|
+
if (block.type !== "tool_result") return null;
|
|
220
|
+
const meta = block.metadata?.command;
|
|
221
|
+
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return null;
|
|
222
|
+
return readNoticeMeta(meta.notice);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Persisted form of a harness notice: a display-only `role: 'command'` turn,
|
|
226
|
+
* which is the existing carrier for content that must survive a reload without
|
|
227
|
+
* ever reaching the model.
|
|
228
|
+
*
|
|
229
|
+
* Pairs with the `readNoticeMeta` branch in {@link eventsFromTurns}. Callers
|
|
230
|
+
* pre-mint `turnId` and emit the live event with the same id so the live row
|
|
231
|
+
* and the reloaded row are the same row.
|
|
232
|
+
*
|
|
233
|
+
* `origin` carries the live event's `childId` / `depth`. A subagent notice is
|
|
234
|
+
* persisted by the PARENT's depth-0 run, so without it the run-derived tag in
|
|
235
|
+
* `eventsFromTurns` would reload the row un-indented and unattributed.
|
|
236
|
+
*/
|
|
237
|
+
function noticeTurnContent(notice, text, origin = {}) {
|
|
238
|
+
return [{
|
|
239
|
+
type: "tool_result",
|
|
240
|
+
callId: `notice_${crypto.randomUUID()}`,
|
|
241
|
+
output: text,
|
|
242
|
+
metadata: { command: { notice: {
|
|
243
|
+
severity: notice.severity,
|
|
244
|
+
label: notice.label,
|
|
245
|
+
...origin.childId ? { childId: origin.childId } : {},
|
|
246
|
+
...origin.depth !== void 0 ? { depth: origin.depth } : {}
|
|
247
|
+
} } }
|
|
248
|
+
}];
|
|
249
|
+
}
|
|
192
250
|
function eventsFromTurns(turns, runs = []) {
|
|
193
251
|
const runById = /* @__PURE__ */ new Map();
|
|
194
252
|
for (const run of runs) if (!runById.has(run.id)) runById.set(run.id, run);
|
|
@@ -426,7 +484,8 @@ function eventsFromTurns(turns, runs = []) {
|
|
|
426
484
|
continue;
|
|
427
485
|
}
|
|
428
486
|
if (turn.role === "command") {
|
|
429
|
-
|
|
487
|
+
const noticeOnly = turn.content.length > 0 && turn.content.every((block) => noticeFromBlock(block) !== null);
|
|
488
|
+
if (lastDepthAtEmission === 0 && !noticeOnly) events.push({
|
|
430
489
|
kind: "separator",
|
|
431
490
|
text: ""
|
|
432
491
|
});
|
|
@@ -439,6 +498,18 @@ function eventsFromTurns(turns, runs = []) {
|
|
|
439
498
|
else if (block.type === "tool_result") {
|
|
440
499
|
const meta = block.metadata?.command;
|
|
441
500
|
const commandMeta = meta && typeof meta === "object" && !Array.isArray(meta) ? meta : void 0;
|
|
501
|
+
const persistedNotice = readNoticeMeta(commandMeta?.notice);
|
|
502
|
+
if (persistedNotice) {
|
|
503
|
+
events.push({
|
|
504
|
+
kind: persistedNotice.notice.severity === "error" ? "error" : "info",
|
|
505
|
+
text: toolResultText(block.output),
|
|
506
|
+
notice: persistedNotice.notice,
|
|
507
|
+
...tag,
|
|
508
|
+
...persistedNotice.childId ? { childId: persistedNotice.childId } : {},
|
|
509
|
+
...persistedNotice.depth !== void 0 ? { depth: persistedNotice.depth } : {}
|
|
510
|
+
});
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
442
513
|
const customMeta = commandMeta?.custom;
|
|
443
514
|
if (customMeta && typeof customMeta === "object" && !Array.isArray(customMeta) && typeof customMeta.renderer === "string") {
|
|
444
515
|
const bound = customMeta;
|
|
@@ -733,6 +804,7 @@ function marginTopFor(event, previous) {
|
|
|
733
804
|
if (TOOL_KINDS.has(event.kind) && previous && TOOL_KINDS.has(previous.kind)) return 0;
|
|
734
805
|
if (event.kind === "spawn-start" && previous && (TOOL_KINDS.has(previous.kind) || previous.kind === "spawn-start" || previous.kind === "spawn-end")) return 0;
|
|
735
806
|
if (event.kind === "task-notification" && previous?.kind === "task-notification") return 0;
|
|
807
|
+
if (event.notice && previous?.notice) return 0;
|
|
736
808
|
if (event.kind === "thinking" && previous?.kind === "thinking" && previous.childId === event.childId) return 0;
|
|
737
809
|
return MARGIN_TOP[event.kind] ?? 0;
|
|
738
810
|
}
|
|
@@ -2074,7 +2146,10 @@ const SHARED_BEHAVIOR = {
|
|
|
2074
2146
|
grep: discoveryDedupHash,
|
|
2075
2147
|
list_files: discoveryDedupHash
|
|
2076
2148
|
},
|
|
2077
|
-
repeatGuard: {
|
|
2149
|
+
repeatGuard: {
|
|
2150
|
+
mode: "both",
|
|
2151
|
+
windowSize: 40
|
|
2152
|
+
},
|
|
2078
2153
|
thinkingDecay: {
|
|
2079
2154
|
afterTurn: 10,
|
|
2080
2155
|
floor: 1024
|
|
@@ -2524,13 +2599,13 @@ function chatAutoCompactBehavior(opts) {
|
|
|
2524
2599
|
}
|
|
2525
2600
|
//#endregion
|
|
2526
2601
|
//#region src/chat/auto-update.ts
|
|
2527
|
-
const OPT_OUT_VARS = [
|
|
2602
|
+
const OPT_OUT_VARS$1 = [
|
|
2528
2603
|
"ZIDANE_NO_UPDATE",
|
|
2529
2604
|
"NO_UPDATE_NOTIFIER",
|
|
2530
2605
|
"CI"
|
|
2531
2606
|
];
|
|
2532
2607
|
function isOptedOut(env = process.env) {
|
|
2533
|
-
for (const key of OPT_OUT_VARS) {
|
|
2608
|
+
for (const key of OPT_OUT_VARS$1) {
|
|
2534
2609
|
const v = env[key];
|
|
2535
2610
|
if (v !== void 0 && v !== "" && v !== "0" && v.toLowerCase() !== "false") return true;
|
|
2536
2611
|
}
|
|
@@ -6572,8 +6647,10 @@ const DEFAULT_SETTINGS = {
|
|
|
6572
6647
|
smoothStreaming: true,
|
|
6573
6648
|
fileLogging: false,
|
|
6574
6649
|
showTodoIndicator: true,
|
|
6650
|
+
showUsageIndicator: true,
|
|
6575
6651
|
showThrobber: false,
|
|
6576
6652
|
checkForUpdates: true,
|
|
6653
|
+
subscriptionUsage: true,
|
|
6577
6654
|
uiMode: "full",
|
|
6578
6655
|
userInstructionsScope: "both",
|
|
6579
6656
|
ctrlCBehavior: "quit",
|
|
@@ -6651,7 +6728,7 @@ function useSettings() {
|
|
|
6651
6728
|
const SETTINGS_CATEGORIES = [{
|
|
6652
6729
|
id: "agent",
|
|
6653
6730
|
label: "Agent",
|
|
6654
|
-
description: "
|
|
6731
|
+
description: "behavior, capabilities, persistence, auth"
|
|
6655
6732
|
}, {
|
|
6656
6733
|
id: "ui",
|
|
6657
6734
|
label: "UI",
|
|
@@ -6680,37 +6757,37 @@ const SETTINGS_TOGGLES = [
|
|
|
6680
6757
|
category: "agent",
|
|
6681
6758
|
key: "loadProjectExtensions",
|
|
6682
6759
|
label: "Load project extensions",
|
|
6683
|
-
description: "run extensions
|
|
6760
|
+
description: "run extensions from <project>/.zidane/extensions (off by default, they execute arbitrary local code)"
|
|
6684
6761
|
},
|
|
6685
6762
|
{
|
|
6686
6763
|
category: "agent",
|
|
6687
6764
|
key: "importForeignConfigs",
|
|
6688
6765
|
label: "Import configurations from other agents",
|
|
6689
|
-
description: "discover MCP servers
|
|
6766
|
+
description: "discover MCP servers and skills from Claude Code, Codex and Pi configs (imported stdio servers run commands from files zidane does not own)"
|
|
6690
6767
|
},
|
|
6691
6768
|
{
|
|
6692
6769
|
category: "agent",
|
|
6693
6770
|
key: "importRulerConfigs",
|
|
6694
6771
|
label: "Support Ruler configuration (.ruler)",
|
|
6695
|
-
description: "discover MCP servers, skills
|
|
6772
|
+
description: "discover MCP servers, skills and instructions from .ruler/ and ~/.config/ruler (read-only, zidane's own configs win collisions; stdio servers also need \"Import configurations from other agents\")"
|
|
6696
6773
|
},
|
|
6697
6774
|
{
|
|
6698
6775
|
category: "agent",
|
|
6699
6776
|
key: "attachmentsEnabled",
|
|
6700
6777
|
label: "Paste attachments",
|
|
6701
|
-
description: "
|
|
6778
|
+
description: "turn dragged files and multiline pastes into attachments (off = everything pastes inline)"
|
|
6702
6779
|
},
|
|
6703
6780
|
{
|
|
6704
6781
|
category: "agent",
|
|
6705
6782
|
key: "externalEditor",
|
|
6706
6783
|
label: "Edit prompt in $EDITOR",
|
|
6707
|
-
description: "
|
|
6784
|
+
description: "edit the current prompt in $EDITOR / $VISUAL with ctrl+g, reloaded when you save and quit"
|
|
6708
6785
|
},
|
|
6709
6786
|
{
|
|
6710
6787
|
category: "agent",
|
|
6711
6788
|
key: "inlineAutocomplete",
|
|
6712
6789
|
label: "Inline autocomplete",
|
|
6713
|
-
description: "ghost-text suggestions in the prompt (file paths
|
|
6790
|
+
description: "ghost-text suggestions in the prompt, tab accepts (file paths and extension providers)"
|
|
6714
6791
|
},
|
|
6715
6792
|
{
|
|
6716
6793
|
category: "agent",
|
|
@@ -6722,19 +6799,25 @@ const SETTINGS_TOGGLES = [
|
|
|
6722
6799
|
category: "agent",
|
|
6723
6800
|
key: "subagentModels",
|
|
6724
6801
|
label: "Allow subagents to use different models",
|
|
6725
|
-
description: "
|
|
6802
|
+
description: "let the agent delegate to cheaper models with `model_search` (off = children inherit your model)"
|
|
6726
6803
|
},
|
|
6727
6804
|
{
|
|
6728
6805
|
category: "agent",
|
|
6729
6806
|
key: "resumeOnMaxTokens",
|
|
6730
6807
|
label: "Auto-resume on output limit",
|
|
6731
|
-
description: "when a turn hits the model
|
|
6808
|
+
description: "when a turn hits the model's output-token ceiling mid-answer, continue instead of stopping (off = stop at the limit)"
|
|
6732
6809
|
},
|
|
6733
6810
|
{
|
|
6734
6811
|
category: "agent",
|
|
6735
6812
|
key: "checkForUpdates",
|
|
6736
6813
|
label: "Check for updates",
|
|
6737
|
-
description: "
|
|
6814
|
+
description: "check npm once a day and flag new releases in the footer (zidane upgrade works either way)"
|
|
6815
|
+
},
|
|
6816
|
+
{
|
|
6817
|
+
category: "agent",
|
|
6818
|
+
key: "subscriptionUsage",
|
|
6819
|
+
label: "Read subscription usage",
|
|
6820
|
+
description: "ask signed-in providers for rate-limit windows and credits (off = no automatic requests, ctrl+r in the panel still fetches once)"
|
|
6738
6821
|
},
|
|
6739
6822
|
{
|
|
6740
6823
|
category: "ui",
|
|
@@ -6764,13 +6847,13 @@ const SETTINGS_TOGGLES = [
|
|
|
6764
6847
|
category: "ui",
|
|
6765
6848
|
key: "groupToolCalls",
|
|
6766
6849
|
label: "Group tool calls",
|
|
6767
|
-
description: "collapse each exchange's tool calls into one row
|
|
6850
|
+
description: "collapse each exchange's tool calls into one row, click to expand"
|
|
6768
6851
|
},
|
|
6769
6852
|
{
|
|
6770
6853
|
category: "ui",
|
|
6771
6854
|
key: "showFullShellOutput",
|
|
6772
6855
|
label: "Full shell output",
|
|
6773
|
-
description: "show every line of ! shell output instead of the last 10 (display only
|
|
6856
|
+
description: "show every line of ! shell output instead of the last 10 (display only, the model still gets the trimmed result)"
|
|
6774
6857
|
},
|
|
6775
6858
|
{
|
|
6776
6859
|
category: "ui",
|
|
@@ -6788,7 +6871,7 @@ const SETTINGS_TOGGLES = [
|
|
|
6788
6871
|
category: "ui",
|
|
6789
6872
|
key: "smoothStreaming",
|
|
6790
6873
|
label: "Smooth streaming",
|
|
6791
|
-
description: "
|
|
6874
|
+
description: "reveal streamed text character by character (typewriter effect)"
|
|
6792
6875
|
},
|
|
6793
6876
|
{
|
|
6794
6877
|
category: "ui",
|
|
@@ -6796,11 +6879,17 @@ const SETTINGS_TOGGLES = [
|
|
|
6796
6879
|
label: "Todo indicator",
|
|
6797
6880
|
description: "show the `n/total todos` chip in the footer (ctrl+t opens the list regardless)"
|
|
6798
6881
|
},
|
|
6882
|
+
{
|
|
6883
|
+
category: "ui",
|
|
6884
|
+
key: "showUsageIndicator",
|
|
6885
|
+
label: "Usage indicator",
|
|
6886
|
+
description: "show the subscription gauge (▰ 5h 43%) in the footer (ctrl+p opens the panel regardless)"
|
|
6887
|
+
},
|
|
6799
6888
|
{
|
|
6800
6889
|
category: "ui",
|
|
6801
6890
|
key: "showThrobber",
|
|
6802
6891
|
label: "Streaming throbber",
|
|
6803
|
-
description: "animated
|
|
6892
|
+
description: "animated glyphs under the last message while the agent works"
|
|
6804
6893
|
},
|
|
6805
6894
|
{
|
|
6806
6895
|
category: "ui",
|
|
@@ -6812,7 +6901,7 @@ const SETTINGS_TOGGLES = [
|
|
|
6812
6901
|
category: "ui",
|
|
6813
6902
|
key: "mouseInteractions",
|
|
6814
6903
|
label: "Clickable UI",
|
|
6815
|
-
description: "mouse clicks, hovers
|
|
6904
|
+
description: "mouse clicks, hovers and wheel scrolling on hints and rows (off = terminal-native text selection and scrollback)"
|
|
6816
6905
|
}
|
|
6817
6906
|
];
|
|
6818
6907
|
const SETTINGS_CHOICES = [
|
|
@@ -6820,7 +6909,7 @@ const SETTINGS_CHOICES = [
|
|
|
6820
6909
|
category: "agent",
|
|
6821
6910
|
key: "interactionMode",
|
|
6822
6911
|
label: "Interaction mode",
|
|
6823
|
-
description: "ask normally,
|
|
6912
|
+
description: "ask normally, answer automatically from the repository, or turn the interaction tools off",
|
|
6824
6913
|
options: [
|
|
6825
6914
|
{
|
|
6826
6915
|
value: "ask",
|
|
@@ -6916,7 +7005,7 @@ const SETTINGS_CHOICES = [
|
|
|
6916
7005
|
category: "agent",
|
|
6917
7006
|
key: "retryMaxAttempts",
|
|
6918
7007
|
label: "Provider retry attempts",
|
|
6919
|
-
description: "max attempts per provider call
|
|
7008
|
+
description: "max attempts per provider call, for retryable errors and silent stream stalls (applies to the session and its subagents)",
|
|
6920
7009
|
options: [
|
|
6921
7010
|
{
|
|
6922
7011
|
value: 0,
|
|
@@ -7060,7 +7149,7 @@ const SETTINGS_CHOICES = [
|
|
|
7060
7149
|
category: "ui",
|
|
7061
7150
|
key: "toolCallDisplay",
|
|
7062
7151
|
label: "Tool calls display",
|
|
7063
|
-
description: "how `↳ <tool>` lines render
|
|
7152
|
+
description: "how `↳ <tool>` lines render: hidden, per-tool summary, or full JSON",
|
|
7064
7153
|
options: [
|
|
7065
7154
|
{
|
|
7066
7155
|
value: "formatted",
|
|
@@ -7080,7 +7169,7 @@ const SETTINGS_CHOICES = [
|
|
|
7080
7169
|
category: "ui",
|
|
7081
7170
|
key: "editDiffDisplay",
|
|
7082
7171
|
label: "Edit diff density",
|
|
7083
|
-
description: "full unified diff with line numbers
|
|
7172
|
+
description: "full unified diff with line numbers, or a compact per-hunk summary",
|
|
7084
7173
|
options: [{
|
|
7085
7174
|
value: "full",
|
|
7086
7175
|
label: "Full"
|
|
@@ -7103,7 +7192,7 @@ const SETTINGS_CHOICES = [
|
|
|
7103
7192
|
category: "ui",
|
|
7104
7193
|
key: "uiMode",
|
|
7105
7194
|
label: "UI mode",
|
|
7106
|
-
description: "chat
|
|
7195
|
+
description: "chat screen density: full shows every shortcut, minimal hides them (ctrl+y to peek)",
|
|
7107
7196
|
options: [{
|
|
7108
7197
|
value: "full",
|
|
7109
7198
|
label: "Full"
|
|
@@ -7116,7 +7205,7 @@ const SETTINGS_CHOICES = [
|
|
|
7116
7205
|
category: "ui",
|
|
7117
7206
|
key: "targetFps",
|
|
7118
7207
|
label: "Renderer FPS",
|
|
7119
|
-
description: "30 saves CPU
|
|
7208
|
+
description: "30 saves CPU, 60 recommended, 120 for ProMotion and modern terminals",
|
|
7120
7209
|
options: [
|
|
7121
7210
|
{
|
|
7122
7211
|
value: 30,
|
|
@@ -7136,7 +7225,7 @@ const SETTINGS_CHOICES = [
|
|
|
7136
7225
|
category: "ui",
|
|
7137
7226
|
key: "ctrlCBehavior",
|
|
7138
7227
|
label: "Ctrl+C behavior",
|
|
7139
|
-
description: "quit immediately
|
|
7228
|
+
description: "quit immediately, confirm first, or stop the current action and quit on a second press",
|
|
7140
7229
|
options: [
|
|
7141
7230
|
{
|
|
7142
7231
|
value: "quit",
|
|
@@ -7156,7 +7245,7 @@ const SETTINGS_CHOICES = [
|
|
|
7156
7245
|
category: "ui",
|
|
7157
7246
|
key: "terminalTitle",
|
|
7158
7247
|
label: "Terminal title",
|
|
7159
|
-
description: "what the window
|
|
7248
|
+
description: "what the window or tab title shows: off leaves it alone, zidane pins the app name, session tracks the session title, icon + session adds your icon",
|
|
7160
7249
|
options: [
|
|
7161
7250
|
{
|
|
7162
7251
|
value: "off",
|
|
@@ -7182,21 +7271,21 @@ const SETTINGS_INPUTS = [
|
|
|
7182
7271
|
category: "ui",
|
|
7183
7272
|
key: "sessionIcon",
|
|
7184
7273
|
label: "Session icon",
|
|
7185
|
-
description: "
|
|
7274
|
+
description: "shown before the session title when \"Terminal title\" is set to icon + session (empty = ⚽)",
|
|
7186
7275
|
placeholder: "⚽"
|
|
7187
7276
|
},
|
|
7188
7277
|
{
|
|
7189
7278
|
category: "ui",
|
|
7190
7279
|
key: "attentionIcon",
|
|
7191
7280
|
label: "Attention icon",
|
|
7192
|
-
description: "replaces the session icon while zidane waits on
|
|
7281
|
+
description: "replaces the session icon while zidane waits on you, like tool approvals (empty = ⚠️)",
|
|
7193
7282
|
placeholder: "⚠️"
|
|
7194
7283
|
},
|
|
7195
7284
|
{
|
|
7196
7285
|
category: "ui",
|
|
7197
7286
|
key: "activeIcon",
|
|
7198
7287
|
label: "Active icon",
|
|
7199
|
-
description: "replaces the session icon while zidane is running
|
|
7288
|
+
description: "replaces the session icon while zidane is running (empty = ⚡), attention wins when input is needed",
|
|
7200
7289
|
placeholder: "⚡"
|
|
7201
7290
|
}
|
|
7202
7291
|
];
|
|
@@ -9025,6 +9114,536 @@ function formatPathForCwd(projectRelativePath, projectRoot, cwd) {
|
|
|
9025
9114
|
return sep === "/" ? rel : rel.split(sep).join(posix.sep);
|
|
9026
9115
|
}
|
|
9027
9116
|
//#endregion
|
|
9117
|
+
//#region src/chat/provider-usage.ts
|
|
9118
|
+
/** Per-request timeout. Generous, since these endpoints are occasionally slow. */
|
|
9119
|
+
const FETCH_TIMEOUT_MS = 15e3;
|
|
9120
|
+
/** How long a snapshot stays fresh. Usage windows move in minutes, not seconds. */
|
|
9121
|
+
const USAGE_CACHE_TTL_MS = 6e4;
|
|
9122
|
+
/**
|
|
9123
|
+
* Credential-file key to probe. Keyed by credential key rather than provider
|
|
9124
|
+
* key so a custom descriptor reusing a builtin credential slot
|
|
9125
|
+
* (`credentialFileKey: 'anthropic'`) gets usage for free.
|
|
9126
|
+
*/
|
|
9127
|
+
const USAGE_SOURCE_BY_CRED_KEY = {
|
|
9128
|
+
"anthropic": "claude",
|
|
9129
|
+
"openai-codex": "codex",
|
|
9130
|
+
"xai-oauth": "grok",
|
|
9131
|
+
"openrouter": "openrouter"
|
|
9132
|
+
};
|
|
9133
|
+
/** Credential kind each probe authenticates with. */
|
|
9134
|
+
const SOURCE_CRED_KIND = {
|
|
9135
|
+
claude: "oauth",
|
|
9136
|
+
codex: "oauth",
|
|
9137
|
+
grok: "oauth",
|
|
9138
|
+
openrouter: "apikey"
|
|
9139
|
+
};
|
|
9140
|
+
/** The probe for a descriptor, or `null` when it has no usage surface. */
|
|
9141
|
+
function usageSourceFor(descriptor) {
|
|
9142
|
+
return USAGE_SOURCE_BY_CRED_KEY[credKeyOf(descriptor)] ?? null;
|
|
9143
|
+
}
|
|
9144
|
+
/** Whether this provider can report subscription usage at all. */
|
|
9145
|
+
function providerUsageSupported(descriptor) {
|
|
9146
|
+
return usageSourceFor(descriptor) !== null;
|
|
9147
|
+
}
|
|
9148
|
+
/**
|
|
9149
|
+
* The window closest to its ceiling, which is the one that will bite first and
|
|
9150
|
+
* the only one a single-line indicator has room for. `null` when the snapshot
|
|
9151
|
+
* carries no windows.
|
|
9152
|
+
*/
|
|
9153
|
+
function mostConstrainedWindow(snapshot) {
|
|
9154
|
+
let worst = null;
|
|
9155
|
+
for (const win of snapshot.windows) if (!worst || win.usedPercent > worst.usedPercent) worst = win;
|
|
9156
|
+
return worst;
|
|
9157
|
+
}
|
|
9158
|
+
/** Keyed by credential key: two descriptors sharing tokens share a snapshot. */
|
|
9159
|
+
const cache = /* @__PURE__ */ new Map();
|
|
9160
|
+
/** In-flight probes, so a footer read and a panel open share one request. */
|
|
9161
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
9162
|
+
/**
|
|
9163
|
+
* Newest probe issued per credential key, as a monotonic ticket. `refresh`
|
|
9164
|
+
* skips both maps above, so two probes for one key can be outstanding at once
|
|
9165
|
+
* (panel refresh while the footer's post-run refresh is still in flight); only
|
|
9166
|
+
* the newest may write the cache, or a slow older probe would pin stale
|
|
9167
|
+
* numbers for a full TTL. Clearing the map also invalidates every outstanding
|
|
9168
|
+
* ticket, so a probe that spans `clearProviderUsageCache()` cannot repopulate
|
|
9169
|
+
* the cache it was meant to drop.
|
|
9170
|
+
*/
|
|
9171
|
+
const latestProbe = /* @__PURE__ */ new Map();
|
|
9172
|
+
let probeTicket = 0;
|
|
9173
|
+
/**
|
|
9174
|
+
* Drop every cached snapshot. Entries are keyed by credential KEY, not by the
|
|
9175
|
+
* token itself, so signing out and back in (or re-authing to another account)
|
|
9176
|
+
* would otherwise keep serving the previous account's numbers for the rest of
|
|
9177
|
+
* the TTL. The TUI calls this wherever it re-reads credentials.
|
|
9178
|
+
*/
|
|
9179
|
+
function clearProviderUsageCache() {
|
|
9180
|
+
cache.clear();
|
|
9181
|
+
inFlight.clear();
|
|
9182
|
+
latestProbe.clear();
|
|
9183
|
+
}
|
|
9184
|
+
const OPT_OUT_VARS = ["ZIDANE_NO_USAGE_CHECK", "CI"];
|
|
9185
|
+
/** Whether the environment forbids usage probes outright. */
|
|
9186
|
+
function usageChecksOptedOut(env = process.env) {
|
|
9187
|
+
for (const key of OPT_OUT_VARS) {
|
|
9188
|
+
const v = env[key];
|
|
9189
|
+
if (v !== void 0 && v !== "" && v !== "0" && v.toLowerCase() !== "false") return true;
|
|
9190
|
+
}
|
|
9191
|
+
return false;
|
|
9192
|
+
}
|
|
9193
|
+
/**
|
|
9194
|
+
* Usage for one provider. `null` means there is nothing to show: the provider
|
|
9195
|
+
* has no usage surface, no credential of the kind its probe needs (file or,
|
|
9196
|
+
* for API keys, env), or the environment opted out (see
|
|
9197
|
+
* {@link usageChecksOptedOut}).
|
|
9198
|
+
*
|
|
9199
|
+
* Never rejects. Transport and parse failures land in the snapshot.
|
|
9200
|
+
*/
|
|
9201
|
+
async function fetchProviderUsage(dataDir, descriptor, opts = {}) {
|
|
9202
|
+
const source = usageSourceFor(descriptor);
|
|
9203
|
+
if (!source || usageChecksOptedOut()) return null;
|
|
9204
|
+
const credKey = credKeyOf(descriptor);
|
|
9205
|
+
const cred = resolveUsageCredential(dataDir, descriptor, source);
|
|
9206
|
+
if (!cred) return null;
|
|
9207
|
+
const identify = (snapshot) => ({
|
|
9208
|
+
...snapshot,
|
|
9209
|
+
providerKey: descriptor.key,
|
|
9210
|
+
label: descriptor.label
|
|
9211
|
+
});
|
|
9212
|
+
if (!opts.refresh) {
|
|
9213
|
+
const cached = cache.get(credKey);
|
|
9214
|
+
if (cached && cached.expiresAt > Date.now()) return identify(cached.snapshot);
|
|
9215
|
+
const pending = inFlight.get(credKey);
|
|
9216
|
+
if (pending) return pending.then(identify);
|
|
9217
|
+
}
|
|
9218
|
+
const ticket = ++probeTicket;
|
|
9219
|
+
latestProbe.set(credKey, ticket);
|
|
9220
|
+
const probe = runProbe(source, descriptor, cred).then((snapshot) => {
|
|
9221
|
+
if (latestProbe.get(credKey) === ticket) cache.set(credKey, {
|
|
9222
|
+
expiresAt: Date.now() + USAGE_CACHE_TTL_MS,
|
|
9223
|
+
snapshot
|
|
9224
|
+
});
|
|
9225
|
+
return snapshot;
|
|
9226
|
+
}).finally(() => {
|
|
9227
|
+
if (inFlight.get(credKey) === probe) inFlight.delete(credKey);
|
|
9228
|
+
});
|
|
9229
|
+
inFlight.set(credKey, probe);
|
|
9230
|
+
return probe.then(identify);
|
|
9231
|
+
}
|
|
9232
|
+
/**
|
|
9233
|
+
* The credential a probe authenticates with, or `null` when the provider isn't
|
|
9234
|
+
* authenticated in a way that probe can use.
|
|
9235
|
+
*
|
|
9236
|
+
* An API key is taken from the environment when the file has none: `detectAuth`
|
|
9237
|
+
* marks a provider available on a shell-set `envKey` alone, and the launcher
|
|
9238
|
+
* copies stored keys INTO env, so the two are the same signal. Reading only the
|
|
9239
|
+
* file would leave an env-authed user with no row at all, under an empty state
|
|
9240
|
+
* telling them they aren't signed in. OAuth tokens have no env equivalent, so
|
|
9241
|
+
* those probes stay file-only.
|
|
9242
|
+
*/
|
|
9243
|
+
function resolveUsageCredential(dataDir, descriptor, source) {
|
|
9244
|
+
const kind = SOURCE_CRED_KIND[source];
|
|
9245
|
+
const stored = readCredentials(dataDir)[credKeyOf(descriptor)];
|
|
9246
|
+
if (stored?.kind === kind && (stored.kind !== "apikey" || stored.value)) return stored;
|
|
9247
|
+
if (kind === "apikey" && descriptor.envKey) {
|
|
9248
|
+
const fromEnv = process.env[descriptor.envKey];
|
|
9249
|
+
if (fromEnv) return {
|
|
9250
|
+
kind: "apikey",
|
|
9251
|
+
value: fromEnv
|
|
9252
|
+
};
|
|
9253
|
+
}
|
|
9254
|
+
return null;
|
|
9255
|
+
}
|
|
9256
|
+
/**
|
|
9257
|
+
* Usage for every registered provider that has both a usage surface and a
|
|
9258
|
+
* usable credential, in registry order. What the panel renders.
|
|
9259
|
+
*/
|
|
9260
|
+
async function fetchAllProviderUsage(dataDir, registry, opts = {}) {
|
|
9261
|
+
const candidates = Object.values(registry).filter(providerUsageSupported);
|
|
9262
|
+
return (await Promise.all(candidates.map((descriptor) => fetchProviderUsage(dataDir, descriptor, opts)))).filter((snapshot) => snapshot !== null);
|
|
9263
|
+
}
|
|
9264
|
+
async function runProbe(source, descriptor, cred) {
|
|
9265
|
+
const base = {
|
|
9266
|
+
providerKey: descriptor.key,
|
|
9267
|
+
label: descriptor.label
|
|
9268
|
+
};
|
|
9269
|
+
try {
|
|
9270
|
+
switch (source) {
|
|
9271
|
+
case "claude": return await fetchClaudeUsage(base, cred);
|
|
9272
|
+
case "codex": return await fetchCodexUsage(base, cred);
|
|
9273
|
+
case "grok": return await fetchGrokUsage(base, cred);
|
|
9274
|
+
case "openrouter": return await fetchOpenRouterUsage(base, cred);
|
|
9275
|
+
}
|
|
9276
|
+
} catch (err) {
|
|
9277
|
+
return snapshot(base, { error: err instanceof Error ? err.message : "request failed" });
|
|
9278
|
+
}
|
|
9279
|
+
}
|
|
9280
|
+
/** Snapshot factory. Defaults to `unavailable` so failure paths stay one-liners. */
|
|
9281
|
+
function snapshot(base, partial = {}) {
|
|
9282
|
+
return {
|
|
9283
|
+
...base,
|
|
9284
|
+
status: "unavailable",
|
|
9285
|
+
plan: null,
|
|
9286
|
+
windows: [],
|
|
9287
|
+
credits: null,
|
|
9288
|
+
fetchedAt: Date.now(),
|
|
9289
|
+
error: null,
|
|
9290
|
+
...partial
|
|
9291
|
+
};
|
|
9292
|
+
}
|
|
9293
|
+
/**
|
|
9294
|
+
* OAuth access token, or an `unauthorized` snapshot explaining why there isn't
|
|
9295
|
+
* one. Expiry is checked without refreshing (see the module header); the next
|
|
9296
|
+
* run refreshes it.
|
|
9297
|
+
*/
|
|
9298
|
+
function accessTokenOr(base, cred) {
|
|
9299
|
+
if (cred.kind !== "oauth" || !cred.access) return { fail: snapshot(base, {
|
|
9300
|
+
status: "unauthorized",
|
|
9301
|
+
error: "not signed in"
|
|
9302
|
+
}) };
|
|
9303
|
+
if (typeof cred.expires === "number" && cred.expires <= Date.now()) return { fail: snapshot(base, {
|
|
9304
|
+
status: "unauthorized",
|
|
9305
|
+
error: "token expired"
|
|
9306
|
+
}) };
|
|
9307
|
+
return { token: cred.access };
|
|
9308
|
+
}
|
|
9309
|
+
/**
|
|
9310
|
+
* 401/403 to unauthorized, other non-2xx to unavailable. `null` when the
|
|
9311
|
+
* response is fine.
|
|
9312
|
+
*
|
|
9313
|
+
* A rejected token reads in the same register as the local checks (`not signed
|
|
9314
|
+
* in`, `token expired`): it is the row a real user hits most, and the status
|
|
9315
|
+
* code tells them nothing. Other codes keep the number, where it is the useful
|
|
9316
|
+
* detail.
|
|
9317
|
+
*/
|
|
9318
|
+
function httpFailure(base, res) {
|
|
9319
|
+
if (res.status === 401 || res.status === 403) return snapshot(base, {
|
|
9320
|
+
status: "unauthorized",
|
|
9321
|
+
error: "token rejected"
|
|
9322
|
+
});
|
|
9323
|
+
if (!res.ok) return snapshot(base, { error: `HTTP ${res.status}` });
|
|
9324
|
+
return null;
|
|
9325
|
+
}
|
|
9326
|
+
const CLAUDE_WINDOWS = [
|
|
9327
|
+
{
|
|
9328
|
+
key: "five_hour",
|
|
9329
|
+
id: "five_hour",
|
|
9330
|
+
label: "5h"
|
|
9331
|
+
},
|
|
9332
|
+
{
|
|
9333
|
+
key: "seven_day",
|
|
9334
|
+
id: "seven_day",
|
|
9335
|
+
label: "weekly"
|
|
9336
|
+
},
|
|
9337
|
+
{
|
|
9338
|
+
key: "seven_day_opus",
|
|
9339
|
+
id: "seven_day_opus",
|
|
9340
|
+
label: "opus weekly"
|
|
9341
|
+
},
|
|
9342
|
+
{
|
|
9343
|
+
key: "seven_day_sonnet",
|
|
9344
|
+
id: "seven_day_sonnet",
|
|
9345
|
+
label: "sonnet weekly"
|
|
9346
|
+
}
|
|
9347
|
+
];
|
|
9348
|
+
async function fetchClaudeUsage(base, cred) {
|
|
9349
|
+
const auth = accessTokenOr(base, cred);
|
|
9350
|
+
if ("fail" in auth) return auth.fail;
|
|
9351
|
+
const res = await fetch("https://api.anthropic.com/api/oauth/usage", {
|
|
9352
|
+
headers: {
|
|
9353
|
+
"Authorization": `Bearer ${auth.token}`,
|
|
9354
|
+
"anthropic-beta": "oauth-2025-04-20",
|
|
9355
|
+
"Accept": "application/json"
|
|
9356
|
+
},
|
|
9357
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
9358
|
+
});
|
|
9359
|
+
const failure = httpFailure(base, res);
|
|
9360
|
+
if (failure) return failure;
|
|
9361
|
+
const data = await res.json().catch(() => null);
|
|
9362
|
+
if (!data) return snapshot(base, { error: "invalid response" });
|
|
9363
|
+
const windows = [];
|
|
9364
|
+
for (const { key, id, label } of CLAUDE_WINDOWS) {
|
|
9365
|
+
const win = data[key];
|
|
9366
|
+
if (win && typeof win.utilization === "number") windows.push({
|
|
9367
|
+
id,
|
|
9368
|
+
label,
|
|
9369
|
+
usedPercent: clampPercent(win.utilization),
|
|
9370
|
+
resetsAt: isoToEpoch(win.resets_at)
|
|
9371
|
+
});
|
|
9372
|
+
}
|
|
9373
|
+
if (windows.length === 0) return snapshot(base, { error: "no usage data" });
|
|
9374
|
+
return snapshot(base, {
|
|
9375
|
+
status: "ok",
|
|
9376
|
+
windows
|
|
9377
|
+
});
|
|
9378
|
+
}
|
|
9379
|
+
/** Codex reports window length, not a name, so derive `5h` / `weekly` / `Nd`. */
|
|
9380
|
+
function codexWindowLabel(win, fallback) {
|
|
9381
|
+
const seconds = win.limit_window_seconds;
|
|
9382
|
+
if (!seconds) return fallback;
|
|
9383
|
+
if (seconds < 86400) return `${Math.round(seconds / 3600)}h`;
|
|
9384
|
+
const days = Math.round(seconds / 86400);
|
|
9385
|
+
return days === 7 ? "weekly" : `${days}d`;
|
|
9386
|
+
}
|
|
9387
|
+
async function fetchCodexUsage(base, cred) {
|
|
9388
|
+
const auth = accessTokenOr(base, cred);
|
|
9389
|
+
if ("fail" in auth) return auth.fail;
|
|
9390
|
+
const accountId = cred.kind === "oauth" && typeof cred.accountId === "string" ? cred.accountId : null;
|
|
9391
|
+
const res = await fetch("https://chatgpt.com/backend-api/wham/usage", {
|
|
9392
|
+
headers: {
|
|
9393
|
+
"Authorization": `Bearer ${auth.token}`,
|
|
9394
|
+
...accountId ? { "ChatGPT-Account-Id": accountId } : {},
|
|
9395
|
+
"User-Agent": "codex-cli",
|
|
9396
|
+
"Accept": "application/json"
|
|
9397
|
+
},
|
|
9398
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
9399
|
+
});
|
|
9400
|
+
const failure = httpFailure(base, res);
|
|
9401
|
+
if (failure) return failure;
|
|
9402
|
+
const data = await res.json().catch(() => null);
|
|
9403
|
+
if (!data) return snapshot(base, { error: "invalid response" });
|
|
9404
|
+
const rateLimit = data.rate_limit ?? data.rate_limits;
|
|
9405
|
+
const windows = [];
|
|
9406
|
+
const push = (win, id, fallbackLabel) => {
|
|
9407
|
+
if (win && typeof win.used_percent === "number") windows.push({
|
|
9408
|
+
id,
|
|
9409
|
+
label: codexWindowLabel(win, fallbackLabel),
|
|
9410
|
+
usedPercent: clampPercent(win.used_percent),
|
|
9411
|
+
resetsAt: typeof win.reset_at === "number" ? win.reset_at * 1e3 : null
|
|
9412
|
+
});
|
|
9413
|
+
};
|
|
9414
|
+
push(rateLimit?.primary_window, "primary", "5h");
|
|
9415
|
+
push(rateLimit?.secondary_window, "secondary", "weekly");
|
|
9416
|
+
const credits = data.credits && typeof data.credits.balance === "number" && !data.credits.unlimited ? {
|
|
9417
|
+
remaining: data.credits.balance,
|
|
9418
|
+
total: null
|
|
9419
|
+
} : null;
|
|
9420
|
+
if (windows.length === 0 && !credits) return snapshot(base, { error: "no usage data" });
|
|
9421
|
+
return snapshot(base, {
|
|
9422
|
+
status: "ok",
|
|
9423
|
+
plan: typeof data.plan_type === "string" ? data.plan_type : null,
|
|
9424
|
+
windows,
|
|
9425
|
+
credits
|
|
9426
|
+
});
|
|
9427
|
+
}
|
|
9428
|
+
async function fetchOpenRouterUsage(base, cred) {
|
|
9429
|
+
if (cred.kind !== "apikey" || !cred.value) return snapshot(base, {
|
|
9430
|
+
status: "unauthorized",
|
|
9431
|
+
error: "no API key stored"
|
|
9432
|
+
});
|
|
9433
|
+
const res = await fetch("https://openrouter.ai/api/v1/credits", {
|
|
9434
|
+
headers: {
|
|
9435
|
+
Authorization: `Bearer ${cred.value}`,
|
|
9436
|
+
Accept: "application/json"
|
|
9437
|
+
},
|
|
9438
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
9439
|
+
});
|
|
9440
|
+
const failure = httpFailure(base, res);
|
|
9441
|
+
if (failure) return failure;
|
|
9442
|
+
const totals = (await res.json().catch(() => null))?.data;
|
|
9443
|
+
if (!totals || typeof totals.total_credits !== "number" || typeof totals.total_usage !== "number") return snapshot(base, { error: "invalid response" });
|
|
9444
|
+
return snapshot(base, {
|
|
9445
|
+
status: "ok",
|
|
9446
|
+
windows: [{
|
|
9447
|
+
id: "credits",
|
|
9448
|
+
label: "credits",
|
|
9449
|
+
usedPercent: totals.total_credits > 0 ? clampPercent(totals.total_usage / totals.total_credits * 100) : 0,
|
|
9450
|
+
resetsAt: null
|
|
9451
|
+
}],
|
|
9452
|
+
credits: {
|
|
9453
|
+
remaining: totals.total_credits - totals.total_usage,
|
|
9454
|
+
total: totals.total_credits
|
|
9455
|
+
}
|
|
9456
|
+
});
|
|
9457
|
+
}
|
|
9458
|
+
const GROK_BILLING_URL = "https://grok.com/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig";
|
|
9459
|
+
const GROK_AUTH_FAILURE_RE = /access token|unauthenticated|expired/i;
|
|
9460
|
+
async function fetchGrokUsage(base, cred) {
|
|
9461
|
+
const auth = accessTokenOr(base, cred);
|
|
9462
|
+
if ("fail" in auth) return auth.fail;
|
|
9463
|
+
const res = await fetch(GROK_BILLING_URL, {
|
|
9464
|
+
method: "POST",
|
|
9465
|
+
headers: {
|
|
9466
|
+
"Authorization": `Bearer ${auth.token}`,
|
|
9467
|
+
"Content-Type": "application/grpc-web+proto",
|
|
9468
|
+
"x-grpc-web": "1",
|
|
9469
|
+
"x-user-agent": "connect-es/2.1.1",
|
|
9470
|
+
"Origin": "https://grok.com",
|
|
9471
|
+
"Referer": "https://grok.com/?_s=usage",
|
|
9472
|
+
"Accept": "*/*"
|
|
9473
|
+
},
|
|
9474
|
+
body: new Uint8Array([
|
|
9475
|
+
0,
|
|
9476
|
+
0,
|
|
9477
|
+
0,
|
|
9478
|
+
0,
|
|
9479
|
+
0
|
|
9480
|
+
]),
|
|
9481
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
9482
|
+
});
|
|
9483
|
+
const failure = httpFailure(base, res);
|
|
9484
|
+
if (failure) return failure;
|
|
9485
|
+
const grpcStatus = res.headers.get("grpc-status");
|
|
9486
|
+
if (grpcStatus && grpcStatus !== "0") {
|
|
9487
|
+
const message = res.headers.get("grpc-message") ?? "";
|
|
9488
|
+
return grpcStatus === "16" || grpcStatus === "7" && GROK_AUTH_FAILURE_RE.test(message) ? snapshot(base, {
|
|
9489
|
+
status: "unauthorized",
|
|
9490
|
+
error: "token not accepted"
|
|
9491
|
+
}) : snapshot(base, { error: `gRPC ${grpcStatus}${message ? `: ${message}` : ""}` });
|
|
9492
|
+
}
|
|
9493
|
+
const parsed = parseGrokBillingResponse(new Uint8Array(await res.arrayBuffer()));
|
|
9494
|
+
if (!parsed) return snapshot(base, { error: "unreadable response" });
|
|
9495
|
+
return snapshot(base, {
|
|
9496
|
+
status: "ok",
|
|
9497
|
+
windows: [{
|
|
9498
|
+
id: "credits",
|
|
9499
|
+
label: "credits",
|
|
9500
|
+
usedPercent: clampPercent(parsed.usedPercent),
|
|
9501
|
+
resetsAt: parsed.resetsAt ? parsed.resetsAt.getTime() : null
|
|
9502
|
+
}]
|
|
9503
|
+
});
|
|
9504
|
+
}
|
|
9505
|
+
/** Split a gRPC-web body into data frames. Flag bit 0x80 marks trailers. */
|
|
9506
|
+
function grpcWebDataFrames(bytes) {
|
|
9507
|
+
const frames = [];
|
|
9508
|
+
let index = 0;
|
|
9509
|
+
while (index < bytes.length) {
|
|
9510
|
+
if (index + 5 > bytes.length) return [];
|
|
9511
|
+
const flags = bytes[index];
|
|
9512
|
+
const length = bytes[index + 1] << 24 | bytes[index + 2] << 16 | bytes[index + 3] << 8 | bytes[index + 4];
|
|
9513
|
+
const start = index + 5;
|
|
9514
|
+
const end = start + length;
|
|
9515
|
+
if (length < 0 || end > bytes.length) return [];
|
|
9516
|
+
if ((flags & 128) === 0) frames.push(bytes.subarray(start, end));
|
|
9517
|
+
index = end;
|
|
9518
|
+
}
|
|
9519
|
+
return frames;
|
|
9520
|
+
}
|
|
9521
|
+
function looksLikeProtobuf(bytes) {
|
|
9522
|
+
if (bytes.length === 0) return false;
|
|
9523
|
+
const fieldNumber = bytes[0] >> 3;
|
|
9524
|
+
const wireType = bytes[0] & 7;
|
|
9525
|
+
return fieldNumber > 0 && (wireType === 0 || wireType === 1 || wireType === 2 || wireType === 5);
|
|
9526
|
+
}
|
|
9527
|
+
function readVarint(bytes, offset) {
|
|
9528
|
+
let value = 0;
|
|
9529
|
+
let shift = 0;
|
|
9530
|
+
let index = offset;
|
|
9531
|
+
while (index < bytes.length && shift < 64) {
|
|
9532
|
+
const byte = bytes[index];
|
|
9533
|
+
value += (byte & 127) * 2 ** shift;
|
|
9534
|
+
index += 1;
|
|
9535
|
+
if ((byte & 128) === 0) return {
|
|
9536
|
+
value,
|
|
9537
|
+
next: index
|
|
9538
|
+
};
|
|
9539
|
+
shift += 7;
|
|
9540
|
+
}
|
|
9541
|
+
return null;
|
|
9542
|
+
}
|
|
9543
|
+
function scanProtobuf(bytes, path, scan, orderRef, depth) {
|
|
9544
|
+
if (depth > 6) return;
|
|
9545
|
+
let index = 0;
|
|
9546
|
+
while (index < bytes.length) {
|
|
9547
|
+
const tag = readVarint(bytes, index);
|
|
9548
|
+
if (!tag) return;
|
|
9549
|
+
const fieldNumber = Math.floor(tag.value / 8);
|
|
9550
|
+
const wireType = tag.value % 8;
|
|
9551
|
+
if (fieldNumber === 0) return;
|
|
9552
|
+
index = tag.next;
|
|
9553
|
+
const fieldPath = [...path, fieldNumber];
|
|
9554
|
+
switch (wireType) {
|
|
9555
|
+
case 0: {
|
|
9556
|
+
const v = readVarint(bytes, index);
|
|
9557
|
+
if (!v) return;
|
|
9558
|
+
scan.varint.push({
|
|
9559
|
+
path: fieldPath,
|
|
9560
|
+
order: orderRef.n++,
|
|
9561
|
+
value: v.value
|
|
9562
|
+
});
|
|
9563
|
+
index = v.next;
|
|
9564
|
+
break;
|
|
9565
|
+
}
|
|
9566
|
+
case 1:
|
|
9567
|
+
if (index + 8 > bytes.length) return;
|
|
9568
|
+
index += 8;
|
|
9569
|
+
break;
|
|
9570
|
+
case 5: {
|
|
9571
|
+
if (index + 4 > bytes.length) return;
|
|
9572
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset + index, 4);
|
|
9573
|
+
scan.fixed32.push({
|
|
9574
|
+
path: fieldPath,
|
|
9575
|
+
order: orderRef.n++,
|
|
9576
|
+
value: view.getFloat32(0, true)
|
|
9577
|
+
});
|
|
9578
|
+
index += 4;
|
|
9579
|
+
break;
|
|
9580
|
+
}
|
|
9581
|
+
case 2: {
|
|
9582
|
+
const len = readVarint(bytes, index);
|
|
9583
|
+
if (!len || len.next + len.value > bytes.length) return;
|
|
9584
|
+
const sub = bytes.subarray(len.next, len.next + len.value);
|
|
9585
|
+
if (looksLikeProtobuf(sub)) scanProtobuf(sub, fieldPath, scan, orderRef, depth + 1);
|
|
9586
|
+
index = len.next + len.value;
|
|
9587
|
+
break;
|
|
9588
|
+
}
|
|
9589
|
+
default: return;
|
|
9590
|
+
}
|
|
9591
|
+
}
|
|
9592
|
+
}
|
|
9593
|
+
/**
|
|
9594
|
+
* Recover `{ usedPercent, resetsAt }` from the billing protobuf without a
|
|
9595
|
+
* schema. The used percent is a fixed32 float in 0-100 whose path ends in field
|
|
9596
|
+
* 1 (shallowest, earliest wins); the reset is the nearest future epoch-second
|
|
9597
|
+
* varint, preferring path `[1,5,1]` (billing-cycle end).
|
|
9598
|
+
*/
|
|
9599
|
+
function parseGrokBillingResponse(bytes, now = /* @__PURE__ */ new Date()) {
|
|
9600
|
+
let frames = grpcWebDataFrames(bytes);
|
|
9601
|
+
if (frames.length === 0 && looksLikeProtobuf(bytes)) frames = [bytes];
|
|
9602
|
+
if (frames.length === 0) return null;
|
|
9603
|
+
const scan = {
|
|
9604
|
+
fixed32: [],
|
|
9605
|
+
varint: []
|
|
9606
|
+
};
|
|
9607
|
+
const orderRef = { n: 0 };
|
|
9608
|
+
for (const frame of frames) scanProtobuf(frame, [], scan, orderRef, 0);
|
|
9609
|
+
const percentField = scan.fixed32.filter((f) => f.path.at(-1) === 1 && Number.isFinite(f.value) && f.value >= 0 && f.value <= 100).sort((a, b) => a.path.length === b.path.length ? a.order - b.order : a.path.length - b.path.length)[0];
|
|
9610
|
+
const epochFields = scan.varint.filter((f) => f.value >= 17e8 && f.value <= 21e8).map((f) => ({
|
|
9611
|
+
path: f.path,
|
|
9612
|
+
date: /* @__PURE__ */ new Date(f.value * 1e3)
|
|
9613
|
+
})).filter((f) => f.date > now);
|
|
9614
|
+
const preferred = epochFields.filter((f) => f.path.length === 3 && f.path[0] === 1 && f.path[1] === 5 && f.path[2] === 1);
|
|
9615
|
+
const resetsAt = (preferred.length > 0 ? preferred : epochFields).map((f) => f.date).sort((a, b) => a.getTime() - b.getTime())[0] ?? null;
|
|
9616
|
+
const hasUsagePeriod = scan.varint.some((f) => f.path.length >= 2 && f.path[0] === 1 && f.path[1] === 6 || f.path.length === 3 && f.path[0] === 1 && f.path[1] === 8 && f.path[2] === 1 && (f.value === 1 || f.value === 2));
|
|
9617
|
+
if (!percentField) {
|
|
9618
|
+
if (scan.fixed32.length === 0 && resetsAt && hasUsagePeriod) return {
|
|
9619
|
+
usedPercent: 0,
|
|
9620
|
+
resetsAt
|
|
9621
|
+
};
|
|
9622
|
+
return null;
|
|
9623
|
+
}
|
|
9624
|
+
return {
|
|
9625
|
+
usedPercent: percentField.value,
|
|
9626
|
+
resetsAt
|
|
9627
|
+
};
|
|
9628
|
+
}
|
|
9629
|
+
/**
|
|
9630
|
+
* Percent without trailing noise: `43`, `7.5`, `100`. Used by the panel. The
|
|
9631
|
+
* footer floors to a whole percent instead (`footerUsagePercent` in
|
|
9632
|
+
* `tui/components.tsx`), so a status bar never reads `100%` for `99.9`.
|
|
9633
|
+
*/
|
|
9634
|
+
function formatUsagePercent(usedPercent) {
|
|
9635
|
+
return Number.isInteger(usedPercent) ? String(usedPercent) : usedPercent.toFixed(1);
|
|
9636
|
+
}
|
|
9637
|
+
function clampPercent(value) {
|
|
9638
|
+
if (!Number.isFinite(value)) return 0;
|
|
9639
|
+
return Math.min(100, Math.max(0, Math.round(value * 10) / 10));
|
|
9640
|
+
}
|
|
9641
|
+
function isoToEpoch(value) {
|
|
9642
|
+
if (typeof value !== "string") return null;
|
|
9643
|
+
const ts = new Date(value).getTime();
|
|
9644
|
+
return Number.isFinite(ts) ? ts : null;
|
|
9645
|
+
}
|
|
9646
|
+
//#endregion
|
|
9028
9647
|
//#region src/chat/shell-parse.ts
|
|
9029
9648
|
/**
|
|
9030
9649
|
* Lightweight shell command parser for safelist enforcement.
|
|
@@ -10555,6 +11174,6 @@ function computeTurnAnchors(items) {
|
|
|
10555
11174
|
};
|
|
10556
11175
|
}
|
|
10557
11176
|
//#endregion
|
|
10558
|
-
export {
|
|
11177
|
+
export { buildToolToggle as $, singleAgentRegistry as $n, CATPPUCCIN_MACCHIATO as $t, writeProjects as A, bootProfileEnabled as An, DEFAULT_SESSION_TITLE as Ar, hintsLength as At, formatPathForCwd as B, performSelfUpdate as Bn, saveState as Br, SETTINGS_TOGGLES as Bt, addToSafelist as C, upsertMcpServer as Cn, SUBAGENT_GUIDANCE as Cr, makeRequestInteraction as Ct, projectsFilePath as D, discoverProjectMcps as Dn, envSection as Dr, useInteractionsQueue as Dt, matchesSafelistEntry as E, defaultMcpsConfigPaths as En, buildPlanSystem as Er, useInteractionsActions as Et, formatUsagePercent as F, compareSemver as Fn, lastContextSizeFromTurns as Fr, useEnabledToggleSet as Ft, runOAuthLogin as G, BUILD_AGENT as Gn, toolCallPreview as Gr, DEFAULT_THEME as Gt, normalizeRedirectPaste as H, chatAutoCompactBehavior as Hn, stripSpawnTokensLine as Hr, clampFps as Ht, mostConstrainedWindow as I, detectLibc as In, listSessionMeta as Ir, DEFAULT_SETTINGS as It, loadMcpToolsCache as J, DEFAULT_BUDGET_EXCLUDE_TOOLS as Jn, usageByModel as Jr, VAPORWAVE_THEME as Jt, supportsOAuth as K, BUILTIN_AGENTS as Kn, toolResultText as Kr, resolveChipColor as Kt, providerUsageSupported as L, detectPackageManager as Ln, loadState as Lr, SETTINGS_CATEGORIES as Lt, clearProviderUsageCache as M, buildUpdateHint as Mn, createStateStore as Mr, buildHints as Mt, fetchAllProviderUsage as N, useUpdateCheck as Nn, deriveSessionTitle as Nr, shortChord as Nt, readProjects as O, parseMcpsFile as On, chatReferenceTokensForPrompt as Or, EMPTY_HINTS as Ot, fetchProviderUsage as P, checkForUpdate as Pn, eventsFromTurns as Pr, listProjectFiles as Pt, subscribeMcpToolsCache as Q, resolveAgentId as Qn, CATPPUCCIN_LATTE as Qt, usageChecksOptedOut as R, parseSemver as Rn, marginTopFor as Rr, SETTINGS_CHOICES as Rt, IMPLICITLY_SAFE_TOOLS as S, removeMcpServer as Sn, PLAN_MODE_DOCTRINE_NO_PROMPTS as Sr, isModelInteraction as St, isOnSafelist as T, dedupMcpServersByName as Tn, buildBuildSystem as Tr, serializeInteractionResponse as Tt, redirectUriFromAuthUrl as U, discoverAgentsMd as Un, sumRunCosts as Ur, useSettings as Ut, fetchOAuthRedirect as V, resolvePlatformPackage as Vn, serverToolResultSummary as Vr, SettingsProvider as Vt, oauthUsesManualCodePaste as W, renderAgentsMdBlock as Wn, titleFromTurns as Wr, BUILTIN_THEMES as Wt, refreshMcpToolsCatalog as X, PLAN_AGENT as Xn, GRUVBOX_LIGHT as Xt, mcpToolsCachePath as Y, DEFAULT_PERSIST_EXCLUDE_TOOLS as Yn, GRUVBOX_DARK as Yt, saveMcpToolsCache as Z, accentColor as Zn, CATPPUCCIN_FRAPPE as Zt, resolveSessionExportTarget as _, useCompletion as _n, DOING_TASKS_DOCTRINE as _r, PRESENT_PLAN_TOOL as _t, useSurfaces as a, ConfigProvider as an, createTodoTools as ar, createFileMcpCredentialStore as at, useSafeModeActions as b, importFromClaudeCode as bn, INTERACTION_GUIDANCE_NO_PROMPTS as br, createInteractionTools as bt, discoverSystemMd as c, resolveStoragePaths as cn, isTodoTool as cr, McpAuthProvider as ct, userSystemMdPath as d, chainsEqual as dn, selectActiveTodos as dr, getMcpAuthStatus as dt, CATPPUCCIN_MOCHA as en, TODOREAD_TOOL as er, useMcpToolToggleMap as et, useStreamBuffer as f, filterChainFallbacks as fn, setTodosForRun as fr, reduceMcpAuth as ft, renderSession as g, toggleChainEntry as gn, COMMUNICATION_DOCTRINE as gr, InteractionsProvider as gt, discoverProjectSkills as h, sanitizeModelChain as hn, ACTIONS_WITH_CARE_DOCTRINE as hr, HOST_INTERACTION_ID_PREFIX as ht, useSelectStyle as i, useDiscoveryOptional as in, TODO_WRITE_COUNTS_METADATA_KEY as ir, parentServerName as it, USAGE_CACHE_TTL_MS as j, bootTick as jn, compactSummaryEvent as jr, truncateTrailing as jt, suggestSafelistEntries as k, tryOpenBrowser as kn, createChatTurnsReadTool as kr, clipHintsToWidth as kt, renderSystemMdSeed as l, buildRunModel as ln, pickActiveRunId as lr, useMcpAuthDispatch as lt, defaultSkillScanPaths as m, restoreChainFallbacks as mn, useActiveTodos as mr, AUTOMATIC_INTERACTION_INSTRUCTION as mt, ThemeProvider as n, DiscoveryProvider as nn, TODOWRITE_TOOL as nr, buildVisibleMcpRows as nt, useSyntaxStyles as o, useConfig as on, getArchivedTodosForRun as or, mcpCredentialsPath as ot, buildSkillsConfig as p, providerKeySetChanged as pn, todoProgressLabel as pr, ASK_USER_TOOL as pt, clearMcpToolsCache as q, DEFAULT_AGENT_ID as qn, updateToolEventOutcomes as qr, resolveTheme as qt, useColors as r, useDiscovery as rn, TODO_STATUS_GLYPHS as rr, indexOfServerRow as rt, useTheme as s, resolveConfig as sn, getTodosForRun as sr, patchMcpCredential as st, computeTurnAnchors as t, createDiscoverySlot as tn, TODOS_METADATA_KEY as tr, useMcpToolToggleSet as tt, userScopeOverridesLoad as u, chainPositionOf as un, pruneTodosByRun as ur, useMcpAuthState as ut, writeSessionExport as v, countClaudeSessionFiles as vn, IDENTITY_PREFIX as vr, buildResumedToolResultsTurn as vt, getSafelist as w, buildMcpServers as wn, TOKEN_DISCIPLINE_DOCTRINE as wr, pendingInteractionsFromTurns as wt, useSafeModeQueue as x, mcpsWritePath as xn, PLAN_MODE_DOCTRINE as xr, isInteractionTool as xt, SafeModeProvider as y, hasClaudeCodeSetup as yn, INTERACTION_GUIDANCE as yr, createAutomaticInteractionResponse as yt, usageSourceFor as z, performInPlaceSelfUpdate as zn, noticeTurnContent as zr, SETTINGS_INPUTS as zt };
|
|
10559
11178
|
|
|
10560
|
-
//# sourceMappingURL=transcript-anchors-
|
|
11179
|
+
//# sourceMappingURL=transcript-anchors-BjH-68k2.js.map
|