zidane 4.1.8 → 4.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/{index-bgh-k8Mv.d.ts → agent-CMIhYhDz.d.ts} +2032 -1993
  2. package/dist/agent-CMIhYhDz.d.ts.map +1 -0
  3. package/dist/chat.d.ts +7 -6
  4. package/dist/chat.d.ts.map +1 -1
  5. package/dist/chat.js +2 -2
  6. package/dist/contexts.d.ts +1 -1
  7. package/dist/{index-BB4kuRh3.d.ts → index-CXVvqTQj.d.ts} +1 -1
  8. package/dist/{index-BB4kuRh3.d.ts.map → index-CXVvqTQj.d.ts.map} +1 -1
  9. package/dist/{index-Ds5YpvfZ.d.ts → index-D6Dd6Kc0.d.ts} +3 -3
  10. package/dist/{index-Ds5YpvfZ.d.ts.map → index-D6Dd6Kc0.d.ts.map} +1 -1
  11. package/dist/{index-DRoG_udt.d.ts → index-DAaKyadO.d.ts} +2 -2
  12. package/dist/{index-DRoG_udt.d.ts.map → index-DAaKyadO.d.ts.map} +1 -1
  13. package/dist/index.d.ts +4 -4
  14. package/dist/index.js +6 -6
  15. package/dist/{interpolate-CukJwP2G.js → interpolate-BydkV1eT.js} +3 -1
  16. package/dist/interpolate-BydkV1eT.js.map +1 -0
  17. package/dist/{mcp-8wClKY-3.js → mcp-Dw-fRPVk.js} +61 -65
  18. package/dist/mcp-Dw-fRPVk.js.map +1 -0
  19. package/dist/mcp.d.ts +1 -1
  20. package/dist/mcp.js +1 -1
  21. package/dist/{presets-BzkJDW1K.js → presets-4zCJzCYw.js} +2 -2
  22. package/dist/{presets-BzkJDW1K.js.map → presets-4zCJzCYw.js.map} +1 -1
  23. package/dist/presets.d.ts +1 -1
  24. package/dist/presets.js +1 -1
  25. package/dist/providers.d.ts +1 -1
  26. package/dist/session/sqlite.d.ts +13 -2
  27. package/dist/session/sqlite.d.ts.map +1 -1
  28. package/dist/session/sqlite.js +70 -27
  29. package/dist/session/sqlite.js.map +1 -1
  30. package/dist/{session-Cn68UASv.js → session-B1RN0uoi.js} +42 -30
  31. package/dist/{session-Cn68UASv.js.map → session-B1RN0uoi.js.map} +1 -1
  32. package/dist/session.d.ts +1 -1
  33. package/dist/session.js +1 -1
  34. package/dist/skills.d.ts +2 -2
  35. package/dist/skills.js +1 -1
  36. package/dist/{stats-BT9l57RS.js → stats-DZIsGqzu.js} +15 -5
  37. package/dist/stats-DZIsGqzu.js.map +1 -0
  38. package/dist/{theme-BlXO6yHe.d.ts → theme-Caf4AvTO.d.ts} +147 -13
  39. package/dist/theme-Caf4AvTO.d.ts.map +1 -0
  40. package/dist/{theme-context-MungM3SY.js → theme-context-DQM2lx4U.js} +212 -72
  41. package/dist/theme-context-DQM2lx4U.js.map +1 -0
  42. package/dist/{tools-C8kDot0H.js → tools-BdQENveS.js} +409 -312
  43. package/dist/tools-BdQENveS.js.map +1 -0
  44. package/dist/tools.d.ts +2 -2
  45. package/dist/tools.js +1 -1
  46. package/dist/tui.d.ts +64 -7
  47. package/dist/tui.d.ts.map +1 -1
  48. package/dist/tui.js +481 -143
  49. package/dist/tui.js.map +1 -1
  50. package/dist/types.d.ts +3 -3
  51. package/dist/types.js +1 -1
  52. package/package.json +6 -1
  53. package/dist/index-bgh-k8Mv.d.ts.map +0 -1
  54. package/dist/interpolate-CukJwP2G.js.map +0 -1
  55. package/dist/mcp-8wClKY-3.js.map +0 -1
  56. package/dist/stats-BT9l57RS.js.map +0 -1
  57. package/dist/theme-BlXO6yHe.d.ts.map +0 -1
  58. package/dist/theme-context-MungM3SY.js.map +0 -1
  59. package/dist/tools-C8kDot0H.js.map +0 -1
