zidane 5.14.5 → 6.0.0
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-BpFEbCMQ.js → acp-Irby04Us.js} +7 -8
- package/dist/{acp-BpFEbCMQ.js.map → acp-Irby04Us.js.map} +1 -1
- package/dist/acp-cli.js +7 -7
- package/dist/acp.d.ts +2 -3
- package/dist/acp.d.ts.map +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-DyI0YsAR.js → agent-DkSmGkDM.js} +10873 -5437
- package/dist/agent-DkSmGkDM.js.map +1 -0
- package/dist/agent.d.ts +1 -2
- package/dist/agent.js +2 -2
- package/dist/{anthropic-cbfW2RGg.js → anthropic-CTHsdFG9.js} +5 -4
- package/dist/{anthropic-cbfW2RGg.js.map → anthropic-CTHsdFG9.js.map} +1 -1
- package/dist/{auth-D9uj0nNr.js → auth-BDSu_0t3.js} +13 -86
- package/dist/auth-BDSu_0t3.js.map +1 -0
- package/dist/chat/pure.d.ts +4 -4
- package/dist/chat/pure.js +4 -3
- package/dist/chat.d.ts +29 -8
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +9 -6
- package/dist/chat.js.map +1 -1
- package/dist/completion-core-CXYSVhZo.js +147 -0
- package/dist/completion-core-CXYSVhZo.js.map +1 -0
- package/dist/{context-breakdown-kO-pDsay.js → context-breakdown-Dzo25N45.js} +2 -23
- package/dist/context-breakdown-Dzo25N45.js.map +1 -0
- package/dist/contexts/daytona.d.ts +3 -3
- package/dist/contexts/daytona.js +1 -1
- package/dist/contexts/docker.d.ts +10 -2
- package/dist/contexts/docker.d.ts.map +1 -1
- package/dist/contexts/docker.js +142 -103
- package/dist/contexts/docker.js.map +1 -1
- package/dist/contexts/e2b.d.ts +2 -2
- package/dist/contexts/e2b.js +1 -1
- package/dist/contexts/sandbox.d.ts +1 -1
- package/dist/contexts/sandbox.js +4 -1
- package/dist/contexts/sandbox.js.map +1 -1
- package/dist/{contexts-Lzf5huID.js → contexts-Vone3qQQ.js} +26 -37
- package/dist/contexts-Vone3qQQ.js.map +1 -0
- package/dist/contexts.d.ts +3 -3
- package/dist/contexts.js +1 -1
- package/dist/errors.d.ts +1 -2
- package/dist/errors.js +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +3 -3
- package/dist/exec-file-size-PXkr-MGA.js +63 -0
- package/dist/exec-file-size-PXkr-MGA.js.map +1 -0
- package/dist/extensions.d.ts +2 -0
- package/dist/extensions.js +2 -0
- package/dist/generate-data-WL_yGVL6.js +324 -0
- package/dist/generate-data-WL_yGVL6.js.map +1 -0
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +15 -10
- package/dist/headless.js.map +1 -1
- package/dist/index-B8UtiZy7.d.ts +14967 -0
- package/dist/index-B8UtiZy7.d.ts.map +1 -0
- package/dist/{index-B3ciFUZx.d.ts → index-DsPQbyUn.d.ts} +2 -2
- package/dist/index-DsPQbyUn.d.ts.map +1 -0
- package/dist/index.d.ts +6 -12
- package/dist/index.js +20 -43
- package/dist/index.js.map +1 -1
- package/dist/{logger-Ktm-lj1s.js → logger-DBX9uYOw.js} +108 -5
- package/dist/logger-DBX9uYOw.js.map +1 -0
- package/dist/login-DOF8pMVM.js +539 -0
- package/dist/login-DOF8pMVM.js.map +1 -0
- package/dist/{mcp-Dn5W65Lv.js → mcp-CcvuVXB9.js} +2 -2
- package/dist/{mcp-Dn5W65Lv.js.map → mcp-CcvuVXB9.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-CozesxbB.js → messages-CwujiS74.js} +4 -4
- package/dist/messages-CwujiS74.js.map +1 -0
- package/dist/{openai-compat-FII_9wJP.js → openai-compat-BvgYGSR1.js} +5 -4
- package/dist/{openai-compat-FII_9wJP.js.map → openai-compat-BvgYGSR1.js.map} +1 -1
- package/dist/output/stream-json.d.ts +1 -2
- package/dist/output/stream-json.d.ts.map +1 -1
- package/dist/output/terminal.d.ts +1 -2
- package/dist/output/terminal.d.ts.map +1 -1
- package/dist/{glob-DluQFSw1.js → output-cap-kBUHBlnq.js} +50 -2
- package/dist/output-cap-kBUHBlnq.js.map +1 -0
- package/dist/presets.d.ts +1 -2
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.js +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/arcee.js +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/baseten.js +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/cerebras.js +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/local.js +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai-compat.js +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +3 -3
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +1 -1
- package/dist/{providers-DYXj2LcE.js → providers-z6LDMAr3.js} +3 -3
- package/dist/{providers-DYXj2LcE.js.map → providers-z6LDMAr3.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +4 -4
- package/dist/{read-state-BymF41Jb.js → read-state-Dq_TXUX1.js} +2 -2
- package/dist/{read-state-BymF41Jb.js.map → read-state-Dq_TXUX1.js.map} +1 -1
- package/dist/{interpolate-BtIgcCuz.js → resolve-Cr8JSL_O.js} +562 -136
- package/dist/resolve-Cr8JSL_O.js.map +1 -0
- package/dist/restate.d.ts +2 -2
- package/dist/restate.js +2 -2
- package/dist/{sandbox-Dgy-m6UF.d.ts → sandbox-CgjG2VvQ.d.ts} +2 -2
- package/dist/{sandbox-Dgy-m6UF.d.ts.map → sandbox-CgjG2VvQ.d.ts.map} +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-BRCsZsXa.js → session-CsdDIPY8.js} +108 -3
- package/dist/session-CsdDIPY8.js.map +1 -0
- package/dist/session.d.ts +2 -2
- package/dist/session.js +3 -3
- package/dist/skills-Bsb0rvWI.js +295 -0
- package/dist/skills-Bsb0rvWI.js.map +1 -0
- package/dist/skills.d.ts +2 -3
- package/dist/skills.js +3 -24
- package/dist/{errors-CkTvg97v.d.ts → timeout-8vSIRjzl.d.ts} +10 -2
- package/dist/timeout-8vSIRjzl.d.ts.map +1 -0
- package/dist/{timeout-kGGSXagK.js → timeout-BDetbuN6.js} +59 -2
- package/dist/timeout-BDetbuN6.js.map +1 -0
- package/dist/tool-formatters-DY0TDb1r.d.ts +378 -0
- package/dist/tool-formatters-DY0TDb1r.d.ts.map +1 -0
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +2 -3
- package/dist/tools.js +3 -4
- package/dist/{transcript-anchors-BEKMYhfo.d.ts → transcript-anchors-Cb-bDbNH.d.ts} +231 -638
- package/dist/transcript-anchors-Cb-bDbNH.d.ts.map +1 -0
- package/dist/{transcript-anchors-DR4URysA.js → transcript-anchors-DSRPWjiH.js} +189 -984
- package/dist/transcript-anchors-DSRPWjiH.js.map +1 -0
- package/dist/tui.d.ts +329 -11
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +5367 -1418
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-BhglWus_.d.ts → turn-operations-CgMROLsr.d.ts} +29 -4
- package/dist/turn-operations-CgMROLsr.d.ts.map +1 -0
- package/dist/{turn-operations-tG0vuBNc.js → turn-operations-Dq9Kx7m2.js} +321 -81
- package/dist/turn-operations-Dq9Kx7m2.js.map +1 -0
- package/dist/{types-BqdTeBaC.d.ts → types-BDccavwj.d.ts} +42 -1
- package/dist/types-BDccavwj.d.ts.map +1 -0
- package/dist/{types-CyVGdbia.js → types-BIarq1qE.js} +90 -2
- package/dist/types-BIarq1qE.js.map +1 -0
- package/dist/types.d.ts +5 -6
- package/dist/types.js +1 -1
- package/dist/{xai-D9tfkrIe.js → xai-B_e6TOA8.js} +2 -2
- package/dist/{xai-D9tfkrIe.js.map → xai-B_e6TOA8.js.map} +1 -1
- package/docs/EXECUTION_CONTEXT.md +16 -0
- package/docs/EXTENSIONS.md +1063 -0
- package/docs/SKILL.md +16 -0
- package/docs/TUI.md +4 -0
- package/package.json +12 -3
- package/dist/agent-DCtsviti.d.ts +0 -6955
- package/dist/agent-DCtsviti.d.ts.map +0 -1
- package/dist/agent-DyI0YsAR.js.map +0 -1
- package/dist/auth-D9uj0nNr.js.map +0 -1
- package/dist/context-breakdown-kO-pDsay.js.map +0 -1
- package/dist/contexts-Lzf5huID.js.map +0 -1
- package/dist/errors-CkTvg97v.d.ts.map +0 -1
- package/dist/glob-DluQFSw1.js.map +0 -1
- package/dist/glob-shell-rJMoCIGb.js +0 -21
- package/dist/glob-shell-rJMoCIGb.js.map +0 -1
- package/dist/index-B3ciFUZx.d.ts.map +0 -1
- package/dist/index-CIbemeZd.d.ts +0 -316
- package/dist/index-CIbemeZd.d.ts.map +0 -1
- package/dist/index-DsIW6pmv.d.ts +0 -2738
- package/dist/index-DsIW6pmv.d.ts.map +0 -1
- package/dist/interpolate-BtIgcCuz.js.map +0 -1
- package/dist/logger-Ktm-lj1s.js.map +0 -1
- package/dist/logger-mw-311EC.d.ts +0 -102
- package/dist/logger-mw-311EC.d.ts.map +0 -1
- package/dist/login-CCA-1lgK.js +0 -267
- package/dist/login-CCA-1lgK.js.map +0 -1
- package/dist/messages-CozesxbB.js.map +0 -1
- package/dist/policy-DcGlpaNs.d.ts +0 -129
- package/dist/policy-DcGlpaNs.d.ts.map +0 -1
- package/dist/presets-DtyUA1-S.js +0 -114
- package/dist/presets-DtyUA1-S.js.map +0 -1
- package/dist/run-summary-BExU5Flb.d.ts +0 -249
- package/dist/run-summary-BExU5Flb.d.ts.map +0 -1
- package/dist/session-BRCsZsXa.js.map +0 -1
- package/dist/shell-quote-BmnhZmdM.js +0 -33
- package/dist/shell-quote-BmnhZmdM.js.map +0 -1
- package/dist/skills.js.map +0 -1
- package/dist/timeout-CpFm0jJ6.d.ts +0 -10
- package/dist/timeout-CpFm0jJ6.d.ts.map +0 -1
- package/dist/timeout-kGGSXagK.js.map +0 -1
- package/dist/tool-formatters-CpCEWwKt.d.ts +0 -1525
- package/dist/tool-formatters-CpCEWwKt.d.ts.map +0 -1
- package/dist/tools-NSzbC6Nr.js +0 -1739
- package/dist/tools-NSzbC6Nr.js.map +0 -1
- package/dist/transcript-anchors-BEKMYhfo.d.ts.map +0 -1
- package/dist/transcript-anchors-DR4URysA.js.map +0 -1
- package/dist/turn-operations-BhglWus_.d.ts.map +0 -1
- package/dist/turn-operations-tG0vuBNc.js.map +0 -1
- package/dist/types-BqdTeBaC.d.ts.map +0 -1
- package/dist/types-CyVGdbia.js.map +0 -1
|
@@ -1,26 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { a as multiEdit, c as grep, i as readFile$1, l as glob, n as createSpawnTool, o as listFiles, t as writeFile$1, u as edit } from "./tools-NSzbC6Nr.js";
|
|
1
|
+
import { l as toolResultToText } from "./types-BIarq1qE.js";
|
|
2
|
+
import { Ct as listFiles, Dt as edit, Et as glob, Ht as readKeybindingsRaw, I as EMPTY_EXTENSION_REGISTRY, St as multiEdit, Tt as grep, Ut as resolveExtensionKeybindings, Y as buildExtensionPreset, at as definePreset, ct as writeFile$1, ft as createSpawnTool, it as composePresets, kt as resolveStorageDirs, xt as readFile$1, yt as shell, zt as mergeKeybindings } from "./agent-DkSmGkDM.js";
|
|
4
3
|
import { c as shortId, r as fmtTokens, s as previewLine } from "./format-BNOXpl-1.js";
|
|
5
4
|
import { l as errorMessage } from "./errors-DJUxZg9b.js";
|
|
6
|
-
import {
|
|
5
|
+
import { M as findGitRoot$1, i as discoverSkills, j as projectUserPaths } from "./resolve-Cr8JSL_O.js";
|
|
7
6
|
import { t as writeFileAtomic } from "./atomic-write-B7zr1ddF.js";
|
|
8
|
-
import { o as discoverSkills } from "./interpolate-BtIgcCuz.js";
|
|
9
7
|
import { r as formatTokenUsage } from "./stats-DAKBEKjc.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { joinSystemPrompt } from "./prompt.js";
|
|
9
|
+
import { a as normalizeMcpServers, n as connectMcpServers } from "./mcp-CcvuVXB9.js";
|
|
10
|
+
import { i as findActiveTriggers, n as collectReferences, o as mergeSuggestions, t as applyInsert } from "./completion-core-CXYSVhZo.js";
|
|
12
11
|
import { r as fetchUrl } from "./fetch-url-CWE8X5OD.js";
|
|
13
12
|
import { webSearch } from "./tools/web-search.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
13
|
+
import { C as restoreModelOptions, l as BUILTIN_PROVIDERS, n as applyApiKeyEnv, r as credentialsPath, t as detectAuth, v as modelSupportsReasoning, y as modelsForDescriptor } from "./auth-BDSu_0t3.js";
|
|
14
|
+
import { H as extractEditPayload, Z as parseEditOutcomesFromResult, p as ownerOf, rt as chatReferenceTokensFromText, u as capTranscriptEvents } from "./turn-operations-Dq9Kx7m2.js";
|
|
16
15
|
import { createRequire } from "node:module";
|
|
17
16
|
import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
18
17
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
19
|
-
import { chmodSync, createReadStream, createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, renameSync, rmSync,
|
|
18
|
+
import { chmodSync, createReadStream, createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
20
19
|
import { readdir, stat, writeFile } from "node:fs/promises";
|
|
21
20
|
import { spawn } from "node:child_process";
|
|
22
|
-
import { createHash } from "node:crypto";
|
|
23
21
|
import { homedir, tmpdir } from "node:os";
|
|
22
|
+
import { createHash } from "node:crypto";
|
|
24
23
|
import { createGunzip } from "node:zlib";
|
|
25
24
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
26
25
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -201,7 +200,7 @@ function eventsFromTurns(turns, runs = []) {
|
|
|
201
200
|
}
|
|
202
201
|
}
|
|
203
202
|
const pending = pendingToolCallsPerRun.get(rid) ?? /* @__PURE__ */ new Set();
|
|
204
|
-
for (const block of turn.content) if (block.type === "tool_call") pending.add(block.id);
|
|
203
|
+
for (const block of turn.content) if (block.type === "tool_call" && /spawn/i.test(block.name)) pending.add(block.id);
|
|
205
204
|
else if (block.type === "tool_result") pending.delete(block.callId);
|
|
206
205
|
pendingToolCallsPerRun.set(rid, pending);
|
|
207
206
|
}
|
|
@@ -311,7 +310,21 @@ function eventsFromTurns(turns, runs = []) {
|
|
|
311
310
|
size: sibling.text.length
|
|
312
311
|
});
|
|
313
312
|
}
|
|
314
|
-
|
|
313
|
+
const commandOrigin = turn.runId ? runById.get(turn.runId)?.origin : void 0;
|
|
314
|
+
const commandDisplay = commandOrigin?.kind === "command" ? commandOrigin.display : void 0;
|
|
315
|
+
if (commandDisplay) {
|
|
316
|
+
events.push({
|
|
317
|
+
kind: "user-prompt",
|
|
318
|
+
text: commandDisplay,
|
|
319
|
+
turnId: turn.id
|
|
320
|
+
});
|
|
321
|
+
events.push({
|
|
322
|
+
kind: "command-detail",
|
|
323
|
+
label: "prompt",
|
|
324
|
+
text: block.text,
|
|
325
|
+
...tag
|
|
326
|
+
});
|
|
327
|
+
} else events.push({
|
|
315
328
|
kind: "user-prompt",
|
|
316
329
|
text: block.text,
|
|
317
330
|
turnId: turn.id,
|
|
@@ -333,6 +346,46 @@ function eventsFromTurns(turns, runs = []) {
|
|
|
333
346
|
lastDepthAtEmission = depth;
|
|
334
347
|
continue;
|
|
335
348
|
}
|
|
349
|
+
if (turn.role === "command") {
|
|
350
|
+
if (lastDepthAtEmission === 0) events.push({
|
|
351
|
+
kind: "separator",
|
|
352
|
+
text: ""
|
|
353
|
+
});
|
|
354
|
+
for (const block of turn.content) if (block.type === "text" && block.text.trim()) events.push({
|
|
355
|
+
kind: "markdown",
|
|
356
|
+
text: block.text,
|
|
357
|
+
streaming: false,
|
|
358
|
+
...tag
|
|
359
|
+
});
|
|
360
|
+
else if (block.type === "tool_result") {
|
|
361
|
+
const meta = block.metadata?.command;
|
|
362
|
+
const commandMeta = meta && typeof meta === "object" && !Array.isArray(meta) ? meta : void 0;
|
|
363
|
+
const customMeta = commandMeta?.custom;
|
|
364
|
+
if (customMeta && typeof customMeta === "object" && !Array.isArray(customMeta) && typeof customMeta.renderer === "string") {
|
|
365
|
+
const bound = customMeta;
|
|
366
|
+
events.push({
|
|
367
|
+
kind: "custom",
|
|
368
|
+
text: toolResultText(block.output),
|
|
369
|
+
custom: {
|
|
370
|
+
renderer: bound.renderer,
|
|
371
|
+
...bound.data !== void 0 ? { data: bound.data } : {}
|
|
372
|
+
},
|
|
373
|
+
...tag
|
|
374
|
+
});
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
const label = typeof commandMeta?.label === "string" ? commandMeta.label : void 0;
|
|
378
|
+
events.push({
|
|
379
|
+
kind: "command-detail",
|
|
380
|
+
text: toolResultText(block.output),
|
|
381
|
+
...label ? { label } : {},
|
|
382
|
+
...commandMeta?.syntax === "diff" ? { syntax: "diff" } : {},
|
|
383
|
+
...tag
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
lastDepthAtEmission = depth;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
336
389
|
if (turn.role === "assistant") {
|
|
337
390
|
for (const block of turn.content) if (block.type === "text" && block.text.trim()) events.push({
|
|
338
391
|
kind: "markdown",
|
|
@@ -559,12 +612,14 @@ const MARGIN_TOP = {
|
|
|
559
612
|
"thinking": 1,
|
|
560
613
|
"tool": 1,
|
|
561
614
|
"tool-result": 0,
|
|
615
|
+
"command-detail": 1,
|
|
562
616
|
"error": 1,
|
|
563
617
|
"markdown": 1,
|
|
564
618
|
"spawn-start": 1,
|
|
565
619
|
"spawn-end": 0,
|
|
566
620
|
"compact-summary": 1,
|
|
567
|
-
"task-notification": 1
|
|
621
|
+
"task-notification": 1,
|
|
622
|
+
"custom": 1
|
|
568
623
|
};
|
|
569
624
|
const TOOL_KINDS = new Set(["tool", "tool-result"]);
|
|
570
625
|
/**
|
|
@@ -1918,62 +1973,6 @@ function singleAgentRegistry(preset) {
|
|
|
1918
1973
|
} };
|
|
1919
1974
|
}
|
|
1920
1975
|
//#endregion
|
|
1921
|
-
//#region src/chat/project-root.ts
|
|
1922
|
-
/**
|
|
1923
|
-
* Git root detection — walks parents from `cwd` looking for a `.git`
|
|
1924
|
-
* entry, returning the absolute path of the repo root or `null` when
|
|
1925
|
-
* the search reaches the filesystem root.
|
|
1926
|
-
*
|
|
1927
|
-
* Why parent-walk over `git rev-parse --show-toplevel`:
|
|
1928
|
-
*
|
|
1929
|
-
* - Zero dependencies (no shell-out, no git binary requirement,
|
|
1930
|
-
* works even when `git` is missing from `PATH`).
|
|
1931
|
-
* - Pure-sync, deterministic, easy to test against tmp dirs.
|
|
1932
|
-
* - Equally fast in practice — most lookups are a single `existsSync`.
|
|
1933
|
-
*
|
|
1934
|
-
* Recognizes `.git` as either:
|
|
1935
|
-
*
|
|
1936
|
-
* - A directory (standard repo layout).
|
|
1937
|
-
* - A file containing `gitdir: …` (worktrees + submodules).
|
|
1938
|
-
*
|
|
1939
|
-
* Bare repos (no working tree) are intentionally NOT detected here —
|
|
1940
|
-
* the TUI's data dir contract is "project working tree", and a bare
|
|
1941
|
-
* repo has no working files to scope sessions against.
|
|
1942
|
-
*/
|
|
1943
|
-
/**
|
|
1944
|
-
* Walk parents of `cwd` looking for a `.git` entry. Returns the
|
|
1945
|
-
* absolute path of the directory containing `.git`, or `null` when no
|
|
1946
|
-
* git repo is found above `cwd`.
|
|
1947
|
-
*
|
|
1948
|
-
* Stops at the filesystem root (`/`) — no infinite loop on `dirname`'s
|
|
1949
|
-
* idempotent root case (`dirname('/')` returns `/`).
|
|
1950
|
-
*/
|
|
1951
|
-
function findGitRoot$1(cwd = process.cwd()) {
|
|
1952
|
-
let dir = resolve(cwd);
|
|
1953
|
-
for (let depth = 0; depth < 64; depth++) {
|
|
1954
|
-
if (hasGitMarker(dir)) return dir;
|
|
1955
|
-
const parent = dirname(dir);
|
|
1956
|
-
if (parent === dir) return null;
|
|
1957
|
-
dir = parent;
|
|
1958
|
-
}
|
|
1959
|
-
return null;
|
|
1960
|
-
}
|
|
1961
|
-
/**
|
|
1962
|
-
* `.git` exists as a directory (standard repo) OR as a file containing
|
|
1963
|
-
* a `gitdir:` pointer (worktree / submodule). Tolerant on stat failures
|
|
1964
|
-
* — a permission error on a parent directory shouldn't crash the walk.
|
|
1965
|
-
*/
|
|
1966
|
-
function hasGitMarker(dir) {
|
|
1967
|
-
const candidate = resolve(dir, ".git");
|
|
1968
|
-
try {
|
|
1969
|
-
if (!existsSync(candidate)) return false;
|
|
1970
|
-
const stat = statSync(candidate);
|
|
1971
|
-
return stat.isDirectory() || stat.isFile();
|
|
1972
|
-
} catch {
|
|
1973
|
-
return false;
|
|
1974
|
-
}
|
|
1975
|
-
}
|
|
1976
|
-
//#endregion
|
|
1977
1976
|
//#region src/chat/agents-md.ts
|
|
1978
1977
|
/**
|
|
1979
1978
|
* Personal instructions discovery — load user + project `AGENTS.md` /
|
|
@@ -2155,7 +2154,11 @@ function chatAutoCompactBehavior(opts) {
|
|
|
2155
2154
|
const contextWindow = typeof opts.rawContextWindow === "number" && Number.isFinite(opts.rawContextWindow) && opts.rawContextWindow > 0 ? opts.rawContextWindow : null;
|
|
2156
2155
|
return {
|
|
2157
2156
|
threshold: opts.threshold,
|
|
2158
|
-
...contextWindow ? { contextWindow } : {}
|
|
2157
|
+
...contextWindow ? { contextWindow } : {},
|
|
2158
|
+
...opts.summarizer ? {
|
|
2159
|
+
provider: opts.summarizer.provider,
|
|
2160
|
+
model: opts.summarizer.model
|
|
2161
|
+
} : {}
|
|
2159
2162
|
};
|
|
2160
2163
|
}
|
|
2161
2164
|
//#endregion
|
|
@@ -3061,17 +3064,22 @@ function useCompletion(input, providers, options = {}) {
|
|
|
3061
3064
|
const { text, cursor } = input;
|
|
3062
3065
|
const { maxQueryLength } = options;
|
|
3063
3066
|
const [dismissedTriggerStart, setDismissedTriggerStart] = useState(null);
|
|
3064
|
-
const rawTrigger = useMemo(() =>
|
|
3067
|
+
const rawTrigger = useMemo(() => findActiveTriggers(text, cursor, providers, { maxQueryLength }), [
|
|
3065
3068
|
text,
|
|
3066
3069
|
cursor,
|
|
3067
3070
|
providers,
|
|
3068
3071
|
maxQueryLength
|
|
3069
3072
|
]);
|
|
3070
|
-
const
|
|
3073
|
+
const activeMulti = useMemo(() => {
|
|
3071
3074
|
if (!rawTrigger) return null;
|
|
3072
3075
|
if (rawTrigger.span.start === dismissedTriggerStart) return null;
|
|
3073
3076
|
return rawTrigger;
|
|
3074
3077
|
}, [rawTrigger, dismissedTriggerStart]);
|
|
3078
|
+
const active = useMemo(() => activeMulti ? {
|
|
3079
|
+
provider: activeMulti.providers[0],
|
|
3080
|
+
query: activeMulti.query,
|
|
3081
|
+
span: activeMulti.span
|
|
3082
|
+
} : null, [activeMulti]);
|
|
3075
3083
|
useEffect(() => {
|
|
3076
3084
|
if (!rawTrigger) setDismissedTriggerStart(null);
|
|
3077
3085
|
}, [rawTrigger]);
|
|
@@ -3081,7 +3089,7 @@ function useCompletion(input, providers, options = {}) {
|
|
|
3081
3089
|
const abortRef = useRef(null);
|
|
3082
3090
|
useEffect(() => {
|
|
3083
3091
|
abortRef.current?.abort();
|
|
3084
|
-
if (!
|
|
3092
|
+
if (!activeMulti) {
|
|
3085
3093
|
setItems([]);
|
|
3086
3094
|
setLoading(false);
|
|
3087
3095
|
setSelectedIndex(0);
|
|
@@ -3094,30 +3102,35 @@ function useCompletion(input, providers, options = {}) {
|
|
|
3094
3102
|
cursor
|
|
3095
3103
|
};
|
|
3096
3104
|
let cancelled = false;
|
|
3097
|
-
const
|
|
3098
|
-
|
|
3099
|
-
|
|
3105
|
+
const outs = activeMulti.providers.map((provider) => ({
|
|
3106
|
+
provider,
|
|
3107
|
+
out: provider.suggest(activeMulti.query, ctx, controller.signal)
|
|
3108
|
+
}));
|
|
3109
|
+
if (outs.every((o) => Array.isArray(o.out))) {
|
|
3110
|
+
setItems(mergeSuggestions(outs.map((o) => ({
|
|
3111
|
+
provider: o.provider,
|
|
3112
|
+
items: o.out
|
|
3113
|
+
}))));
|
|
3100
3114
|
setSelectedIndex(0);
|
|
3101
3115
|
setLoading(false);
|
|
3102
3116
|
return;
|
|
3103
3117
|
}
|
|
3104
3118
|
setLoading(true);
|
|
3105
|
-
|
|
3119
|
+
Promise.allSettled(outs.map(async (o) => ({
|
|
3120
|
+
provider: o.provider,
|
|
3121
|
+
items: await o.out
|
|
3122
|
+
}))).then((results) => {
|
|
3106
3123
|
if (cancelled) return;
|
|
3107
|
-
setItems(
|
|
3124
|
+
setItems(mergeSuggestions(results.flatMap((r) => r.status === "fulfilled" ? [r.value] : [])));
|
|
3108
3125
|
setSelectedIndex(0);
|
|
3109
3126
|
setLoading(false);
|
|
3110
|
-
}, () => {
|
|
3111
|
-
if (cancelled) return;
|
|
3112
|
-
setItems([]);
|
|
3113
|
-
setLoading(false);
|
|
3114
3127
|
});
|
|
3115
3128
|
return () => {
|
|
3116
3129
|
cancelled = true;
|
|
3117
3130
|
controller.abort();
|
|
3118
3131
|
};
|
|
3119
3132
|
}, [
|
|
3120
|
-
|
|
3133
|
+
activeMulti,
|
|
3121
3134
|
text,
|
|
3122
3135
|
cursor
|
|
3123
3136
|
]);
|
|
@@ -3167,538 +3180,6 @@ function useCompletion(input, providers, options = {}) {
|
|
|
3167
3180
|
]);
|
|
3168
3181
|
}
|
|
3169
3182
|
//#endregion
|
|
3170
|
-
//#region src/chat/keybindings.ts
|
|
3171
|
-
/**
|
|
3172
|
-
* Keybindings — a single source of truth for the app's global shortcuts.
|
|
3173
|
-
*
|
|
3174
|
-
* Every customizable action is declared once in {@link KEYBINDING_DEFS}
|
|
3175
|
-
* with its default trigger, label, and description. The TUI imports
|
|
3176
|
-
* `DEFAULT_KEYBINDINGS` for the baked-in defaults; on launch we load
|
|
3177
|
-
* `<userDir>/keybindings.json` (when present) and merge user overrides
|
|
3178
|
-
* on top so the user can rebind any action without recompiling.
|
|
3179
|
-
*
|
|
3180
|
-
* Scope (deliberate):
|
|
3181
|
-
*
|
|
3182
|
-
* - We expose only the **global, action-opening** shortcuts here —
|
|
3183
|
-
* "open the Settings modal", "cycle agent profile", "enter
|
|
3184
|
-
* select-turn mode", etc. Modal-internal letter shortcuts (`f`
|
|
3185
|
-
* fork, `d` delete, `c` copy on the turn-details modal) and
|
|
3186
|
-
* universal navigation (`↑/↓/↵/⎋`) stay hardcoded — they live
|
|
3187
|
-
* too close to a renderable's input contract to remap safely.
|
|
3188
|
-
*
|
|
3189
|
-
* - Storage lives at the **user level** (`<userDir>/keybindings.json`),
|
|
3190
|
-
* never inside a project's `.{prefix}/`. Shortcut muscle memory is
|
|
3191
|
-
* a per-user concern, not a per-repo one.
|
|
3192
|
-
*
|
|
3193
|
-
* Format on disk: JSON with `//` and `/* … *\/` comments allowed
|
|
3194
|
-
* (parsed via a small JSONC stripper). Each entry maps an action id
|
|
3195
|
-
* to a binding spec string. Specs are parsed by {@link parseBindingSpec};
|
|
3196
|
-
* unknown / malformed entries fall back to the default for that action
|
|
3197
|
-
* (with a debug log under `ZIDANE_DEBUG`) so a syntax slip never bricks
|
|
3198
|
-
* the TUI.
|
|
3199
|
-
*/
|
|
3200
|
-
/**
|
|
3201
|
-
* The canonical action catalog. Order is the source of truth for the
|
|
3202
|
-
* generated keybindings file and the settings UI.
|
|
3203
|
-
*/
|
|
3204
|
-
const KEYBINDING_DEFS = [
|
|
3205
|
-
{
|
|
3206
|
-
action: "openSettings",
|
|
3207
|
-
default: "ctrl+o",
|
|
3208
|
-
label: "settings",
|
|
3209
|
-
description: "open the Settings modal (toggles, theme, keybindings)",
|
|
3210
|
-
group: "Global"
|
|
3211
|
-
},
|
|
3212
|
-
{
|
|
3213
|
-
action: "openSessionDetails",
|
|
3214
|
-
default: "ctrl+shift+x",
|
|
3215
|
-
label: "session",
|
|
3216
|
-
description: "open the session details modal (stats, export, delete, rename)",
|
|
3217
|
-
group: "Global"
|
|
3218
|
-
},
|
|
3219
|
-
{
|
|
3220
|
-
action: "newSession",
|
|
3221
|
-
default: "ctrl+n",
|
|
3222
|
-
label: "new session",
|
|
3223
|
-
description: "start a fresh chat session with the active provider/model (same as the \"+ new session\" row)",
|
|
3224
|
-
group: "Global"
|
|
3225
|
-
},
|
|
3226
|
-
{
|
|
3227
|
-
action: "openModelPicker",
|
|
3228
|
-
default: "ctrl+m",
|
|
3229
|
-
label: "model",
|
|
3230
|
-
description: "open the cross-provider model picker",
|
|
3231
|
-
group: "Global"
|
|
3232
|
-
},
|
|
3233
|
-
{
|
|
3234
|
-
action: "openEffortPicker",
|
|
3235
|
-
default: "ctrl+l",
|
|
3236
|
-
label: "options",
|
|
3237
|
-
description: "open the model-options picker — reasoning effort + feature toggles (e.g. fast mode)",
|
|
3238
|
-
group: "Global"
|
|
3239
|
-
},
|
|
3240
|
-
{
|
|
3241
|
-
action: "openTodos",
|
|
3242
|
-
default: "ctrl+t",
|
|
3243
|
-
label: "todos",
|
|
3244
|
-
description: "open the active run's todo list (the agent's `todowrite` checkpoints)",
|
|
3245
|
-
group: "Global"
|
|
3246
|
-
},
|
|
3247
|
-
{
|
|
3248
|
-
action: "openContextPanel",
|
|
3249
|
-
default: "ctrl+shift+g",
|
|
3250
|
-
label: "context",
|
|
3251
|
-
description: "open the context-usage breakdown (what is taking up the context window)",
|
|
3252
|
-
group: "Global"
|
|
3253
|
-
},
|
|
3254
|
-
{
|
|
3255
|
-
action: "openKeybindings",
|
|
3256
|
-
default: "ctrl+y",
|
|
3257
|
-
label: "keybindings",
|
|
3258
|
-
description: "open the keybindings panel — list every shortcut and jump to the keybindings.json file",
|
|
3259
|
-
group: "Global"
|
|
3260
|
-
},
|
|
3261
|
-
{
|
|
3262
|
-
action: "cycleAgent",
|
|
3263
|
-
default: "shift+tab",
|
|
3264
|
-
label: "cycle agent",
|
|
3265
|
-
description: "cycle to the next agent profile (when multiple profiles are registered)",
|
|
3266
|
-
group: "Global"
|
|
3267
|
-
},
|
|
3268
|
-
{
|
|
3269
|
-
action: "enterSelectTurnMode",
|
|
3270
|
-
default: "ctrl+s",
|
|
3271
|
-
label: "messages",
|
|
3272
|
-
description: "enter select-turn mode to navigate previous messages",
|
|
3273
|
-
group: "Global"
|
|
3274
|
-
},
|
|
3275
|
-
{
|
|
3276
|
-
action: "cancelToolCall",
|
|
3277
|
-
default: "ctrl+k",
|
|
3278
|
-
label: "cancel tool",
|
|
3279
|
-
description: "open the in-flight tool picker to cancel a single tool call without aborting the run (esc still aborts the whole run)",
|
|
3280
|
-
group: "Global"
|
|
3281
|
-
},
|
|
3282
|
-
{
|
|
3283
|
-
action: "changeCwd",
|
|
3284
|
-
default: "ctrl+h",
|
|
3285
|
-
label: "cwd",
|
|
3286
|
-
description: "open the directory picker to change the working directory",
|
|
3287
|
-
group: "Global"
|
|
3288
|
-
},
|
|
3289
|
-
{
|
|
3290
|
-
action: "attachClipboardImage",
|
|
3291
|
-
default: "ctrl+v",
|
|
3292
|
-
label: "paste image",
|
|
3293
|
-
description: "attach an image from the OS clipboard to the prompt (needs a platform helper: pngpaste on macOS, wl-clipboard/xclip on Linux)",
|
|
3294
|
-
group: "Global"
|
|
3295
|
-
},
|
|
3296
|
-
{
|
|
3297
|
-
action: "openExternalEditor",
|
|
3298
|
-
default: "ctrl+g",
|
|
3299
|
-
label: "edit in $EDITOR",
|
|
3300
|
-
description: "open the current prompt in $EDITOR / $VISUAL, reloading it when you save & quit (git-style); requires the \"Edit prompt in $EDITOR\" setting",
|
|
3301
|
-
group: "Global"
|
|
3302
|
-
},
|
|
3303
|
-
{
|
|
3304
|
-
action: "enterQueueSelection",
|
|
3305
|
-
default: "",
|
|
3306
|
-
label: "select queue",
|
|
3307
|
-
description: "move focus from the prompt textarea into the type-ahead queue box (also: ↑ on an empty prompt)",
|
|
3308
|
-
group: "Message queue"
|
|
3309
|
-
},
|
|
3310
|
-
{
|
|
3311
|
-
action: "pushQueuedMessage",
|
|
3312
|
-
default: "ctrl+return",
|
|
3313
|
-
label: "push",
|
|
3314
|
-
description: "steer the selected queued message into the live run (delivered between tool calls)",
|
|
3315
|
-
group: "Message queue"
|
|
3316
|
-
},
|
|
3317
|
-
{
|
|
3318
|
-
action: "dropQueuedMessage",
|
|
3319
|
-
default: "backspace",
|
|
3320
|
-
label: "drop",
|
|
3321
|
-
description: "remove the selected queued message — exits back to the prompt when the queue empties (the big \"remove\" key works everywhere: backspace on Win/Linux, the laptop key labeled \"delete\" on Mac, which sends backspace)",
|
|
3322
|
-
group: "Message queue"
|
|
3323
|
-
},
|
|
3324
|
-
{
|
|
3325
|
-
action: "turnFork",
|
|
3326
|
-
default: "f",
|
|
3327
|
-
label: "fork",
|
|
3328
|
-
description: "fork the session at the selected turn (two-press confirm)",
|
|
3329
|
-
group: "Turn details"
|
|
3330
|
-
},
|
|
3331
|
-
{
|
|
3332
|
-
action: "turnDelete",
|
|
3333
|
-
default: "d",
|
|
3334
|
-
label: "delete",
|
|
3335
|
-
description: "delete the selected turn (two-press confirm)",
|
|
3336
|
-
group: "Turn details"
|
|
3337
|
-
},
|
|
3338
|
-
{
|
|
3339
|
-
action: "turnCopy",
|
|
3340
|
-
default: "c",
|
|
3341
|
-
label: "copy",
|
|
3342
|
-
description: "copy the selected turn content to the clipboard (OSC 52)",
|
|
3343
|
-
group: "Turn details"
|
|
3344
|
-
},
|
|
3345
|
-
{
|
|
3346
|
-
action: "turnEdit",
|
|
3347
|
-
default: "e",
|
|
3348
|
-
label: "edit",
|
|
3349
|
-
description: "edit the text content of the selected turn",
|
|
3350
|
-
group: "Turn details"
|
|
3351
|
-
},
|
|
3352
|
-
{
|
|
3353
|
-
action: "sessionDelete",
|
|
3354
|
-
default: "d",
|
|
3355
|
-
label: "delete",
|
|
3356
|
-
description: "delete the entire session (two-press confirm)",
|
|
3357
|
-
group: "Session details"
|
|
3358
|
-
},
|
|
3359
|
-
{
|
|
3360
|
-
action: "sessionCopyId",
|
|
3361
|
-
default: "c",
|
|
3362
|
-
label: "copy id",
|
|
3363
|
-
description: "copy the full session id to the clipboard (OSC 52)",
|
|
3364
|
-
group: "Session details"
|
|
3365
|
-
},
|
|
3366
|
-
{
|
|
3367
|
-
action: "sessionGenerateTitle",
|
|
3368
|
-
default: "g",
|
|
3369
|
-
label: "generate title",
|
|
3370
|
-
description: "generate a title for the session via the active provider/model",
|
|
3371
|
-
group: "Session details"
|
|
3372
|
-
},
|
|
3373
|
-
{
|
|
3374
|
-
action: "sessionExportMarkdown",
|
|
3375
|
-
default: "e",
|
|
3376
|
-
label: "export md",
|
|
3377
|
-
description: "export the session as Markdown under .{prefix}/sessions/",
|
|
3378
|
-
group: "Session details"
|
|
3379
|
-
},
|
|
3380
|
-
{
|
|
3381
|
-
action: "sessionExportJson",
|
|
3382
|
-
default: "j",
|
|
3383
|
-
label: "export json",
|
|
3384
|
-
description: "export the session as JSON under .{prefix}/sessions/",
|
|
3385
|
-
group: "Session details"
|
|
3386
|
-
},
|
|
3387
|
-
{
|
|
3388
|
-
action: "sessionCompact",
|
|
3389
|
-
default: "k",
|
|
3390
|
-
label: "compact",
|
|
3391
|
-
description: "compact older turns via an LLM summary (model still sees everything from the boundary down)",
|
|
3392
|
-
group: "Session details"
|
|
3393
|
-
}
|
|
3394
|
-
];
|
|
3395
|
-
/** Index by action for O(1) lookup of label / description / default. */
|
|
3396
|
-
const KEYBINDING_DEF_BY_ACTION = KEYBINDING_DEFS.reduce((acc, def) => Object.assign(acc, { [def.action]: def }), {});
|
|
3397
|
-
/** Defaults, derived from {@link KEYBINDING_DEFS} so the two never drift. */
|
|
3398
|
-
const DEFAULT_KEYBINDINGS = KEYBINDING_DEFS.reduce((acc, def) => Object.assign(acc, { [def.action]: def.default }), {});
|
|
3399
|
-
/**
|
|
3400
|
-
* Aliases the parser accepts on input so the on-disk format can be
|
|
3401
|
-
* forgiving (different terminals + docs label modifiers + keys
|
|
3402
|
-
* differently). Output names are canonicalized to one form per key so
|
|
3403
|
-
* `matchesBinding` only has to compare one canonical against a
|
|
3404
|
-
* KeyEvent's `name`. Unknown names pass through verbatim — that's
|
|
3405
|
-
* useful for terminal-specific keys we don't have to enumerate.
|
|
3406
|
-
*/
|
|
3407
|
-
const NAME_ALIASES = {
|
|
3408
|
-
esc: "escape",
|
|
3409
|
-
enter: "return",
|
|
3410
|
-
ret: "return",
|
|
3411
|
-
space: "space",
|
|
3412
|
-
spc: "space",
|
|
3413
|
-
del: "delete",
|
|
3414
|
-
ins: "insert",
|
|
3415
|
-
pgup: "pageup",
|
|
3416
|
-
pgdn: "pagedown",
|
|
3417
|
-
pagup: "pageup",
|
|
3418
|
-
pagedn: "pagedown"
|
|
3419
|
-
};
|
|
3420
|
-
const MODIFIER_ALIASES = {
|
|
3421
|
-
ctrl: "ctrl",
|
|
3422
|
-
control: "ctrl",
|
|
3423
|
-
shift: "shift",
|
|
3424
|
-
alt: "alt",
|
|
3425
|
-
option: "alt",
|
|
3426
|
-
opt: "alt",
|
|
3427
|
-
meta: "meta",
|
|
3428
|
-
cmd: "meta",
|
|
3429
|
-
command: "meta",
|
|
3430
|
-
super: "meta",
|
|
3431
|
-
win: "meta"
|
|
3432
|
-
};
|
|
3433
|
-
/**
|
|
3434
|
-
* Parse a `'ctrl+shift+o'`-style spec into structured fields. Returns
|
|
3435
|
-
* `null` for empty / malformed inputs; the caller falls back to the
|
|
3436
|
-
* default for that action.
|
|
3437
|
-
*
|
|
3438
|
-
* Tolerant by design:
|
|
3439
|
-
* - Case-insensitive (`Ctrl+O` → same as `ctrl+o`).
|
|
3440
|
-
* - Modifier order doesn't matter (`shift+ctrl+o` works).
|
|
3441
|
-
* - Mixed `+` and `-` separators allowed (`ctrl-o`, `ctrl+o`).
|
|
3442
|
-
* - Trailing / leading whitespace is stripped.
|
|
3443
|
-
* - Duplicate modifiers collapse silently.
|
|
3444
|
-
*/
|
|
3445
|
-
function parseBindingSpec(spec) {
|
|
3446
|
-
if (typeof spec !== "string") return null;
|
|
3447
|
-
const trimmed = spec.trim().toLowerCase();
|
|
3448
|
-
if (!trimmed) return null;
|
|
3449
|
-
const parts = trimmed.split(/[+\-]/).map((p) => p.trim()).filter(Boolean);
|
|
3450
|
-
if (parts.length === 0) return null;
|
|
3451
|
-
const result = {
|
|
3452
|
-
ctrl: false,
|
|
3453
|
-
shift: false,
|
|
3454
|
-
alt: false,
|
|
3455
|
-
meta: false,
|
|
3456
|
-
name: ""
|
|
3457
|
-
};
|
|
3458
|
-
for (const part of parts) {
|
|
3459
|
-
const modifier = MODIFIER_ALIASES[part];
|
|
3460
|
-
if (modifier) {
|
|
3461
|
-
result[modifier] = true;
|
|
3462
|
-
continue;
|
|
3463
|
-
}
|
|
3464
|
-
if (result.name) return null;
|
|
3465
|
-
result.name = NAME_ALIASES[part] ?? part;
|
|
3466
|
-
}
|
|
3467
|
-
if (!result.name) return null;
|
|
3468
|
-
return result;
|
|
3469
|
-
}
|
|
3470
|
-
/**
|
|
3471
|
-
* Test a KeyEvent against a parsed-or-string binding. Accepts either
|
|
3472
|
-
* form so callers can pre-parse (hot path) or pass the raw spec for
|
|
3473
|
-
* one-off checks.
|
|
3474
|
-
*
|
|
3475
|
-
* Modifier semantics: ALL declared modifiers must be pressed AND no
|
|
3476
|
-
* extra modifiers may be pressed. So `'o'` does NOT match `ctrl+o`,
|
|
3477
|
-
* and `'ctrl+o'` does NOT match `ctrl+shift+o`. This avoids surprises
|
|
3478
|
-
* where a binding that should be specific accidentally fires for a
|
|
3479
|
-
* superset of key combinations.
|
|
3480
|
-
*/
|
|
3481
|
-
function matchesBinding(event, spec) {
|
|
3482
|
-
const parsed = typeof spec === "string" ? parseBindingSpec(spec) : spec;
|
|
3483
|
-
if (!parsed) return false;
|
|
3484
|
-
const altPressed = !!event.option || !!event.alt;
|
|
3485
|
-
if (parsed.ctrl !== !!event.ctrl) return false;
|
|
3486
|
-
if (parsed.shift !== !!event.shift) return false;
|
|
3487
|
-
if (parsed.alt !== altPressed) return false;
|
|
3488
|
-
if (parsed.meta !== !!event.meta) return false;
|
|
3489
|
-
if ((event.name ?? "").toLowerCase() !== parsed.name) return false;
|
|
3490
|
-
return true;
|
|
3491
|
-
}
|
|
3492
|
-
/** Verbose key names → compact single-cell glyphs used by hint rows. */
|
|
3493
|
-
const KEY_GLYPHS = {
|
|
3494
|
-
up: "↑",
|
|
3495
|
-
down: "↓",
|
|
3496
|
-
left: "←",
|
|
3497
|
-
right: "→",
|
|
3498
|
-
return: "↵",
|
|
3499
|
-
enter: "↵",
|
|
3500
|
-
delete: "⌫",
|
|
3501
|
-
backspace: "⌫",
|
|
3502
|
-
escape: "esc",
|
|
3503
|
-
space: "␣",
|
|
3504
|
-
tab: "⇥"
|
|
3505
|
-
};
|
|
3506
|
-
/**
|
|
3507
|
-
* Render a binding spec (`"ctrl+return"`, `"backspace"`, `"delete"`) as a
|
|
3508
|
-
* compact display string the user recognizes at a glance. Substitutes
|
|
3509
|
-
* arrow / enter / backspace glyphs for their verbose names so the hint
|
|
3510
|
-
* row stays narrow; modifier names + plain keys pass through unchanged.
|
|
3511
|
-
*
|
|
3512
|
-
* Empty / missing specs return an empty string — callers decide whether
|
|
3513
|
-
* to render a placeholder (e.g. `'—'` for an unbound action in the
|
|
3514
|
-
* keybindings panel) or hide the surface entirely (e.g. hint rows that
|
|
3515
|
-
* fall back to a different chord when the binding is empty).
|
|
3516
|
-
*/
|
|
3517
|
-
function formatBindingForDisplay(spec) {
|
|
3518
|
-
if (!spec) return "";
|
|
3519
|
-
const segments = spec.toLowerCase().split("+");
|
|
3520
|
-
const key = segments.pop() ?? "";
|
|
3521
|
-
const modifiers = segments.join("+");
|
|
3522
|
-
const glyph = KEY_GLYPHS[key] ?? key;
|
|
3523
|
-
return modifiers ? `${modifiers}+${glyph}` : glyph;
|
|
3524
|
-
}
|
|
3525
|
-
/**
|
|
3526
|
-
* Merge a partial map of user overrides into the defaults. Unknown
|
|
3527
|
-
* action keys are dropped (a future TUI version may have retired the
|
|
3528
|
-
* action); invalid spec strings (parse fails) fall back to the default
|
|
3529
|
-
* for that action.
|
|
3530
|
-
*/
|
|
3531
|
-
function mergeKeybindings(overrides) {
|
|
3532
|
-
if (!overrides || typeof overrides !== "object") return { ...DEFAULT_KEYBINDINGS };
|
|
3533
|
-
const merged = { ...DEFAULT_KEYBINDINGS };
|
|
3534
|
-
for (const def of KEYBINDING_DEFS) {
|
|
3535
|
-
const raw = overrides[def.action];
|
|
3536
|
-
if (typeof raw !== "string") continue;
|
|
3537
|
-
if (!parseBindingSpec(raw)) continue;
|
|
3538
|
-
merged[def.action] = raw;
|
|
3539
|
-
}
|
|
3540
|
-
return merged;
|
|
3541
|
-
}
|
|
3542
|
-
/** Path of the user-level keybindings file. */
|
|
3543
|
-
function keybindingsPath(userDir) {
|
|
3544
|
-
return resolve(userDir, "keybindings.json");
|
|
3545
|
-
}
|
|
3546
|
-
/**
|
|
3547
|
-
* Walk {@link KEYBINDING_DEFS} in order and bucket rows into contiguous
|
|
3548
|
-
* sections by `group`. Shared between the standalone `KeybindingsModal`
|
|
3549
|
-
* and the `Keybindings` tab inside `SettingsModal` so both surfaces
|
|
3550
|
-
* render the same catalog with the same ordering rules.
|
|
3551
|
-
*/
|
|
3552
|
-
function groupBindings(bindings) {
|
|
3553
|
-
const sections = [];
|
|
3554
|
-
for (const def of KEYBINDING_DEFS) {
|
|
3555
|
-
const last = sections[sections.length - 1];
|
|
3556
|
-
const spec = bindings[def.action] ?? "";
|
|
3557
|
-
if (last && last.group === def.group) {
|
|
3558
|
-
last.rows.push({
|
|
3559
|
-
def,
|
|
3560
|
-
spec
|
|
3561
|
-
});
|
|
3562
|
-
continue;
|
|
3563
|
-
}
|
|
3564
|
-
sections.push({
|
|
3565
|
-
group: def.group,
|
|
3566
|
-
rows: [{
|
|
3567
|
-
def,
|
|
3568
|
-
spec
|
|
3569
|
-
}]
|
|
3570
|
-
});
|
|
3571
|
-
}
|
|
3572
|
-
return sections;
|
|
3573
|
-
}
|
|
3574
|
-
/**
|
|
3575
|
-
* Width (in cells) of the fixed key column used by every binding-row
|
|
3576
|
-
* renderer. Derived once from {@link KEYBINDING_DEFS} so a new action
|
|
3577
|
-
* with a wider default spec (`ctrl+shift+x`, …) automatically grows
|
|
3578
|
-
* the column instead of truncating the label.
|
|
3579
|
-
*/
|
|
3580
|
-
const KEYBINDING_KEY_COL_WIDTH = (() => {
|
|
3581
|
-
let max = 8;
|
|
3582
|
-
for (const def of KEYBINDING_DEFS) {
|
|
3583
|
-
const width = formatBindingForDisplay(def.default).length;
|
|
3584
|
-
if (width > max) max = width;
|
|
3585
|
-
}
|
|
3586
|
-
return max + 2;
|
|
3587
|
-
})();
|
|
3588
|
-
/**
|
|
3589
|
-
* Load + merge the user's keybindings file (when present). Always
|
|
3590
|
-
* returns a fully-resolved {@link KeyBindings} — defaults fill in for
|
|
3591
|
-
* any missing / invalid entries so consumers can treat the result as
|
|
3592
|
-
* total.
|
|
3593
|
-
*
|
|
3594
|
-
* Failure modes (all tolerated, all fall back to defaults):
|
|
3595
|
-
* - File missing
|
|
3596
|
-
* - File not valid JSON / JSONC (after comment stripping)
|
|
3597
|
-
* - Top-level is not an object
|
|
3598
|
-
* - Individual entries have invalid spec strings
|
|
3599
|
-
*/
|
|
3600
|
-
function readKeybindings(userDir) {
|
|
3601
|
-
const path = keybindingsPath(userDir);
|
|
3602
|
-
if (!existsSync(path)) return { ...DEFAULT_KEYBINDINGS };
|
|
3603
|
-
try {
|
|
3604
|
-
const raw = readFileSync(path, "utf-8");
|
|
3605
|
-
const parsed = JSON.parse(stripJsonComments(raw));
|
|
3606
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return { ...DEFAULT_KEYBINDINGS };
|
|
3607
|
-
return mergeKeybindings(parsed);
|
|
3608
|
-
} catch {
|
|
3609
|
-
return { ...DEFAULT_KEYBINDINGS };
|
|
3610
|
-
}
|
|
3611
|
-
}
|
|
3612
|
-
/**
|
|
3613
|
-
* Create the user-level `keybindings.json` if it doesn't exist,
|
|
3614
|
-
* prefilled with every default. Returns the absolute file path either
|
|
3615
|
-
* way so the caller can open it in `$EDITOR`. Idempotent: an existing
|
|
3616
|
-
* user file is NEVER overwritten — the user's customizations are
|
|
3617
|
-
* sacred.
|
|
3618
|
-
*/
|
|
3619
|
-
function ensureKeybindingsFile(userDir) {
|
|
3620
|
-
const path = keybindingsPath(userDir);
|
|
3621
|
-
if (existsSync(path)) return path;
|
|
3622
|
-
writeFileAtomic(path, renderDefaultFile(), { ensureDir: true });
|
|
3623
|
-
return path;
|
|
3624
|
-
}
|
|
3625
|
-
/**
|
|
3626
|
-
* Render the default `keybindings.json` body. JSONC with a header
|
|
3627
|
-
* explaining the format + per-action comments derived from
|
|
3628
|
-
* {@link KEYBINDING_DEFS}'s descriptions. The user sees the catalog +
|
|
3629
|
-
* defaults at a glance the moment they open the file.
|
|
3630
|
-
*/
|
|
3631
|
-
function renderDefaultFile() {
|
|
3632
|
-
const lines = [];
|
|
3633
|
-
lines.push("// Zidane keybindings — edit values below to customize.");
|
|
3634
|
-
lines.push("//");
|
|
3635
|
-
lines.push("// Format:");
|
|
3636
|
-
lines.push("// \"<action>\": \"<modifier>+<modifier>+<key>\"");
|
|
3637
|
-
lines.push("//");
|
|
3638
|
-
lines.push("// Modifiers: ctrl, shift, alt (option), meta (cmd).");
|
|
3639
|
-
lines.push("// Keys: a-z, 0-9, tab, return, escape, space, delete, …");
|
|
3640
|
-
lines.push("//");
|
|
3641
|
-
lines.push("// Comments (`//` line, `/* block */`) are stripped before parsing.");
|
|
3642
|
-
lines.push("// Restart the TUI to apply changes.");
|
|
3643
|
-
lines.push("{");
|
|
3644
|
-
KEYBINDING_DEFS.forEach((def, i) => {
|
|
3645
|
-
const trailingComma = i < KEYBINDING_DEFS.length - 1 ? "," : "";
|
|
3646
|
-
lines.push(` // ${def.description}`);
|
|
3647
|
-
lines.push(` ${JSON.stringify(def.action)}: ${JSON.stringify(def.default)}${trailingComma}`);
|
|
3648
|
-
});
|
|
3649
|
-
lines.push("}");
|
|
3650
|
-
lines.push("");
|
|
3651
|
-
return lines.join("\n");
|
|
3652
|
-
}
|
|
3653
|
-
/**
|
|
3654
|
-
* Strip `//` line comments and `/* … *\/` block comments from a JSONC
|
|
3655
|
-
* source string. Stays out of string literals so a `"foo // bar"` value
|
|
3656
|
-
* keeps its slashes. Exported for unit-tests; the only runtime caller
|
|
3657
|
-
* is {@link readKeybindings}.
|
|
3658
|
-
*
|
|
3659
|
-
* Trade-off: this is a tiny state-machine, not a full JSONC parser. It
|
|
3660
|
-
* doesn't understand `\\` outside strings (irrelevant for JSON) and
|
|
3661
|
-
* doesn't track line numbers in error messages. Good enough for a
|
|
3662
|
-
* config file — corruption in production gracefully falls back to the
|
|
3663
|
-
* defaults via the try/catch in `readKeybindings`.
|
|
3664
|
-
*/
|
|
3665
|
-
function stripJsonComments(input) {
|
|
3666
|
-
let out = "";
|
|
3667
|
-
let i = 0;
|
|
3668
|
-
let inString = false;
|
|
3669
|
-
let escape = false;
|
|
3670
|
-
while (i < input.length) {
|
|
3671
|
-
const c = input[i];
|
|
3672
|
-
if (inString) {
|
|
3673
|
-
out += c;
|
|
3674
|
-
if (escape) escape = false;
|
|
3675
|
-
else if (c === "\\") escape = true;
|
|
3676
|
-
else if (c === "\"") inString = false;
|
|
3677
|
-
i++;
|
|
3678
|
-
continue;
|
|
3679
|
-
}
|
|
3680
|
-
if (c === "\"") {
|
|
3681
|
-
inString = true;
|
|
3682
|
-
out += c;
|
|
3683
|
-
i++;
|
|
3684
|
-
continue;
|
|
3685
|
-
}
|
|
3686
|
-
if (c === "/" && input[i + 1] === "/") {
|
|
3687
|
-
while (i < input.length && input[i] !== "\n") i++;
|
|
3688
|
-
continue;
|
|
3689
|
-
}
|
|
3690
|
-
if (c === "/" && input[i + 1] === "*") {
|
|
3691
|
-
i += 2;
|
|
3692
|
-
while (i < input.length && !(input[i] === "*" && input[i + 1] === "/")) i++;
|
|
3693
|
-
i += 2;
|
|
3694
|
-
continue;
|
|
3695
|
-
}
|
|
3696
|
-
out += c;
|
|
3697
|
-
i++;
|
|
3698
|
-
}
|
|
3699
|
-
return out;
|
|
3700
|
-
}
|
|
3701
|
-
//#endregion
|
|
3702
3183
|
//#region src/chat/user-config.ts
|
|
3703
3184
|
/**
|
|
3704
3185
|
* User-level config at `~/.{prefix}/config.json`. Holds global flags
|
|
@@ -3773,9 +3254,18 @@ function resolveConfig(options) {
|
|
|
3773
3254
|
const store = typeof options.store === "function" ? options.store(paths) : options.store;
|
|
3774
3255
|
const stateStore = createStateStore(paths.state);
|
|
3775
3256
|
const initialState = stateStore.load();
|
|
3776
|
-
const
|
|
3257
|
+
const extensions = options.extensionRegistry ?? EMPTY_EXTENSION_REGISTRY;
|
|
3258
|
+
const baseProviders = options.providers ?? BUILTIN_PROVIDERS;
|
|
3259
|
+
const providers = wrapProvidersWithExtensionMiddleware(Object.keys(extensions.providers).length === 0 ? baseProviders : {
|
|
3260
|
+
...extensions.providers,
|
|
3261
|
+
...baseProviders
|
|
3262
|
+
}, extensions);
|
|
3777
3263
|
if (options.agents && options.preset) throw new Error("resolveConfig: pass either `preset` (single-agent legacy) or `agents` (multi-profile registry), not both. Migrate `preset` into an entry inside `agents` if you want both worlds.");
|
|
3778
|
-
const
|
|
3264
|
+
const baseAgents = options.agents ?? (options.preset ? singleAgentRegistry(options.preset) : BUILTIN_AGENTS);
|
|
3265
|
+
const agents = Object.keys(extensions.agents).length === 0 ? baseAgents : {
|
|
3266
|
+
...extensions.agents,
|
|
3267
|
+
...baseAgents
|
|
3268
|
+
};
|
|
3779
3269
|
if (Object.keys(agents).length === 0) throw new Error("resolveConfig: `agents` registry is empty — at least one profile is required.");
|
|
3780
3270
|
process.env.ZIDANE_CREDENTIALS_PATH = credentialsPath(paths.userDir);
|
|
3781
3271
|
applyApiKeyEnv(paths.userDir, providers);
|
|
@@ -3788,7 +3278,9 @@ function resolveConfig(options) {
|
|
|
3788
3278
|
}) : resumeProvider ? pickInitial(resumeProvider, providers, initialState) : null;
|
|
3789
3279
|
const initialAgentId = resolveAgentId(agents, initialState.lastAgent, options.defaultAgent ?? "build");
|
|
3790
3280
|
if (!initialAgentId) throw new Error("resolveConfig: failed to resolve an initial agent id from a non-empty registry.");
|
|
3791
|
-
const
|
|
3281
|
+
const rawKeybindings = readKeybindingsRaw(paths.userDir);
|
|
3282
|
+
const keybindings = mergeKeybindings(rawKeybindings);
|
|
3283
|
+
const extensionKeybindings = extensions.tuiContributions.keybindings.length === 0 ? [] : resolveExtensionKeybindings(extensions.tuiContributions.keybindings, rawKeybindings);
|
|
3792
3284
|
return {
|
|
3793
3285
|
prefix,
|
|
3794
3286
|
storageDir,
|
|
@@ -3804,7 +3296,18 @@ function resolveConfig(options) {
|
|
|
3804
3296
|
resumeProvider,
|
|
3805
3297
|
initialPicked,
|
|
3806
3298
|
keybindings,
|
|
3299
|
+
extensionKeybindings,
|
|
3807
3300
|
autoUpdate: options.autoUpdate ?? null,
|
|
3301
|
+
extensions,
|
|
3302
|
+
extensionScanPaths: options.extensionScanPaths ?? [],
|
|
3303
|
+
extensionPreset: buildExtensionPreset(extensions),
|
|
3304
|
+
extensionSystemFragments: extensions.systemFragments,
|
|
3305
|
+
extensionSubagents: extensions.subagents,
|
|
3306
|
+
extensionSubagentOverrides: extensions.subagentOverrides,
|
|
3307
|
+
extensionConfigCategories: extensions.configCategories,
|
|
3308
|
+
extensionConfigItems: extensions.configItems,
|
|
3309
|
+
trustProjectExtensions: options.trustProjectExtensions ?? process.env.ZIDANE_TRUST_PROJECT_EXTENSIONS === "1",
|
|
3310
|
+
rawKeybindings,
|
|
3808
3311
|
launch: options.launch ?? null
|
|
3809
3312
|
};
|
|
3810
3313
|
}
|
|
@@ -3924,6 +3427,33 @@ function makeModelsResolver(registry) {
|
|
|
3924
3427
|
return descriptor ? modelsForDescriptor(descriptor) : [];
|
|
3925
3428
|
};
|
|
3926
3429
|
}
|
|
3430
|
+
function wrapProvidersWithExtensionMiddleware(providers, extensions) {
|
|
3431
|
+
if (extensions.providerMiddlewares.length === 0) return providers;
|
|
3432
|
+
const out = {};
|
|
3433
|
+
for (const [key, descriptor] of Object.entries(providers)) out[key] = {
|
|
3434
|
+
...descriptor,
|
|
3435
|
+
factory: () => {
|
|
3436
|
+
let provider = descriptor.factory();
|
|
3437
|
+
for (const entry of extensions.providerMiddlewares) try {
|
|
3438
|
+
const next = entry.value(provider, {
|
|
3439
|
+
extensionName: entry.extensionName,
|
|
3440
|
+
source: entry.source,
|
|
3441
|
+
descriptor
|
|
3442
|
+
});
|
|
3443
|
+
if (isProviderLike(next)) provider = next;
|
|
3444
|
+
} catch (err) {
|
|
3445
|
+
if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/extensions] ${entry.extensionName} provider middleware failed: ${errorMessage(err)}\n`);
|
|
3446
|
+
}
|
|
3447
|
+
return provider;
|
|
3448
|
+
}
|
|
3449
|
+
};
|
|
3450
|
+
return out;
|
|
3451
|
+
}
|
|
3452
|
+
function isProviderLike(value) {
|
|
3453
|
+
if (!value || typeof value !== "object") return false;
|
|
3454
|
+
const provider = value;
|
|
3455
|
+
return typeof provider.name === "string" && !!provider.meta && typeof provider.meta === "object" && typeof provider.formatTools === "function" && typeof provider.userMessage === "function" && typeof provider.assistantMessage === "function" && typeof provider.toolResultsMessage === "function" && typeof provider.stream === "function";
|
|
3456
|
+
}
|
|
3927
3457
|
function resolveResumeProvider(state, providers, storageDir) {
|
|
3928
3458
|
if (!state.lastProvider) {
|
|
3929
3459
|
if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/chat] resume: no \`lastProvider\` in state — auth screen will show. (Has \`${storageDir}/state.json\` been deleted or never written?)\n`);
|
|
@@ -5269,8 +4799,11 @@ const BUILTIN_THEMES = {
|
|
|
5269
4799
|
[VAPORWAVE_THEME.id]: VAPORWAVE_THEME
|
|
5270
4800
|
};
|
|
5271
4801
|
/** Resolve a theme id to its full `Theme`, falling back to default on unknown ids. */
|
|
5272
|
-
function resolveTheme(id) {
|
|
5273
|
-
if (id
|
|
4802
|
+
function resolveTheme(id, extraThemes) {
|
|
4803
|
+
if (id) {
|
|
4804
|
+
if (BUILTIN_THEMES[id]) return BUILTIN_THEMES[id];
|
|
4805
|
+
if (extraThemes && extraThemes[id]) return extraThemes[id];
|
|
4806
|
+
}
|
|
5274
4807
|
return DEFAULT_THEME;
|
|
5275
4808
|
}
|
|
5276
4809
|
//#endregion
|
|
@@ -5280,6 +4813,7 @@ const DEFAULT_SETTINGS = {
|
|
|
5280
4813
|
toolCallDisplay: "formatted",
|
|
5281
4814
|
showToolResults: true,
|
|
5282
4815
|
safeMode: true,
|
|
4816
|
+
loadProjectExtensions: false,
|
|
5283
4817
|
hideSubagentOutput: true,
|
|
5284
4818
|
theme: DEFAULT_THEME.id,
|
|
5285
4819
|
showAllProjects: false,
|
|
@@ -5295,9 +4829,12 @@ const DEFAULT_SETTINGS = {
|
|
|
5295
4829
|
maxWallMinutes: 0,
|
|
5296
4830
|
showEditDiffs: true,
|
|
5297
4831
|
editDiffDisplay: "full",
|
|
4832
|
+
showCompactionSummary: false,
|
|
4833
|
+
compactionModel: "",
|
|
5298
4834
|
targetFps: 60,
|
|
5299
4835
|
allowInteraction: true,
|
|
5300
4836
|
smoothStreaming: true,
|
|
4837
|
+
fileLogging: false,
|
|
5301
4838
|
showTodoIndicator: true,
|
|
5302
4839
|
showThrobber: false,
|
|
5303
4840
|
checkForUpdates: true,
|
|
@@ -5406,6 +4943,12 @@ const SETTINGS_TOGGLES = [
|
|
|
5406
4943
|
label: "Persist large tool results",
|
|
5407
4944
|
description: "write >8 KiB tool outputs to disk and inline a preview"
|
|
5408
4945
|
},
|
|
4946
|
+
{
|
|
4947
|
+
category: "agent",
|
|
4948
|
+
key: "loadProjectExtensions",
|
|
4949
|
+
label: "Load project extensions",
|
|
4950
|
+
description: "run extensions under <project>/.zidane/extensions — off by default (they execute arbitrary local code)"
|
|
4951
|
+
},
|
|
5409
4952
|
{
|
|
5410
4953
|
category: "agent",
|
|
5411
4954
|
key: "attachmentsEnabled",
|
|
@@ -5466,6 +5009,12 @@ const SETTINGS_TOGGLES = [
|
|
|
5466
5009
|
label: "Edit diffs",
|
|
5467
5010
|
description: "render edit / multi_edit / write_file as a unified diff"
|
|
5468
5011
|
},
|
|
5012
|
+
{
|
|
5013
|
+
category: "ui",
|
|
5014
|
+
key: "showCompactionSummary",
|
|
5015
|
+
label: "Compaction summaries",
|
|
5016
|
+
description: "show the generated summary text under the ⎯⎯ compacted boundary row (off = metadata only)"
|
|
5017
|
+
},
|
|
5469
5018
|
{
|
|
5470
5019
|
category: "ui",
|
|
5471
5020
|
key: "smoothStreaming",
|
|
@@ -5483,6 +5032,12 @@ const SETTINGS_TOGGLES = [
|
|
|
5483
5032
|
key: "showThrobber",
|
|
5484
5033
|
label: "Streaming throbber",
|
|
5485
5034
|
description: "animated gradient glyphs at the transcript tail while the assistant is working"
|
|
5035
|
+
},
|
|
5036
|
+
{
|
|
5037
|
+
category: "ui",
|
|
5038
|
+
key: "fileLogging",
|
|
5039
|
+
label: "File logging",
|
|
5040
|
+
description: "persist this session's logs to ~/.zidane/logs/<datetime>.txt (the Logs tab shows them regardless)"
|
|
5486
5041
|
}
|
|
5487
5042
|
];
|
|
5488
5043
|
const SETTINGS_CHOICES = [
|
|
@@ -5834,7 +5389,7 @@ const SETTINGS_INPUTS = [{
|
|
|
5834
5389
|
*/
|
|
5835
5390
|
function useEnabledToggleSet(opts) {
|
|
5836
5391
|
const { settings, setSetting } = useSettings();
|
|
5837
|
-
const { catalog, keyOf, settingKey } = opts;
|
|
5392
|
+
const { catalog, keyOf, settingKey, gcKeys } = opts;
|
|
5838
5393
|
const persisted = settings[settingKey];
|
|
5839
5394
|
const enabledSet = useMemo(() => {
|
|
5840
5395
|
if (persisted === void 0) return new Set(catalog.map(keyOf));
|
|
@@ -5844,7 +5399,11 @@ function useEnabledToggleSet(opts) {
|
|
|
5844
5399
|
catalog,
|
|
5845
5400
|
keyOf
|
|
5846
5401
|
]);
|
|
5847
|
-
const catalogKeys = useMemo(() => new Set(catalog.map(keyOf)), [
|
|
5402
|
+
const catalogKeys = useMemo(() => new Set(gcKeys ?? catalog.map(keyOf)), [
|
|
5403
|
+
gcKeys,
|
|
5404
|
+
catalog,
|
|
5405
|
+
keyOf
|
|
5406
|
+
]);
|
|
5848
5407
|
return {
|
|
5849
5408
|
enabledSet,
|
|
5850
5409
|
toggle: useCallback((name) => {
|
|
@@ -6174,10 +5733,6 @@ function buildHints(options) {
|
|
|
6174
5733
|
label: inFlightToolCount === 1 ? "cancel task" : `cancel task (${inFlightToolCount})`
|
|
6175
5734
|
} : null;
|
|
6176
5735
|
if (uiMode === "minimal") return [
|
|
6177
|
-
{
|
|
6178
|
-
key: keybindings.openContextPanel,
|
|
6179
|
-
label: "context"
|
|
6180
|
-
},
|
|
6181
5736
|
...hasMultipleAgents ? [{
|
|
6182
5737
|
key: keybindings.cycleAgent,
|
|
6183
5738
|
label: agentLabel,
|
|
@@ -6190,10 +5745,6 @@ function buildHints(options) {
|
|
|
6190
5745
|
}
|
|
6191
5746
|
];
|
|
6192
5747
|
return [
|
|
6193
|
-
{
|
|
6194
|
-
key: keybindings.openContextPanel,
|
|
6195
|
-
label: "context"
|
|
6196
|
-
},
|
|
6197
5748
|
...hasMultipleAgents ? [{
|
|
6198
5749
|
key: keybindings.cycleAgent,
|
|
6199
5750
|
label: agentLabel,
|
|
@@ -6229,129 +5780,6 @@ function shortChord(spec) {
|
|
|
6229
5780
|
return spec.startsWith("ctrl+") ? `/${spec.slice(5)}` : spec;
|
|
6230
5781
|
}
|
|
6231
5782
|
//#endregion
|
|
6232
|
-
//#region src/chat/generate-title.ts
|
|
6233
|
-
/** Hard cap on the result length. Anything longer is truncated client-side. */
|
|
6234
|
-
const TITLE_MAX_CHARS = 60;
|
|
6235
|
-
/** Default turn count fed to the model. 10 covers most exchanges without bloat. */
|
|
6236
|
-
const DEFAULT_MAX_TURNS = 10;
|
|
6237
|
-
/** Tokens budgeted for the model's reply. 64 fits any sane title with headroom. */
|
|
6238
|
-
const TITLE_RESPONSE_MAX_TOKENS = 64;
|
|
6239
|
-
/** Per-turn body cap when assembling the prompt — keeps the request payload tight. */
|
|
6240
|
-
const PROMPT_TURN_CHAR_MAX = 2e3;
|
|
6241
|
-
/**
|
|
6242
|
-
* Drive the provider's `stream()` once and return a concise title for
|
|
6243
|
-
* the conversation represented by `turns`. Throws when:
|
|
6244
|
-
* - `turns` contains no text-bearing content (nothing to summarize),
|
|
6245
|
-
* - the provider stream completes with no output text,
|
|
6246
|
-
* - `signal` is aborted (rethrown verbatim).
|
|
6247
|
-
*
|
|
6248
|
-
* Output is trimmed, single-line, with surrounding quotes / trailing
|
|
6249
|
-
* punctuation stripped, and truncated to `TITLE_MAX_CHARS`.
|
|
6250
|
-
*/
|
|
6251
|
-
async function generateSessionTitle({ provider, model, turns, maxTurns = DEFAULT_MAX_TURNS, signal }) {
|
|
6252
|
-
const transcript = renderTurnsForPrompt(turns.slice(-Math.max(1, maxTurns)));
|
|
6253
|
-
if (!transcript) throw new Error("No text content in the recent turns to summarize.");
|
|
6254
|
-
const system = [
|
|
6255
|
-
"You generate concise, descriptive titles for chat conversations.",
|
|
6256
|
-
"Reply with ONLY the title — no quotes, no markdown, no trailing punctuation,",
|
|
6257
|
-
"no preamble (do not say \"Title:\" or similar).",
|
|
6258
|
-
`Aim for 3 to 7 words and stay under ${TITLE_MAX_CHARS} characters.`
|
|
6259
|
-
].join(" ");
|
|
6260
|
-
const userPrompt = `Generate a title for this conversation:\n\n${transcript}`;
|
|
6261
|
-
let text = "";
|
|
6262
|
-
const result = await provider.stream({
|
|
6263
|
-
model,
|
|
6264
|
-
system,
|
|
6265
|
-
tools: [],
|
|
6266
|
-
messages: [provider.userMessage(userPrompt)],
|
|
6267
|
-
maxTokens: TITLE_RESPONSE_MAX_TOKENS,
|
|
6268
|
-
...signal ? { signal } : {}
|
|
6269
|
-
}, { onText: (delta) => {
|
|
6270
|
-
text += delta;
|
|
6271
|
-
} });
|
|
6272
|
-
return cleanTitle(text.trim().length > 0 ? text : result.text);
|
|
6273
|
-
}
|
|
6274
|
-
/**
|
|
6275
|
-
* Decide whether a session is ready for one-shot automatic titling.
|
|
6276
|
-
*
|
|
6277
|
-
* Returns `true` iff the conversation has had its first exchange — at
|
|
6278
|
-
* least two turns, i.e. the opening user message plus the assistant's
|
|
6279
|
-
* reply (this is exactly the "N turns" count the sessions list shows) —
|
|
6280
|
-
* AND no explicit title has been set yet. An existing `metadata.title`
|
|
6281
|
-
* (a previous auto-generation, or a manual title from the session-details
|
|
6282
|
-
* "generate title" action) is never clobbered.
|
|
6283
|
-
*
|
|
6284
|
-
* Pure + total. The runtime layers its own "is one already in flight?"
|
|
6285
|
-
* and "is a provider picked?" guards on top of this; the predicate only
|
|
6286
|
-
* encodes the content-level rule so it can be pinned in tests without a
|
|
6287
|
-
* mock provider — same rationale as the exported {@link cleanTitle}.
|
|
6288
|
-
*/
|
|
6289
|
-
function shouldAutoGenerateTitle(turns, metadata) {
|
|
6290
|
-
if ((typeof metadata?.title === "string" ? metadata.title.trim() : "").length > 0) return false;
|
|
6291
|
-
return turns.length >= 2;
|
|
6292
|
-
}
|
|
6293
|
-
/**
|
|
6294
|
-
* Compact a turn list into a transcript-style prompt:
|
|
6295
|
-
*
|
|
6296
|
-
* user: …
|
|
6297
|
-
* assistant: …
|
|
6298
|
-
* user: …
|
|
6299
|
-
*
|
|
6300
|
-
* Tool calls and tool results are summarized to keep the request
|
|
6301
|
-
* payload small — the model needs the rough flow of the conversation,
|
|
6302
|
-
* not full JSON blobs. Per-turn text is clipped at `PROMPT_TURN_CHAR_MAX`
|
|
6303
|
-
* so a single huge code dump doesn't blow the budget.
|
|
6304
|
-
*
|
|
6305
|
-
* Returns an empty string when the slice has no text-bearing content
|
|
6306
|
-
* (e.g. a turn list that's only tool plumbing).
|
|
6307
|
-
*/
|
|
6308
|
-
function renderTurnsForPrompt(turns) {
|
|
6309
|
-
const lines = [];
|
|
6310
|
-
for (const turn of turns) {
|
|
6311
|
-
const body = textBodyOf(turn);
|
|
6312
|
-
if (!body) continue;
|
|
6313
|
-
const role = turn.role === "assistant" ? "assistant" : turn.role === "user" ? "user" : "system";
|
|
6314
|
-
lines.push(`${role}: ${clip(body, PROMPT_TURN_CHAR_MAX)}`);
|
|
6315
|
-
}
|
|
6316
|
-
return lines.join("\n\n");
|
|
6317
|
-
}
|
|
6318
|
-
function textBodyOf(turn) {
|
|
6319
|
-
const parts = [];
|
|
6320
|
-
for (const block of turn.content) if (block.type === "text" && block.text.trim()) parts.push(block.text.trim());
|
|
6321
|
-
else if (block.type === "tool_call") parts.push(`[tool: ${block.name}]`);
|
|
6322
|
-
else if (block.type === "tool_result") parts.push(`[tool result]`);
|
|
6323
|
-
return parts.join(" ");
|
|
6324
|
-
}
|
|
6325
|
-
/**
|
|
6326
|
-
* Normalize a model-generated title into the shape we want to persist:
|
|
6327
|
-
*
|
|
6328
|
-
* - Collapse internal whitespace + take the first line only (some
|
|
6329
|
-
* models emit "Title: foo\n\nReason: …" despite instructions).
|
|
6330
|
-
* - Strip surrounding quote characters (`"foo"`, `'foo'`, `` `foo` ``).
|
|
6331
|
-
* - Strip a leading `Title:` / `title -` prefix if the model added one.
|
|
6332
|
-
* - Strip trailing punctuation (`.`, `!`, `?`) — titles read cleaner
|
|
6333
|
-
* without it.
|
|
6334
|
-
* - Truncate to `TITLE_MAX_CHARS` with a trailing `…` when over.
|
|
6335
|
-
*
|
|
6336
|
-
* Exported as `cleanTitle` so tests can pin the normalization rules
|
|
6337
|
-
* without going through a mock provider.
|
|
6338
|
-
*/
|
|
6339
|
-
function cleanTitle(raw) {
|
|
6340
|
-
let s = raw.split("\n")[0]?.trim() ?? "";
|
|
6341
|
-
s = s.replace(/^\s*title\s*[:\-–—]\s*/i, "");
|
|
6342
|
-
if (s.length >= 2) {
|
|
6343
|
-
const first = s.charAt(0);
|
|
6344
|
-
const last = s.charAt(s.length - 1);
|
|
6345
|
-
if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`" || first === "“" && last === "”") s = s.slice(1, -1).trim();
|
|
6346
|
-
}
|
|
6347
|
-
s = s.replace(/[.!?]+$/, "").trim();
|
|
6348
|
-
if (s.length === 0) throw new Error("Model returned an empty title.");
|
|
6349
|
-
return s.length > TITLE_MAX_CHARS ? `${s.slice(0, TITLE_MAX_CHARS - 1)}…` : s;
|
|
6350
|
-
}
|
|
6351
|
-
function clip(text, max) {
|
|
6352
|
-
return text.length > max ? `${text.slice(0, max)}…` : text;
|
|
6353
|
-
}
|
|
6354
|
-
//#endregion
|
|
6355
5783
|
//#region src/chat/hints.ts
|
|
6356
5784
|
/**
|
|
6357
5785
|
* Truncate `text` to at most `max` characters, replacing the trailing
|
|
@@ -6890,181 +6318,6 @@ function makeRequestInteraction(actions) {
|
|
|
6890
6318
|
});
|
|
6891
6319
|
}
|
|
6892
6320
|
//#endregion
|
|
6893
|
-
//#region src/chat/markdown-segments.ts
|
|
6894
|
-
/**
|
|
6895
|
-
* Split markdown text into alternating prose / fenced-code segments.
|
|
6896
|
-
*
|
|
6897
|
-
* Recognizes both ` ``` ` and `~~~` fences, with arbitrary length ≥ 3,
|
|
6898
|
-
* matching CommonMark's "fence indicator must be at least as long to
|
|
6899
|
-
* close" rule. Info-strings (the bit after the opening fence) are kept
|
|
6900
|
-
* as the `lang` hint; any trailing whitespace is trimmed. Closing
|
|
6901
|
-
* fences are detected on a line of their own (whitespace tolerated).
|
|
6902
|
-
*
|
|
6903
|
-
* Unclosed fences fall back to emitting the would-be-code body as a
|
|
6904
|
-
* trailing prose segment so no content is dropped — finalized markdown
|
|
6905
|
-
* isn't expected to ship one, but the fallback keeps the renderer
|
|
6906
|
-
* truthful when the model produces malformed output.
|
|
6907
|
-
*
|
|
6908
|
-
* Exported for unit tests.
|
|
6909
|
-
*/
|
|
6910
|
-
function splitMarkdownCodeBlocks(text) {
|
|
6911
|
-
const lines = text.split("\n");
|
|
6912
|
-
const segments = [];
|
|
6913
|
-
let prose = [];
|
|
6914
|
-
let code = [];
|
|
6915
|
-
let inFence = false;
|
|
6916
|
-
let fenceChar = "";
|
|
6917
|
-
let fenceLen = 0;
|
|
6918
|
-
let lang = "";
|
|
6919
|
-
const flushProse = () => {
|
|
6920
|
-
if (prose.length > 0 && prose[prose.length - 1] === "") prose.pop();
|
|
6921
|
-
if (prose.length > 0) {
|
|
6922
|
-
segments.push({
|
|
6923
|
-
kind: "prose",
|
|
6924
|
-
content: prose.join("\n")
|
|
6925
|
-
});
|
|
6926
|
-
prose = [];
|
|
6927
|
-
}
|
|
6928
|
-
};
|
|
6929
|
-
let trimLeadingProseBlank = false;
|
|
6930
|
-
for (const line of lines) if (!inFence) {
|
|
6931
|
-
const open = line.match(/^(`{3,}|~{3,})([^\n`~]*)$/);
|
|
6932
|
-
if (open) {
|
|
6933
|
-
flushProse();
|
|
6934
|
-
inFence = true;
|
|
6935
|
-
fenceChar = open[1][0];
|
|
6936
|
-
fenceLen = open[1].length;
|
|
6937
|
-
lang = open[2].trim();
|
|
6938
|
-
code = [];
|
|
6939
|
-
continue;
|
|
6940
|
-
}
|
|
6941
|
-
if (trimLeadingProseBlank) {
|
|
6942
|
-
trimLeadingProseBlank = false;
|
|
6943
|
-
if (line === "" && prose.length === 0) continue;
|
|
6944
|
-
}
|
|
6945
|
-
prose.push(line);
|
|
6946
|
-
} else {
|
|
6947
|
-
const close = line.match(/^(`{3,}|~{3,})\s*$/);
|
|
6948
|
-
if (close && close[1][0] === fenceChar && close[1].length >= fenceLen) {
|
|
6949
|
-
segments.push({
|
|
6950
|
-
kind: "code",
|
|
6951
|
-
content: code.join("\n"),
|
|
6952
|
-
lang
|
|
6953
|
-
});
|
|
6954
|
-
inFence = false;
|
|
6955
|
-
code = [];
|
|
6956
|
-
trimLeadingProseBlank = true;
|
|
6957
|
-
continue;
|
|
6958
|
-
}
|
|
6959
|
-
code.push(line);
|
|
6960
|
-
}
|
|
6961
|
-
if (inFence) prose.push(`${fenceChar.repeat(fenceLen)}${lang}`, ...code);
|
|
6962
|
-
flushProse();
|
|
6963
|
-
return segments;
|
|
6964
|
-
}
|
|
6965
|
-
const LIST_ITEM_RE = /^( *)(?:[-+*]|\d+[.)]) +/;
|
|
6966
|
-
/**
|
|
6967
|
-
* Lift indented fenced code blocks to column 0 while preserving the code
|
|
6968
|
-
* body's relative indentation.
|
|
6969
|
-
*
|
|
6970
|
-
* OpenTUI's markdown renderNode hook only receives top-level code tokens.
|
|
6971
|
-
* A common assistant shape is:
|
|
6972
|
-
*
|
|
6973
|
-
* - Run this:
|
|
6974
|
-
* ```bash
|
|
6975
|
-
* command
|
|
6976
|
-
* ```
|
|
6977
|
-
*
|
|
6978
|
-
* Marked parses that as code inside a list token, so the TUI loses the
|
|
6979
|
-
* code-panel wrapper and copy affordance. Normalizing just the fence's list
|
|
6980
|
-
* indentation keeps the prose readable while routing the snippet through
|
|
6981
|
-
* the same top-level renderer as ordinary fences.
|
|
6982
|
-
*/
|
|
6983
|
-
function normalizeMarkdownCodeFences(text) {
|
|
6984
|
-
const lines = text.split("\n");
|
|
6985
|
-
const out = [];
|
|
6986
|
-
const listIndents = [];
|
|
6987
|
-
let inFence = false;
|
|
6988
|
-
let fenceChar = "";
|
|
6989
|
-
let fenceLen = 0;
|
|
6990
|
-
let lift = 0;
|
|
6991
|
-
for (const line of lines) {
|
|
6992
|
-
if (inFence) {
|
|
6993
|
-
const normalized = stripFenceIndent(line, lift);
|
|
6994
|
-
out.push(normalized);
|
|
6995
|
-
if (isFenceClose(normalized, fenceChar, fenceLen)) {
|
|
6996
|
-
inFence = false;
|
|
6997
|
-
fenceChar = "";
|
|
6998
|
-
fenceLen = 0;
|
|
6999
|
-
lift = 0;
|
|
7000
|
-
}
|
|
7001
|
-
continue;
|
|
7002
|
-
}
|
|
7003
|
-
updateListIndentStack(listIndents, line);
|
|
7004
|
-
const open = parseFenceOpen(line);
|
|
7005
|
-
if (!open) {
|
|
7006
|
-
out.push(line);
|
|
7007
|
-
continue;
|
|
7008
|
-
}
|
|
7009
|
-
lift = [...listIndents].reverse().find((i) => i <= open.indent) ?? (open.indent <= 3 ? open.indent : 0);
|
|
7010
|
-
const normalized = stripFenceIndent(line, lift);
|
|
7011
|
-
out.push(normalized);
|
|
7012
|
-
inFence = true;
|
|
7013
|
-
fenceChar = open.char;
|
|
7014
|
-
fenceLen = open.length;
|
|
7015
|
-
}
|
|
7016
|
-
return out.join("\n");
|
|
7017
|
-
}
|
|
7018
|
-
function parseFenceOpen(line) {
|
|
7019
|
-
const indent = leadingSpaces(line);
|
|
7020
|
-
const char = line[indent];
|
|
7021
|
-
if (char !== "`" && char !== "~") return null;
|
|
7022
|
-
const length = countRun(line, indent, char);
|
|
7023
|
-
if (length < 3) return null;
|
|
7024
|
-
const info = line.slice(indent + length);
|
|
7025
|
-
if (char === "`" && info.includes("`")) return null;
|
|
7026
|
-
return {
|
|
7027
|
-
indent,
|
|
7028
|
-
char,
|
|
7029
|
-
length
|
|
7030
|
-
};
|
|
7031
|
-
}
|
|
7032
|
-
function isFenceClose(line, fenceChar, fenceLen) {
|
|
7033
|
-
const indent = leadingSpaces(line);
|
|
7034
|
-
if (line[indent] !== fenceChar) return false;
|
|
7035
|
-
const length = countRun(line, indent, fenceChar);
|
|
7036
|
-
if (length < fenceLen) return false;
|
|
7037
|
-
return onlySpaces(line, indent + length);
|
|
7038
|
-
}
|
|
7039
|
-
function updateListIndentStack(listIndents, line) {
|
|
7040
|
-
const contentIndent = leadingSpaces(line);
|
|
7041
|
-
if (line.trim().length > 0) while (listIndents.length > 0 && contentIndent < listIndents[listIndents.length - 1]) listIndents.pop();
|
|
7042
|
-
const item = line.match(LIST_ITEM_RE);
|
|
7043
|
-
if (!item) return;
|
|
7044
|
-
const indent = item[0].length;
|
|
7045
|
-
if (indent > (listIndents[listIndents.length - 1] ?? 0)) listIndents.push(indent);
|
|
7046
|
-
}
|
|
7047
|
-
function leadingSpaces(line) {
|
|
7048
|
-
let i = 0;
|
|
7049
|
-
while (i < line.length && line[i] === " ") i++;
|
|
7050
|
-
return i;
|
|
7051
|
-
}
|
|
7052
|
-
function countRun(line, start, char) {
|
|
7053
|
-
let i = start;
|
|
7054
|
-
while (i < line.length && line[i] === char) i++;
|
|
7055
|
-
return i - start;
|
|
7056
|
-
}
|
|
7057
|
-
function onlySpaces(line, start) {
|
|
7058
|
-
for (let i = start; i < line.length; i++) if (line[i] !== " ") return false;
|
|
7059
|
-
return true;
|
|
7060
|
-
}
|
|
7061
|
-
function stripFenceIndent(line, columns) {
|
|
7062
|
-
if (columns <= 0) return line;
|
|
7063
|
-
let i = 0;
|
|
7064
|
-
while (i < columns && line[i] === " ") i++;
|
|
7065
|
-
return line.slice(i);
|
|
7066
|
-
}
|
|
7067
|
-
//#endregion
|
|
7068
6321
|
//#region src/chat/mcp-auth-state.ts
|
|
7069
6322
|
/**
|
|
7070
6323
|
* Apply one event to the state map. Pure, immutable — returns a new map
|
|
@@ -7527,58 +6780,6 @@ function isCachedEntry(value) {
|
|
|
7527
6780
|
return true;
|
|
7528
6781
|
}
|
|
7529
6782
|
//#endregion
|
|
7530
|
-
//#region src/chat/project-user-paths.ts
|
|
7531
|
-
/**
|
|
7532
|
-
* Shared search-path builder for project + user config discovery.
|
|
7533
|
-
*
|
|
7534
|
-
* Both Skills (`skills/<name>/SKILL.md`) and MCP servers (`mcps.json`)
|
|
7535
|
-
* follow the same first-found-wins order:
|
|
7536
|
-
*
|
|
7537
|
-
* 1. `{project-root}/.agents/<sub>` — project, agnostic convention
|
|
7538
|
-
* 2. `{project-root}/.{prefix}/<sub>` — project, zidane (`.zidane` default)
|
|
7539
|
-
* 3. `~/.agents/<sub>` — user, agnostic
|
|
7540
|
-
* 4. `~/.{prefix}/<sub>` — user, zidane
|
|
7541
|
-
*
|
|
7542
|
-
* `{project-root}` is the git root walked up from `cwd` when present,
|
|
7543
|
-
* else `cwd` itself. This pairs with the project-scoped session
|
|
7544
|
-
* storage (`resolveStoragePaths`) so all three project-aware surfaces
|
|
7545
|
-
* — sessions, mcps, skills — anchor at the same `.{prefix}/`.
|
|
7546
|
-
*
|
|
7547
|
-
* Centralizing the pattern keeps the four paths in one place — a third
|
|
7548
|
-
* discovery surface (hooks, rules, …) gets the same convention for free
|
|
7549
|
-
* and stays in lock-step on a prefix bump.
|
|
7550
|
-
*/
|
|
7551
|
-
/**
|
|
7552
|
-
* Resolve the four search paths for a given subdirectory (`skills`,
|
|
7553
|
-
* `mcps.json`, …). Returns absolute paths regardless of whether they
|
|
7554
|
-
* exist — the caller chooses to `existsSync`-filter or feed straight
|
|
7555
|
-
* into a tolerant discovery routine.
|
|
7556
|
-
*/
|
|
7557
|
-
function projectUserPaths(opts) {
|
|
7558
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
7559
|
-
const home = opts.home ?? homedir();
|
|
7560
|
-
const projectRoot = findGitRoot$1(cwd) ?? cwd;
|
|
7561
|
-
const prefix = (opts.prefix ?? ".zidane").replace(/^\./, "");
|
|
7562
|
-
return [
|
|
7563
|
-
{
|
|
7564
|
-
path: resolve(projectRoot, `.agents/${opts.subPath}`),
|
|
7565
|
-
source: "project"
|
|
7566
|
-
},
|
|
7567
|
-
{
|
|
7568
|
-
path: resolve(projectRoot, `.${prefix}/${opts.subPath}`),
|
|
7569
|
-
source: "project"
|
|
7570
|
-
},
|
|
7571
|
-
{
|
|
7572
|
-
path: resolve(home, `.agents/${opts.subPath}`),
|
|
7573
|
-
source: "user"
|
|
7574
|
-
},
|
|
7575
|
-
{
|
|
7576
|
-
path: resolve(home, `.${prefix}/${opts.subPath}`),
|
|
7577
|
-
source: "user"
|
|
7578
|
-
}
|
|
7579
|
-
];
|
|
7580
|
-
}
|
|
7581
|
-
//#endregion
|
|
7582
6783
|
//#region src/chat/mcps-discovery.ts
|
|
7583
6784
|
/**
|
|
7584
6785
|
* File names searched in each of the four project/user roots. Order
|
|
@@ -9060,6 +8261,9 @@ function useStreamBuffer(setEvents, options) {
|
|
|
9060
8261
|
const lastTickMsRef = useRef(0);
|
|
9061
8262
|
const getSmoothRef = useRef(options?.getSmooth);
|
|
9062
8263
|
getSmoothRef.current = options?.getSmooth;
|
|
8264
|
+
/** Live mirror so drains read the current cap without re-memoizing. */
|
|
8265
|
+
const maxEventsRef = useRef(options?.maxTranscriptEvents);
|
|
8266
|
+
maxEventsRef.current = options?.maxTranscriptEvents;
|
|
9063
8267
|
/**
|
|
9064
8268
|
* Updaters queued while smooth-streaming was still draining backlog —
|
|
9065
8269
|
* tool-call appends, finalize-markdown transforms, error events.
|
|
@@ -9111,7 +8315,8 @@ function useStreamBuffer(setEvents, options) {
|
|
|
9111
8315
|
for (const bucket of buckets) merged = applyBucket(merged, bucket);
|
|
9112
8316
|
for (const u of queued) merged = u(merged);
|
|
9113
8317
|
if (updater) merged = updater(merged);
|
|
9114
|
-
|
|
8318
|
+
const cap = maxEventsRef.current;
|
|
8319
|
+
return cap === null ? merged : capTranscriptEvents(merged, cap ?? void 0);
|
|
9115
8320
|
});
|
|
9116
8321
|
}, [setEvents, stopTicker]);
|
|
9117
8322
|
/**
|
|
@@ -9309,6 +8514,6 @@ function computeTurnAnchors(items) {
|
|
|
9309
8514
|
};
|
|
9310
8515
|
}
|
|
9311
8516
|
//#endregion
|
|
9312
|
-
export {
|
|
8517
|
+
export { useMcpAuthState as $, eventsFromTurns as $n, useUpdateCheck as $t, runOAuthLogin as A, pruneTodosByRun as An, DEFAULT_THEME as At, saveMcpToolsCache as B, PLAN_MODE_DOCTRINE as Bn, createDiscoverySlot as Bt, projectsFilePath as C, TODO_STATUS_GLYPHS as Cn, SETTINGS_CHOICES as Ct, formatPathForCwd as D, getTodosForRun as Dn, clampFps as Dt, writeProjects as E, getArchivedTodosForRun as En, SettingsProvider as Et, parseMcpsFile as F, COMMUNICATION_DOCTRINE as Fn, GRUVBOX_LIGHT as Ft, buildVisibleMcpRows as G, buildPlanSystem as Gn, useConfig as Gt, buildToolToggle as H, SUBAGENT_GUIDANCE as Hn, useDiscovery as Ht, clearMcpToolsCache as I, DOING_TASKS_DOCTRINE as In, CATPPUCCIN_FRAPPE as It, createFileMcpCredentialStore as J, createChatTurnsReadTool as Jn, useCompletion as Jt, indexOfServerRow as K, envSection as Kn, resolveConfig as Kt, loadMcpToolsCache as L, IDENTITY_PREFIX as Ln, CATPPUCCIN_LATTE as Lt, buildMcpServers as M, setTodosForRun as Mn, resolveTheme as Mt, defaultMcpsConfigPaths as N, useActiveTodos as Nn, VAPORWAVE_THEME as Nt, fetchOAuthRedirect as O, isTodoTool as On, useSettings as Ot, discoverProjectMcps as P, ACTIONS_WITH_CARE_DOCTRINE as Pn, GRUVBOX_DARK as Pt, useMcpAuthDispatch as Q, deriveSessionTitle as Qn, buildUpdateHint as Qt, mcpToolsCachePath as R, INTERACTION_GUIDANCE as Rn, CATPPUCCIN_MACCHIATO as Rt, matchesSafelistEntry as S, TODOWRITE_TOOL as Sn, SETTINGS_CATEGORIES as St, suggestSafelistEntries as T, createTodoTools as Tn, SETTINGS_TOGGLES as Tt, useMcpToolToggleMap as U, TOKEN_DISCIPLINE_DOCTRINE as Un, useDiscoveryOptional as Ut, subscribeMcpToolsCache as V, PLAN_MODE_DOCTRINE_NO_PROMPTS as Vn, DiscoveryProvider as Vt, useMcpToolToggleSet as W, buildBuildSystem as Wn, ConfigProvider as Wt, patchMcpCredential as X, compactSummaryEvent as Xn, bootProfileEnabled as Xt, mcpCredentialsPath as Y, DEFAULT_SESSION_TITLE as Yn, tryOpenBrowser as Yt, McpAuthProvider as Z, createStateStore as Zn, bootTick as Zt, useSafeModeQueue as _, accentColor as _n, buildHints as _t, useSurfaces as a, performInPlaceSelfUpdate as an, serverToolResultSummary as ar, buildResumedToolResultsTurn as at, getSafelist as b, TODOREAD_TOOL as bn, useEnabledToggleSet as bt, useStreamBuffer as c, chatAutoCompactBehavior as cn, titleFromTurns as cr, makeRequestInteraction as ct, discoverProjectSkills as d, BUILD_AGENT as dn, updateToolEventOutcomes as dr, useInteractionsActions as dt, checkForUpdate as en, lastContextSizeFromTurns as er, getMcpAuthStatus as et, renderSession as f, BUILTIN_AGENTS as fn, useInteractionsQueue as ft, useSafeModeActions as g, PLAN_AGENT as gn, truncateTrailing as gt, SafeModeProvider as h, DEFAULT_PERSIST_EXCLUDE_TOOLS as hn, hintsLength as ht, useSelectStyle as i, parseSemver as in, saveState as ir, PRESENT_PLAN_TOOL as it, supportsOAuth as j, selectActiveTodos as jn, resolveChipColor as jt, oauthUsesManualCodePaste as k, pickActiveRunId as kn, BUILTIN_THEMES as kt, buildSkillsConfig as l, discoverAgentsMd as ln, toolCallPreview as lr, pendingInteractionsFromTurns as lt, writeSessionExport as m, DEFAULT_BUDGET_EXCLUDE_TOOLS as mn, clipHintsToWidth as mt, ThemeProvider as n, detectLibc as nn, loadState as nr, ASK_USER_TOOL as nt, useSyntaxStyles as o, performSelfUpdate as on, stripSpawnTokensLine as or, createInteractionTools as ot, resolveSessionExportTarget as p, DEFAULT_AGENT_ID as pn, EMPTY_HINTS as pt, parentServerName as q, chatReferenceTokensForPrompt as qn, resolveStoragePaths as qt, useColors as r, detectPackageManager as rn, marginTopFor as rr, InteractionsProvider as rt, useTheme as s, resolvePlatformPackage as sn, sumRunCosts as sr, isInteractionTool as st, computeTurnAnchors as t, compareSemver as tn, listSessionMeta as tr, reduceMcpAuth as tt, defaultSkillScanPaths as u, renderAgentsMdBlock as un, toolResultText as ur, serializeInteractionResponse as ut, IMPLICITLY_SAFE_TOOLS as v, resolveAgentId as vn, shortChord as vt, readProjects as w, TODO_WRITE_COUNTS_METADATA_KEY as wn, SETTINGS_INPUTS as wt, isOnSafelist as x, TODOS_METADATA_KEY as xn, DEFAULT_SETTINGS as xt, addToSafelist as y, singleAgentRegistry as yn, listProjectFiles as yt, refreshMcpToolsCatalog as z, INTERACTION_GUIDANCE_NO_PROMPTS as zn, CATPPUCCIN_MOCHA as zt };
|
|
9313
8518
|
|
|
9314
|
-
//# sourceMappingURL=transcript-anchors-
|
|
8519
|
+
//# sourceMappingURL=transcript-anchors-DSRPWjiH.js.map
|