@@ -1,16 +1,105 @@
1
+ import { a as multiEdit, c as grep, i as readFile, l as glob, n as createSpawnTool, o as listFiles, r as shell, t as writeFile, u as edit } from "./tools-BdQENveS.js";
1
2
  import { n as toolResultToText } from "./types-Bx_F8jet.js";
2
- import { n as formatTokenUsage } from "./stats-BT9l57RS.js";
3
- import { r as basic_default } from "./presets-BzkJDW1K.js";
3
+ import { n as formatTokenUsage } from "./stats-DZIsGqzu.js";
4
+ import { t as definePreset } from "./presets-4zCJzCYw.js";
4
5
  import { i as anthropic, n as openai, r as cerebras, t as openrouter } from "./providers-CCDvIXGJ.js";
5
- import { createSqliteStore } from "./session/sqlite.js";
6
6
  import { spawn } from "node:child_process";
7
7
  import { dirname, resolve } from "node:path";
8
8
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
9
9
  import { homedir } from "node:os";
10
10
  import { anthropicOAuthProvider, openaiCodexOAuthProvider } from "@mariozechner/pi-ai/oauth";
11
11
  import { getModel, getModels } from "@mariozechner/pi-ai";
12
- import { createContext, useCallback, useContext, useMemo, useRef, useState } from "react";
13
- import { jsx } from "@opentui/react/jsx-runtime";
12
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
13
+ import { jsx } from "react/jsx-runtime";
14
+ //#region src/chat/agents.ts
15
+ /** Read-only tool slice shared by the Plan profile and any host-built read-only variant. */
16
+ const READ_ONLY_TOOLS = {
17
+ readFile,
18
+ listFiles,
19
+ glob,
20
+ grep
21
+ };
22
+ /**
23
+ * Build agent — the default profile. Full read/write/shell access plus
24
+ * subagent spawning. Mirrors the legacy `basic` preset so existing TUI
25
+ * behavior is preserved when `agents` is omitted.
26
+ */
27
+ const BUILD_AGENT = {
28
+ id: "build",
29
+ label: "Build",
30
+ description: "full tool access — read, write, edit, shell, and spawn subagents",
31
+ accent: "accent",
32
+ preset: definePreset({
33
+ name: "build",
34
+ system: "You are a helpful coding assistant with full access to the workspace: shell, file reading, file writing, surgical and multi-edit tools, directory listing, codebase search, and sub-agent spawning. Prefer `edit` / `multi_edit` for in-place changes and `write_file` for full file overwrites. Spawn subagents for parallelizable work.",
35
+ tools: {
36
+ shell,
37
+ readFile,
38
+ writeFile,
39
+ listFiles,
40
+ edit,
41
+ multiEdit,
42
+ glob,
43
+ grep,
44
+ spawn: createSpawnTool({ persist: true })
45
+ }
46
+ })
47
+ };
48
+ /**
49
+ * Plan agent — read-only exploration mode. Locked down to file reading and
50
+ * codebase search; no shell, no edits, no spawning. The system prompt
51
+ * frames the conversation as planning rather than execution so the model
52
+ * outputs proposals instead of attempting mutations.
53
+ */
54
+ const PLAN_AGENT = {
55
+ id: "plan",
56
+ label: "Plan",
57
+ description: "read-only — explore, analyze, and propose without modifying anything",
58
+ accent: "model",
59
+ preset: definePreset({
60
+ name: "plan",
61
+ system: "You are in PLAN mode. You can read files, list directories, and search the codebase — but you CANNOT modify files, run shell commands, or spawn subagents. Use this mode to investigate, understand the problem, and propose a clear plan in detail. The user will switch to Build mode to execute it.",
62
+ tools: READ_ONLY_TOOLS
63
+ })
64
+ };
65
+ /**
66
+ * Default registry shipped with `zidane/tui`. Insertion order = picker order.
67
+ * Hosts that want only one profile can pass `{ agents: { build: BUILD_AGENT } }`,
68
+ * or a fully custom registry of their own profiles.
69
+ */
70
+ const BUILTIN_AGENTS = {
71
+ build: BUILD_AGENT,
72
+ plan: PLAN_AGENT
73
+ };
74
+ /** Id of the profile activated on first launch when nothing is persisted. */
75
+ const DEFAULT_AGENT_ID = "build";
76
+ /**
77
+ * Resolve an agent id against a registry with sensible fallbacks: the
78
+ * requested id wins when present, then `defaultId`, then the first key.
79
+ * Returns `null` when the registry is empty (host misconfiguration —
80
+ * the caller should surface this rather than silently failing).
81
+ */
82
+ function resolveAgentId(registry, requestedId, defaultId) {
83
+ if (requestedId && registry[requestedId]) return requestedId;
84
+ if (defaultId && registry[defaultId]) return defaultId;
85
+ return Object.keys(registry)[0] ?? null;
86
+ }
87
+ /**
88
+ * Wrap a legacy single `Preset` into a single-profile registry. Used by
89
+ * `resolveConfig` when the host passed `preset` without `agents`, so older
90
+ * call sites keep working — they get a one-entry registry whose picker is
91
+ * a no-op (only one option).
92
+ */
93
+ function singleAgentRegistry(preset) {
94
+ return { default: {
95
+ id: "default",
96
+ label: typeof preset.name === "string" && preset.name.length > 0 ? preset.name : "Default",
97
+ description: "host-provided preset",
98
+ preset,
99
+ accent: "accent"
100
+ } };
101
+ }
102
+ //#endregion
14
103
  //#region src/chat/providers.ts
15
104
  /** Convenience accessor — returns `credentialFileKey ?? key`. */
16
105
  function credKeyOf(desc) {
@@ -285,20 +374,16 @@ function detectAuth(dataDir, registry, env = process.env) {
285
374
  }
286
375
  //#endregion
287
376
  //#region src/chat/store.ts
288
- function ensureDir$1(path) {
377
+ function ensureStateDir(path) {
289
378
  const dir = dirname(path);
290
379
  if (existsSync(dir)) return;
291
380
  try {
292
381
  mkdirSync(dir, { recursive: true });
293
382
  } catch (err) {
294
383
  const message = err instanceof Error ? err.message : String(err);
295
- throw new Error(`Could not create TUI storage directory at "${dir}". Override the location via \`runTui({ storageDir, prefix })\` or the \`ZIDANE_STORAGE_DIR\` env var. Original error: ${message}`);
384
+ throw new Error(`Could not create TUI state directory at "${dir}". Override the location via \`runTui({ storageDir, prefix })\` or the \`ZIDANE_STORAGE_DIR\` env var. Original error: ${message}`);
296
385
  }
297
386
  }
298
- function createTuiStore(dbPath) {
299
- ensureDir$1(dbPath);
300
- return createSqliteStore({ path: dbPath });
301
- }
302
387
  function createStateStore(path) {
303
388
  return {
304
389
  load: () => loadState(path),
@@ -314,7 +399,7 @@ function loadState(path) {
314
399
  return {};
315
400
  }
316
401
  function saveState(path, state) {
317
- ensureDir$1(path);
402
+ ensureStateDir(path);
318
403
  const tmp = `${path}.${process.pid}.tmp`;
319
404
  writeFileSync(tmp, JSON.stringify(state, null, 2));
320
405
  renameSync(tmp, path);
@@ -323,10 +408,15 @@ function saveState(path, state) {
323
408
  * Load every session and project it to the compact `SessionMeta` shape used by
324
409
  * the picker. Sorted by recency via the underlying store's `list()` contract
325
410
  * (sqlite store returns by `updated_at DESC`).
411
+ *
412
+ * Robust to per-row failures: `Promise.allSettled` so a single corrupt or
413
+ * unreadable row (malformed JSON, partial write, transient I/O error)
414
+ * doesn't take down the entire picker. Failed rows are silently skipped;
415
+ * a stderr line is emitted under `ZIDANE_DEBUG` for diagnosis.
326
416
  */
327
417
  async function listSessionMeta(store) {
328
418
  const ids = await store.list();
329
- return (await Promise.all(ids.map(async (id) => {
419
+ const settled = await Promise.allSettled(ids.map(async (id) => {
330
420
  const data = await store.load(id);
331
421
  if (!data) return null;
332
422
  return {
@@ -335,7 +425,17 @@ async function listSessionMeta(store) {
335
425
  turnCount: data.turns.length,
336
426
  updatedAt: data.updatedAt
337
427
  };
338
- }))).filter((m) => m !== null);
428
+ }));
429
+ const metas = [];
430
+ for (let i = 0; i < settled.length; i++) {
431
+ const result = settled[i];
432
+ if (result.status === "fulfilled" && result.value) metas.push(result.value);
433
+ else if (result.status === "rejected" && process.env.ZIDANE_DEBUG) {
434
+ const cause = result.reason instanceof Error ? result.reason.message : String(result.reason);
435
+ process.stderr.write(`[zidane/chat] failed to load session "${ids[i]}": ${cause}\n`);
436
+ }
437
+ }
438
+ return metas;
339
439
  }
340
440
  /** Derive a short title from the first user message — returns null when empty. */
341
441
  function titleFromTurns(turns) {
@@ -376,13 +476,25 @@ function eventsFromTurns(turns, runs = []) {
376
476
  if (turn.role !== "assistant") continue;
377
477
  for (const block of turn.content) if (block.type === "tool_call") toolByCallId.set(block.id, block.name);
378
478
  }
479
+ const ancestryOf = (turnRunId) => {
480
+ if (!turnRunId) return [];
481
+ const chain = [];
482
+ let cursor = runById.get(turnRunId);
483
+ const seen = /* @__PURE__ */ new Set();
484
+ while (cursor) {
485
+ if (seen.has(cursor.id)) break;
486
+ seen.add(cursor.id);
487
+ if ((cursor.depth ?? 0) > 0) chain.unshift(cursor);
488
+ cursor = cursor.parentRunId ? runById.get(cursor.parentRunId) : void 0;
489
+ }
490
+ return chain;
491
+ };
379
492
  const events = [];
380
- let lastRunId;
381
- let lastDepth = 0;
382
- const closeRun = (runId, depth) => {
383
- if (!runId || depth <= 0) return;
384
- const run = runById.get(runId);
385
- if (!run) return;
493
+ /** Currently-open child runs, root-of-tree first, innermost last. */
494
+ const openStack = [];
495
+ /** True iff anything has been emitted at the active depth-0 level. Used to decide separators between top-level turns. */
496
+ let lastDepthAtEmission = -1;
497
+ const pushSpawnEnd = (run) => {
386
498
  const tag = run.status === "aborted" || run.status === "error" ? run.status : "done";
387
499
  const usage = formatTokenUsage({
388
500
  totalIn: run.tokensIn ?? run.totalUsage?.input ?? 0,
@@ -393,42 +505,39 @@ function eventsFromTurns(turns, runs = []) {
393
505
  events.push({
394
506
  kind: "spawn-end",
395
507
  text: `${tag} ${usage}`,
396
- childId: labelFor(runId),
397
- depth
508
+ childId: labelFor(run.id),
509
+ depth: run.depth ?? 1
398
510
  });
399
511
  };
400
- const openRun = (runId, depth) => {
401
- if (depth <= 0) return;
402
- const run = runById.get(runId);
403
- if (!run) return;
512
+ const pushSpawnStart = (run) => {
404
513
  const taskPreview = run.prompt.length > 80 ? `${run.prompt.slice(0, 80)}…` : run.prompt;
405
514
  events.push({
406
515
  kind: "spawn-start",
407
516
  text: taskPreview,
408
- childId: labelFor(runId),
409
- depth
517
+ childId: labelFor(run.id),
518
+ depth: run.depth ?? 1
410
519
  });
411
520
  };
412
521
  for (let i = 0; i < turns.length; i++) {
413
522
  const turn = turns[i];
414
- const depth = (turn.runId ? runById.get(turn.runId) : void 0)?.depth ?? 0;
523
+ const targetChain = ancestryOf(turn.runId);
524
+ const depth = targetChain.length;
525
+ let common = 0;
526
+ while (common < openStack.length && common < targetChain.length && openStack[common].id === targetChain[common].id) common++;
527
+ while (openStack.length > common) pushSpawnEnd(openStack.pop());
528
+ while (openStack.length < targetChain.length) {
529
+ const run = targetChain[openStack.length];
530
+ pushSpawnStart(run);
531
+ openStack.push(run);
532
+ }
533
+ if (depth === 0 && lastDepthAtEmission === 0) events.push({
534
+ kind: "separator",
535
+ text: ""
536
+ });
415
537
  const tag = depth > 0 && turn.runId ? {
416
538
  childId: labelFor(turn.runId),
417
539
  depth
418
540
  } : void 0;
419
- if (turn.runId !== lastRunId) {
420
- closeRun(lastRunId, lastDepth);
421
- if (depth === 0 && lastDepth === 0 && i > 0) events.push({
422
- kind: "separator",
423
- text: ""
424
- });
425
- if (turn.runId) openRun(turn.runId, depth);
426
- lastRunId = turn.runId;
427
- lastDepth = depth;
428
- } else if (i > 0 && depth === 0) events.push({
429
- kind: "separator",
430
- text: ""
431
- });
432
541
  if (turn.role === "user") {
433
542
  for (const block of turn.content) if (block.type === "text" && block.text.trim()) {
434
543
  if (depth === 0) events.push({
@@ -446,6 +555,7 @@ function eventsFromTurns(turns, runs = []) {
446
555
  ...tag
447
556
  });
448
557
  }
558
+ lastDepthAtEmission = depth;
449
559
  continue;
450
560
  }
451
561
  if (turn.role === "assistant") {
@@ -461,9 +571,10 @@ function eventsFromTurns(turns, runs = []) {
461
571
  tool: block.name,
462
572
  ...tag
463
573
  });
574
+ lastDepthAtEmission = depth;
464
575
  }
465
576
  }
466
- closeRun(lastRunId, lastDepth);
577
+ while (openStack.length > 0) pushSpawnEnd(openStack.pop());
467
578
  return events;
468
579
  }
469
580
  /** Shared formatter for the `↳ name(args)` line shown on tool calls. */
@@ -489,20 +600,36 @@ function toolResultText(output) {
489
600
  * (post-`formatTokenUsage`) shapes.
490
601
  */
491
602
  function stripSpawnTokensLine(text) {
492
- return text.replace(/^Tokens:[^\n]*\n?/m, "");
603
+ return text.replace(/(^\[sub-agent [^\n]+\n)Tokens:[^\n]*\n?/, "$1");
493
604
  }
494
605
  /** Effective context size of the most recent assistant turn — drives the footer indicator. */
495
- function lastContextSizeFromTurns(turns) {
606
+ /**
607
+ * Walk from the end of `turns` and return the cache-aware input-token total
608
+ * of the most recent **parent** (depth-0) assistant turn. Subagent turns
609
+ * are skipped because their context window is the child's, not the parent's
610
+ * — surfacing a subagent's `usage.input` as the footer's "context used"
611
+ * after a session ends mid-spawn would be misleading. The next prompt
612
+ * feeds the parent, so the parent's last view is what matters.
613
+ *
614
+ * `runs` is optional for backwards compatibility (older call sites and
615
+ * tests that don't have ancestry info). Without it, this falls back to the
616
+ * pre-fix behavior — depth is unknown so every assistant turn qualifies.
617
+ */
618
+ function lastContextSizeFromTurns(turns, runs = []) {
619
+ const childRunIds = /* @__PURE__ */ new Set();
620
+ for (const run of runs) if ((run.depth ?? 0) > 0) childRunIds.add(run.id);
496
621
  for (let i = turns.length - 1; i >= 0; i--) {
497
622
  const turn = turns[i];
498
- if (turn.role === "assistant" && turn.usage) return (turn.usage.input ?? 0) + (turn.usage.cacheRead ?? 0) + (turn.usage.cacheCreation ?? 0);
623
+ if (turn.role !== "assistant" || !turn.usage) continue;
624
+ if (turn.runId && childRunIds.has(turn.runId)) continue;
625
+ return (turn.usage.input ?? 0) + (turn.usage.cacheRead ?? 0) + (turn.usage.cacheCreation ?? 0);
499
626
  }
500
627
  return 0;
501
628
  }
502
629
  //#endregion
503
630
  //#region src/chat/config.ts
504
631
  /** Resolve user options into a fully-bound runtime config. Pure aside from disk reads. */
505
- function resolveConfig(options = {}) {
632
+ function resolveConfig(options) {
506
633
  const prefix = options.prefix ?? process.env.ZIDANE_PREFIX ?? ".zidane";
507
634
  const storageDir = options.storageDir ?? process.env.ZIDANE_STORAGE_DIR ?? homedir();
508
635
  const dir = resolve(storageDir, prefix);
@@ -511,22 +638,28 @@ function resolveConfig(options = {}) {
511
638
  db: resolve(dir, "sessions.db"),
512
639
  state: resolve(dir, "state.json")
513
640
  };
514
- const store = options.store ?? createTuiStore(paths.db);
641
+ if (!options.store) throw new Error("resolveConfig: `store` is required. Pass a `SessionStore` instance, or a factory — terminal hosts can use `createTuiStore` from `zidane/session/sqlite`.");
642
+ const store = typeof options.store === "function" ? options.store(paths) : options.store;
515
643
  const stateStore = createStateStore(paths.state);
516
644
  const initialState = stateStore.load();
517
645
  const providers = options.providers ?? BUILTIN_PROVIDERS;
518
- const preset = options.preset ?? basic_default;
646
+ 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.");
647
+ const agents = options.agents ?? (options.preset ? singleAgentRegistry(options.preset) : BUILTIN_AGENTS);
648
+ if (Object.keys(agents).length === 0) throw new Error("resolveConfig: `agents` registry is empty — at least one profile is required.");
519
649
  process.env.ZIDANE_CREDENTIALS_PATH = credentialsPath(dir);
520
650
  applyApiKeyEnv(dir, providers);
521
651
  const modelsFor = makeModelsResolver(providers);
522
652
  const resumeProvider = resolveResumeProvider(initialState, providers, dir);
523
653
  const initialPicked = resumeProvider ? pickInitial(resumeProvider, providers, initialState) : null;
654
+ const initialAgentId = resolveAgentId(agents, initialState.lastAgent, options.defaultAgent ?? "build");
655
+ if (!initialAgentId) throw new Error("resolveConfig: failed to resolve an initial agent id from a non-empty registry.");
524
656
  return {
525
657
  prefix,
526
658
  storageDir,
527
659
  paths,
528
660
  providers,
529
- preset,
661
+ agents,
662
+ initialAgentId,
530
663
  store,
531
664
  stateStore,
532
665
  modelsFor,
@@ -1131,7 +1264,7 @@ function catppuccinTheme(id, label, p) {
1131
1264
  const CATPPUCCIN_MOCHA = catppuccinTheme("catppuccin-mocha", "Catppuccin Mocha", MOCHA);
1132
1265
  const CATPPUCCIN_MACCHIATO = catppuccinTheme("catppuccin-macchiato", "Catppuccin Macchiato", MACCHIATO);
1133
1266
  const CATPPUCCIN_FRAPPE = catppuccinTheme("catppuccin-frappe", "Catppuccin Frappé", FRAPPE);
1134
- const CATPPUCCIN_LATTE = catppuccinTheme("catppuccin-latte", "Catppuccin Latte", LATTE);
1267
+ const CATPPUCCIN_LATTE = catppuccinTheme("catppuccin-latte", "Catppuccin Latte (light)", LATTE);
1135
1268
  //#endregion
1136
1269
  //#region src/chat/themes/vaporwave.ts
1137
1270
  const PALETTE = {
@@ -1145,6 +1278,12 @@ const PALETTE = {
1145
1278
  yellow: "#FFFB96",
1146
1279
  red: "#F43E5C",
1147
1280
  text: "#D5D8DA",
1281
+ /**
1282
+ * ~70% of `text` — used for captions / helper / "dim" text so that
1283
+ * secondary copy is visually distinct from primary copy. Matches the
1284
+ * upstream theme's sidebar foreground (`#b7c5d3`) tonality.
1285
+ */
1286
+ textDim: "#B7C5D3",
1148
1287
  textBright: "#EEFFFF",
1149
1288
  comment: "#BBBBBB",
1150
1289
  muted: "#6C6F93",
@@ -1160,7 +1299,7 @@ const VAPORWAVE_THEME = {
1160
1299
  model: PALETTE.blue,
1161
1300
  warn: PALETTE.yellow,
1162
1301
  error: PALETTE.red,
1163
- dim: PALETTE.text,
1302
+ dim: PALETTE.textDim,
1164
1303
  mute: PALETTE.muted,
1165
1304
  border: PALETTE.surface,
1166
1305
  borderActive: PALETTE.muted
@@ -1439,27 +1578,28 @@ const DEFAULT_SETTINGS = {
1439
1578
  const SettingsContext = createContext(null);
1440
1579
  function SettingsProvider({ initial, onChange, children }) {
1441
1580
  const [settings, setSettings] = useState(initial);
1581
+ const onChangeRef = useRef(onChange);
1582
+ onChangeRef.current = onChange;
1583
+ const initialMount = useRef(true);
1584
+ useEffect(() => {
1585
+ if (initialMount.current) {
1586
+ initialMount.current = false;
1587
+ return;
1588
+ }
1589
+ onChangeRef.current?.(settings);
1590
+ }, [settings]);
1442
1591
  const toggle = useCallback((key) => {
1443
- setSettings((prev) => {
1444
- const next = {
1445
- ...prev,
1446
- [key]: !prev[key]
1447
- };
1448
- onChange?.(next);
1449
- return next;
1450
- });
1451
- }, [onChange]);
1592
+ setSettings((prev) => ({
1593
+ ...prev,
1594
+ [key]: !prev[key]
1595
+ }));
1596
+ }, []);
1452
1597
  const setSetting = useCallback((key, value) => {
1453
- setSettings((prev) => {
1454
- if (prev[key] === value) return prev;
1455
- const next = {
1456
- ...prev,
1457
- [key]: value
1458
- };
1459
- onChange?.(next);
1460
- return next;
1598
+ setSettings((prev) => prev[key] === value ? prev : {
1599
+ ...prev,
1600
+ [key]: value
1461
1601
  });
1462
- }, [onChange]);
1602
+ }, []);
1463
1603
  const value = useMemo(() => ({
1464
1604
  settings,
1465
1605
  toggle,
@@ -1708,6 +1848,6 @@ function useSyntaxStyles() {
1708
1848
  return useContext(ThemeContext).syntax;
1709
1849
  }
1710
1850
  //#endregion
1711
- export { toolCallPreview as $, isOnSafelist as A, shortId as B, CATPPUCCIN_MOCHA as C, IMPLICITLY_SAFE_TOOLS as D, useSafeModeQueue as E, writeProjects as F, createTuiStore as G, useConfig as H, runOAuthLogin as I, listSessionMeta as J, eventsFromTurns as K, supportsOAuth as L, projectsFilePath as M, readProjects as N, addToSafelist as O, suggestSafelistEntry as P, titleFromTurns as Q, ageString as R, CATPPUCCIN_MACCHIATO as S, useSafeModeActions as T, resolveConfig as U, ConfigProvider as V, createStateStore as W, saveState as X, loadState as Y, stripSpawnTokensLine as Z, DEFAULT_THEME as _, piIdOf as _t, useSyntaxStyles as a, readProviderCredential as at, CATPPUCCIN_FRAPPE as b, finalizeStreamingMarkdownForOwner as c, writeCredentials as ct, DEFAULT_SETTINGS as d, cerebrasDescriptor as dt, toolResultText as et, SETTINGS_CHOICES as f, credKeyOf as ft, BUILTIN_THEMES as g, openrouterDescriptor as gt, useSettings as h, openaiDescriptor as ht, useSurfaces as i, readCredentials as it, matchesSafelistEntry as j, getSafelist as k, turnContextSize as l, BUILTIN_PROVIDERS as lt, SettingsProvider as m, modelsForDescriptor as mt, useColors as n, applyApiKeyEnv as nt, useTheme as o, removeProviderCredential as ot, SETTINGS_TOGGLES as p, getContextWindow as pt, lastContextSizeFromTurns as q, useSelectStyle as r, credentialsPath as rt, finalizeStreamingMarkdown as s, setProviderCredential as st, ThemeProvider as t, detectAuth as tt, useStreamBuffer as u, anthropicDescriptor as ut, resolveTheme as v, SafeModeProvider as w, CATPPUCCIN_LATTE as x, VAPORWAVE_THEME as y, fmtTokens as z };
1851
+ export { toolResultText as $, isOnSafelist as A, shortId as B, CATPPUCCIN_MOCHA as C, IMPLICITLY_SAFE_TOOLS as D, useSafeModeQueue as E, writeProjects as F, eventsFromTurns as G, useConfig as H, runOAuthLogin as I, loadState as J, lastContextSizeFromTurns as K, supportsOAuth as L, projectsFilePath as M, readProjects as N, addToSafelist as O, suggestSafelistEntry as P, toolCallPreview as Q, ageString as R, CATPPUCCIN_MACCHIATO as S, singleAgentRegistry as St, useSafeModeActions as T, resolveConfig as U, ConfigProvider as V, createStateStore as W, stripSpawnTokensLine as X, saveState as Y, titleFromTurns as Z, DEFAULT_THEME as _, BUILD_AGENT as _t, useSyntaxStyles as a, removeProviderCredential as at, CATPPUCCIN_FRAPPE as b, PLAN_AGENT as bt, finalizeStreamingMarkdownForOwner as c, BUILTIN_PROVIDERS as ct, DEFAULT_SETTINGS as d, credKeyOf as dt, detectAuth as et, SETTINGS_CHOICES as f, getContextWindow as ft, BUILTIN_THEMES as g, piIdOf as gt, useSettings as h, openrouterDescriptor as ht, useSurfaces as i, readProviderCredential as it, matchesSafelistEntry as j, getSafelist as k, turnContextSize as l, anthropicDescriptor as lt, SettingsProvider as m, openaiDescriptor as mt, useColors as n, credentialsPath as nt, useTheme as o, setProviderCredential as ot, SETTINGS_TOGGLES as p, modelsForDescriptor as pt, listSessionMeta as q, useSelectStyle as r, readCredentials as rt, finalizeStreamingMarkdown as s, writeCredentials as st, ThemeProvider as t, applyApiKeyEnv as tt, useStreamBuffer as u, cerebrasDescriptor as ut, resolveTheme as v, BUILTIN_AGENTS as vt, SafeModeProvider as w, CATPPUCCIN_LATTE as x, resolveAgentId as xt, VAPORWAVE_THEME as y, DEFAULT_AGENT_ID as yt, fmtTokens as z };
1712
1852
 
1713
- //# sourceMappingURL=theme-context-MungM3SY.js.map
1853
+ //# sourceMappingURL=theme-context-DQM2lx4U.js.map