zidane 5.3.0 → 5.3.2

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 (68) hide show
  1. package/README.md +2 -0
  2. package/dist/{agent-CYpPKn5Z.d.ts → agent-BXRCCHeq.d.ts} +557 -5
  3. package/dist/agent-BXRCCHeq.d.ts.map +1 -0
  4. package/dist/chat.d.ts +310 -6
  5. package/dist/chat.d.ts.map +1 -1
  6. package/dist/chat.js +2 -2
  7. package/dist/{errors-COmsomd5.js → errors-Byb0F8B9.js} +44 -2
  8. package/dist/errors-Byb0F8B9.js.map +1 -0
  9. package/dist/{index-D-cTScN3.d.ts → index-BPk8-Slm.d.ts} +81 -10
  10. package/dist/index-BPk8-Slm.d.ts.map +1 -0
  11. package/dist/{index-Cc-q1hLT.d.ts → index-CT5_p-3P.d.ts} +2 -2
  12. package/dist/{index-Cc-q1hLT.d.ts.map → index-CT5_p-3P.d.ts.map} +1 -1
  13. package/dist/index.d.ts +4 -4
  14. package/dist/index.js +10 -10
  15. package/dist/{interpolate-BhmHKD6x.js → interpolate-ERgZUxgg.js} +2 -2
  16. package/dist/{interpolate-BhmHKD6x.js.map → interpolate-ERgZUxgg.js.map} +1 -1
  17. package/dist/{login-BXVt5wuA.js → login-DrBZ15G7.js} +3 -3
  18. package/dist/{login-BXVt5wuA.js.map → login-DrBZ15G7.js.map} +1 -1
  19. package/dist/{mcp-B1psg7jf.js → mcp-DhmmJfxK.js} +16 -3
  20. package/dist/mcp-DhmmJfxK.js.map +1 -0
  21. package/dist/mcp.d.ts +1 -1
  22. package/dist/mcp.js +1 -1
  23. package/dist/{messages-DsbMYNmt.js → messages-D0xT979U.js} +631 -68
  24. package/dist/messages-D0xT979U.js.map +1 -0
  25. package/dist/{presets-tvD28pCu.js → presets-0_IRJAYF.js} +29 -10
  26. package/dist/presets-0_IRJAYF.js.map +1 -0
  27. package/dist/presets.d.ts +2 -2
  28. package/dist/presets.js +1 -1
  29. package/dist/{providers-v1Rn2rqG.js → providers-x3LZByR5.js} +38 -6
  30. package/dist/providers-x3LZByR5.js.map +1 -0
  31. package/dist/providers.d.ts +2 -2
  32. package/dist/providers.js +3 -3
  33. package/dist/session/sqlite.d.ts +1 -1
  34. package/dist/session/sqlite.js +1 -1
  35. package/dist/{session-DOJgRXvF.js → session-BHZwxmfr.js} +2 -2
  36. package/dist/{session-DOJgRXvF.js.map → session-BHZwxmfr.js.map} +1 -1
  37. package/dist/session.d.ts +1 -1
  38. package/dist/session.js +2 -2
  39. package/dist/skills.d.ts +2 -2
  40. package/dist/skills.js +1 -1
  41. package/dist/{tools-CMVruxF0.js → tools-CCsL5SCO.js} +516 -140
  42. package/dist/tools-CCsL5SCO.js.map +1 -0
  43. package/dist/tools.d.ts +3 -3
  44. package/dist/tools.js +2 -2
  45. package/dist/{transcript-anchors-eyhlGeBI.d.ts → transcript-anchors-DSk8LlWt.d.ts} +28 -4
  46. package/dist/transcript-anchors-DSk8LlWt.d.ts.map +1 -0
  47. package/dist/tui.d.ts +29 -3
  48. package/dist/tui.d.ts.map +1 -1
  49. package/dist/tui.js +365 -80
  50. package/dist/tui.js.map +1 -1
  51. package/dist/{turn-operations-Y7e15gJf.js → turn-operations-CutZin8X.js} +678 -33
  52. package/dist/turn-operations-CutZin8X.js.map +1 -0
  53. package/dist/types-IcokUOyC.js.map +1 -1
  54. package/dist/types.d.ts +2 -2
  55. package/dist/types.js +1 -1
  56. package/docs/ARCHITECTURE.md +1 -1
  57. package/docs/SKILL.md +23 -1
  58. package/package.json +1 -1
  59. package/dist/agent-CYpPKn5Z.d.ts.map +0 -1
  60. package/dist/errors-COmsomd5.js.map +0 -1
  61. package/dist/index-D-cTScN3.d.ts.map +0 -1
  62. package/dist/mcp-B1psg7jf.js.map +0 -1
  63. package/dist/messages-DsbMYNmt.js.map +0 -1
  64. package/dist/presets-tvD28pCu.js.map +0 -1
  65. package/dist/providers-v1Rn2rqG.js.map +0 -1
  66. package/dist/tools-CMVruxF0.js.map +0 -1
  67. package/dist/transcript-anchors-eyhlGeBI.d.ts.map +0 -1
  68. package/dist/turn-operations-Y7e15gJf.js.map +0 -1
@@ -1,11 +1,11 @@
1
- import { a as multiEdit, c as grep, d as resolveOldString, f as styleReplacementForVia, i as readFile$1, l as glob, n as createSpawnTool, o as listFiles, r as shell, t as writeFile$1, u as edit } from "./tools-CMVruxF0.js";
2
- import { o as errorMessage } from "./errors-COmsomd5.js";
1
+ import { a as multiEdit, c as grep, d as resolveOldString, f as styleReplacementForVia, i as readFile$1, l as glob, n as createSpawnTool, o as listFiles, r as shell, t as writeFile$1, u as edit } from "./tools-CCsL5SCO.js";
2
+ import { s as errorMessage } from "./errors-Byb0F8B9.js";
3
3
  import { n as toolResultToText } from "./types-IcokUOyC.js";
4
- import { r as normalizeMcpServers } from "./mcp-B1psg7jf.js";
5
- import { a as discoverSkills } from "./interpolate-BhmHKD6x.js";
4
+ import { r as normalizeMcpServers } from "./mcp-DhmmJfxK.js";
5
+ import { a as discoverSkills } from "./interpolate-ERgZUxgg.js";
6
6
  import { n as formatTokenUsage } from "./stats-DgOvY7wd.js";
7
- import { n as definePreset } from "./presets-tvD28pCu.js";
8
- import { a as writeFileAtomic, i as anthropic, n as openai, r as cerebras, t as openrouter } from "./providers-v1Rn2rqG.js";
7
+ import { n as definePreset, t as composePresets } from "./presets-0_IRJAYF.js";
8
+ import { a as writeFileAtomic, i as anthropic, n as openai, r as cerebras, t as openrouter } from "./providers-x3LZByR5.js";
9
9
  import { spawn } from "node:child_process";
10
10
  import { readdir, stat, writeFile } from "node:fs/promises";
11
11
  import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
@@ -253,6 +253,581 @@ function joinPrompt(parts) {
253
253
  return parts.filter((p) => typeof p === "string" && p.length > 0).join("\n\n");
254
254
  }
255
255
  //#endregion
256
+ //#region src/chat/todos.ts
257
+ const TODOWRITE_TOOL = "todowrite";
258
+ const TODOREAD_TOOL = "todoread";
259
+ /** True when `name` is one of the todo tool canonical names. */
260
+ function isTodoTool(name) {
261
+ return name === "todowrite" || name === "todoread";
262
+ }
263
+ /** `session.metadata[TODOS_METADATA_KEY]: TodosBag` — see {@link TodosBag}. */
264
+ const TODOS_METADATA_KEY = "todos";
265
+ /** `session.metadata[TODO_WRITE_COUNTS_METADATA_KEY]: CountsBag` — mirrors `TodosBag` shape. */
266
+ const TODO_WRITE_COUNTS_METADATA_KEY = "todoWriteCounts";
267
+ const TODO_STATUS_VALUES = [
268
+ "pending",
269
+ "in_progress",
270
+ "completed",
271
+ "cancelled"
272
+ ];
273
+ const TODO_STATUS_GLYPHS = {
274
+ pending: "☐",
275
+ in_progress: "◐",
276
+ completed: "☑",
277
+ cancelled: "☒"
278
+ };
279
+ const WRITE_DESCRIPTION = "Replace the active task list. Pass the **full** list of items every call — there is no partial update. The list persists across user prompts in the same session, so an aborted run with `in_progress` items reads back on the next `todoread` and you can pick up where you left off. When every item is `completed`, the list auto-clears so the next prompt starts fresh.\n\nUse the `status` field to track progress: `pending` (queued), `in_progress` (currently working), `completed` (done), `cancelled` (no longer relevant).\n\nOnly checkpoint at significant transitions:\n 1. When the user gives you a multi-step task — write the initial plan with everything `pending`.\n 2. When you transition between steps — mark the previous one `completed` and the next one `in_progress`.\n 3. When the user asks for the current status — re-emit the list unchanged so they can see it.\n\nDo NOT call this on every action. The list is for the user's situational awareness, not for self-narrating.";
280
+ const READ_DESCRIPTION = "Return the current active task list. Returns an empty list if nothing has been written yet, or if the previous batch was auto-cleared after all items completed. Use sparingly — you already see the latest list in your own `todowrite` tool_result above.";
281
+ function defaultReminder(count) {
282
+ return `(You've called todowrite ${count} times. Make sure each checkpoint reflects real progress; avoid re-planning every step.)`;
283
+ }
284
+ /**
285
+ * `true` when the run with `runId` is a subagent (has a `parentRunId`).
286
+ * Top-level runs return `false`; an unknown / missing run also returns
287
+ * `false` (defensive — should never happen in practice, but treating an
288
+ * orphan as top-level is the right fallback since the session slot is
289
+ * the more general bucket).
290
+ */
291
+ function isSubagentRun(session, runId) {
292
+ return !!session.runs.find((r) => r.id === runId)?.parentRunId;
293
+ }
294
+ /**
295
+ * Read the active list for `runId`. Top-level runs see the session-shared
296
+ * slot; subagent runs see their own. Returns a fresh `[]` (not stored in
297
+ * metadata) when no slot exists — the caller can mutate the result
298
+ * without affecting state.
299
+ */
300
+ function getTodosForRun(session, runId) {
301
+ const bag = readTodosBag(session);
302
+ if (isSubagentRun(session, runId)) {
303
+ const items = bag.byRun?.[runId];
304
+ return Array.isArray(items) ? [...items] : [];
305
+ }
306
+ return Array.isArray(bag.session) ? [...bag.session] : [];
307
+ }
308
+ /**
309
+ * Replace the active list for `runId`. Routes to the session-shared slot
310
+ * for top-level runs and to the per-run slot for subagents. Empty arrays
311
+ * delete the slot entirely so persisted metadata stays clean.
312
+ *
313
+ * Side effect: a **non-empty** write also stashes the same list into
314
+ * `bag.archive` (same routing). Empty writes never touch the archive —
315
+ * clearing the live list (whether explicitly or via the tool's auto-
316
+ * clear) preserves the "last shown" snapshot so UI surfaces can keep
317
+ * rendering what was just completed. See {@link getArchivedTodosForRun}.
318
+ */
319
+ function setTodosForRun(session, runId, items) {
320
+ const bag = readTodosBag(session);
321
+ const normalized = items.map(normalizeItem);
322
+ const subagent = isSubagentRun(session, runId);
323
+ if (subagent) {
324
+ const byRun = { ...bag.byRun ?? {} };
325
+ if (normalized.length === 0) delete byRun[runId];
326
+ else byRun[runId] = normalized;
327
+ bag.byRun = byRun;
328
+ } else if (normalized.length === 0) delete bag.session;
329
+ else bag.session = normalized;
330
+ if (normalized.length > 0) {
331
+ const archive = { ...bag.archive ?? {} };
332
+ if (subagent) {
333
+ const archiveByRun = { ...archive.byRun ?? {} };
334
+ archiveByRun[runId] = normalized;
335
+ archive.byRun = archiveByRun;
336
+ } else archive.session = normalized;
337
+ bag.archive = archive;
338
+ }
339
+ writeTodosBag(session, bag);
340
+ }
341
+ /**
342
+ * Read the archived (most-recent non-empty) list for `runId`. Same slot
343
+ * routing as {@link getTodosForRun}, but reads `bag.archive` instead of
344
+ * the live slot. Returns a fresh `[]` when no archive exists.
345
+ *
346
+ * UI surfaces use this to keep showing "what was just completed" after
347
+ * the live list auto-clears. The model never sees the archive — it's a
348
+ * read-only sidecar for human-facing rendering.
349
+ */
350
+ function getArchivedTodosForRun(session, runId) {
351
+ const archive = readTodosBag(session).archive;
352
+ if (!archive) return [];
353
+ if (isSubagentRun(session, runId)) {
354
+ const items = archive.byRun?.[runId];
355
+ return Array.isArray(items) ? [...items] : [];
356
+ }
357
+ return Array.isArray(archive.session) ? [...archive.session] : [];
358
+ }
359
+ /**
360
+ * Reconcile `session.metadata.todos.byRun` (live + archive) against
361
+ * `session.runs`. Drops subagent slots whose runId isn't in the run
362
+ * list. The top-level `session` slot is unaffected — it's session-
363
+ * scoped, not per-run, so there's no orphan to GC.
364
+ *
365
+ * Useful after `session.setRuns()` (fork / restore) or to GC stale
366
+ * metadata mutated by an external caller. Also prunes the parallel
367
+ * counter bag so the two don't drift.
368
+ *
369
+ * `dropped` reports the live-byRun orphans only (archive-only orphans
370
+ * are silently swept). The live half is the contract callers actually
371
+ * observe.
372
+ */
373
+ function pruneTodosByRun(session) {
374
+ const validRunIds = new Set(session.runs.map((r) => r.id));
375
+ const dropped = [];
376
+ const bag = readTodosBag(session);
377
+ let bagChanged = false;
378
+ if (bag.byRun) {
379
+ const nextByRun = {};
380
+ for (const [runId, items] of Object.entries(bag.byRun)) if (validRunIds.has(runId)) nextByRun[runId] = items;
381
+ else {
382
+ dropped.push(runId);
383
+ bagChanged = true;
384
+ }
385
+ if (Object.keys(nextByRun).length !== Object.keys(bag.byRun).length) bag.byRun = nextByRun;
386
+ }
387
+ if (bag.archive?.byRun) {
388
+ const nextByRun = {};
389
+ let archiveChanged = false;
390
+ for (const [runId, items] of Object.entries(bag.archive.byRun)) if (validRunIds.has(runId)) nextByRun[runId] = items;
391
+ else archiveChanged = true;
392
+ if (archiveChanged) {
393
+ bag.archive = {
394
+ ...bag.archive,
395
+ byRun: nextByRun
396
+ };
397
+ bagChanged = true;
398
+ }
399
+ }
400
+ if (bagChanged) writeTodosBag(session, bag);
401
+ const counts = readCountsBag(session);
402
+ if (counts.byRun) {
403
+ const nextByRun = {};
404
+ let changed = false;
405
+ for (const [runId, n] of Object.entries(counts.byRun)) if (validRunIds.has(runId)) nextByRun[runId] = n;
406
+ else changed = true;
407
+ if (changed) {
408
+ counts.byRun = nextByRun;
409
+ writeCountsBag(session, counts);
410
+ }
411
+ }
412
+ return { dropped };
413
+ }
414
+ /**
415
+ * Build a `Preset` carrying the `{ todowrite, todoread }` tool pair.
416
+ * Identical-payload dedup is handled inside the tool body — the
417
+ * comparison resolves against the active slot (session-shared for
418
+ * top-level runs, per-run for subagents) and intentionally does NOT
419
+ * plumb through `behavior.dedupTools`. See the `dedupIdentical` option
420
+ * doc for the rationale.
421
+ *
422
+ * By default the preset adds **no** `toolBudgets` entry — `todowrite`
423
+ * is a state-transition tool, so a hard cap is the wrong abstraction
424
+ * (it punishes the legitimate "finish the N-item plan" path). Hosts
425
+ * that want a ceiling pass `maxWritesPerRun: N` explicitly; the
426
+ * factory then plumbs a `behavior.toolBudgets.todowrite` entry
427
+ * through.
428
+ *
429
+ * Returning a `Preset` (not a bare tool map) lets the result flow
430
+ * through {@link composePresets} unchanged — todos compose with any
431
+ * other preset the same way every other preset does. `toolBudgets` is
432
+ * a tool-name-keyed record that `composePresets` deep-merges, so a
433
+ * caller's custom budget entries for other tools survive the
434
+ * layering, and a caller's override for `todowrite` itself wins by
435
+ * being placed later in the chain.
436
+ *
437
+ * ```ts
438
+ * import { basic, composePresets } from 'zidane/presets'
439
+ * import { createTodoTools } from 'zidane/chat'
440
+ *
441
+ * // Default: no budget — relies on the in-band reminder + dedup.
442
+ * createAgent({ ...composePresets(basic, createTodoTools()), provider })
443
+ *
444
+ * // Opt-in hard cap: refuse > 20 writes per run.
445
+ * createAgent({
446
+ * ...composePresets(basic, createTodoTools({ maxWritesPerRun: 20 })),
447
+ * provider,
448
+ * })
449
+ * ```
450
+ *
451
+ * For the trivial "just add the tools to an existing config" case, plain
452
+ * spread is also fine since the returned `Preset` only sets `tools`
453
+ * (and `behavior` when a budget is opted in):
454
+ *
455
+ * ```ts
456
+ * createAgent({ ...basic, ...createTodoTools(), provider })
457
+ * ```
458
+ */
459
+ function createTodoTools(options = {}) {
460
+ const maxItems = options.maxItems ?? 100;
461
+ const remindAfter = options.remindAfter ?? 3;
462
+ const reminderText = options.reminderText ?? ((count, _items) => defaultReminder(count));
463
+ const dedupIdentical = options.dedupIdentical ?? true;
464
+ const maxWritesPerRun = options.maxWritesPerRun ?? 0;
465
+ const onMaxWrites = options.onMaxWrites ?? "steer";
466
+ const tools = {
467
+ [TODOWRITE_TOOL]: createTodoWriteTool({
468
+ maxItems,
469
+ remindAfter,
470
+ reminderText,
471
+ dedupIdentical,
472
+ description: options.writeDescription ?? WRITE_DESCRIPTION
473
+ }),
474
+ [TODOREAD_TOOL]: createTodoReadTool({ description: options.readDescription ?? READ_DESCRIPTION })
475
+ };
476
+ const behavior = {};
477
+ if (maxWritesPerRun > 0) behavior.toolBudgets = { [TODOWRITE_TOOL]: {
478
+ max: maxWritesPerRun,
479
+ onExceed: onMaxWrites
480
+ } };
481
+ return definePreset(Object.keys(behavior).length > 0 ? {
482
+ tools,
483
+ behavior
484
+ } : { tools });
485
+ }
486
+ function createTodoWriteTool(opts) {
487
+ return {
488
+ spec: {
489
+ name: TODOWRITE_TOOL,
490
+ description: opts.description,
491
+ inputSchema: {
492
+ type: "object",
493
+ properties: { todos: {
494
+ type: "array",
495
+ description: "The complete task list. Replaces the prior list in full.",
496
+ maxItems: opts.maxItems,
497
+ items: {
498
+ type: "object",
499
+ properties: {
500
+ id: {
501
+ type: "string",
502
+ description: "Stable identifier (the model picks)."
503
+ },
504
+ content: {
505
+ type: "string",
506
+ description: "One-line task summary."
507
+ },
508
+ status: {
509
+ type: "string",
510
+ enum: [...TODO_STATUS_VALUES],
511
+ description: "`pending`, `in_progress`, `completed`, or `cancelled`."
512
+ }
513
+ },
514
+ required: [
515
+ "id",
516
+ "content",
517
+ "status"
518
+ ]
519
+ }
520
+ } },
521
+ required: ["todos"]
522
+ }
523
+ },
524
+ async execute(input, ctx) {
525
+ const { session, runId } = requireSessionAndRun(ctx, TODOWRITE_TOOL);
526
+ const rawItems = Array.isArray(input.todos) ? input.todos : [];
527
+ const items = sanitizeItems(rawItems, opts.maxItems);
528
+ const dropped = rawItems.length - items.length;
529
+ const byId = /* @__PURE__ */ new Map();
530
+ for (const item of items) byId.set(item.id, item);
531
+ const normalized = [...byId.values()];
532
+ const current = getTodosForRun(session, runId);
533
+ const unchanged = opts.dedupIdentical && todosEqual(current, normalized);
534
+ const count = incrementCount(session, runId);
535
+ const allCompleted = normalized.length > 0 && normalized.every((t) => t.status === "completed");
536
+ if (!unchanged) if (allCompleted) {
537
+ setTodosForRun(session, runId, normalized);
538
+ setTodosForRun(session, runId, []);
539
+ } else setTodosForRun(session, runId, normalized);
540
+ return formatWriteResult({
541
+ items: normalized,
542
+ dropped,
543
+ count,
544
+ unchanged,
545
+ cleared: allCompleted && !unchanged,
546
+ opts
547
+ });
548
+ }
549
+ };
550
+ }
551
+ function formatWriteResult(input) {
552
+ const { items, dropped, count, unchanged, cleared, opts } = input;
553
+ const lines = [];
554
+ const n = items.length;
555
+ const suffix = n === 1 ? "" : "s";
556
+ if (cleared) lines.push(`Marked ${n} item${suffix} complete — list cleared.`);
557
+ else if (unchanged) lines.push(`No change — ${n} todo item${suffix} already tracked.`);
558
+ else lines.push(`Updated ${n} todo item${suffix}.`);
559
+ if (!cleared) {
560
+ const tally = summarizeStatuses(items);
561
+ if (tally) lines.push(tally);
562
+ }
563
+ if (dropped > 0) lines.push(`Dropped ${dropped} malformed item${dropped === 1 ? "" : "s"}.`);
564
+ const reminder = opts.remindAfter > 0 && count >= opts.remindAfter ? opts.reminderText(count, items) : void 0;
565
+ if (reminder && reminder.length > 0) lines.push(reminder);
566
+ return lines.join("\n");
567
+ }
568
+ function createTodoReadTool(opts) {
569
+ return {
570
+ spec: {
571
+ name: TODOREAD_TOOL,
572
+ description: opts.description,
573
+ inputSchema: {
574
+ type: "object",
575
+ properties: {}
576
+ }
577
+ },
578
+ async execute(_input, ctx) {
579
+ const { session, runId } = requireSessionAndRun(ctx, TODOREAD_TOOL);
580
+ const items = getTodosForRun(session, runId);
581
+ if (items.length === 0) return "No todos yet — call todowrite to start tracking tasks.";
582
+ return JSON.stringify({ todos: items });
583
+ }
584
+ };
585
+ }
586
+ /**
587
+ * Both tool bodies need a `Session` + `runId`. Centralize the guards so the
588
+ * error messages stay symmetric and one fix lands in both places.
589
+ */
590
+ function requireSessionAndRun(ctx, toolName) {
591
+ if (!ctx.session) throw new Error(`${toolName}: no session on tool context — todos require a session via createSession().`);
592
+ if (!ctx.runId) throw new Error(`${toolName}: no runId on tool context.`);
593
+ return {
594
+ session: ctx.session,
595
+ runId: ctx.runId
596
+ };
597
+ }
598
+ /** Read the todos bag from session metadata, normalizing the live shape. */
599
+ function readTodosBag(session) {
600
+ const raw = session.metadata[TODOS_METADATA_KEY];
601
+ if (!isPlainObject(raw)) return {};
602
+ const archive = isPlainObject(raw.archive) ? {
603
+ session: Array.isArray(raw.archive.session) ? raw.archive.session : void 0,
604
+ byRun: isPlainObject(raw.archive.byRun) ? raw.archive.byRun : void 0
605
+ } : void 0;
606
+ return {
607
+ session: Array.isArray(raw.session) ? raw.session : void 0,
608
+ byRun: isPlainObject(raw.byRun) ? raw.byRun : void 0,
609
+ archive
610
+ };
611
+ }
612
+ function writeTodosBag(session, bag) {
613
+ const clean = {};
614
+ if (bag.session && bag.session.length > 0) clean.session = bag.session;
615
+ if (bag.byRun && Object.keys(bag.byRun).length > 0) clean.byRun = bag.byRun;
616
+ if (bag.archive) {
617
+ const archive = {};
618
+ if (bag.archive.session && bag.archive.session.length > 0) archive.session = bag.archive.session;
619
+ if (bag.archive.byRun && Object.keys(bag.archive.byRun).length > 0) archive.byRun = bag.archive.byRun;
620
+ if (archive.session || archive.byRun) clean.archive = archive;
621
+ }
622
+ session.setMeta(TODOS_METADATA_KEY, clean);
623
+ }
624
+ function readCountsBag(session) {
625
+ const raw = session.metadata[TODO_WRITE_COUNTS_METADATA_KEY];
626
+ if (!isPlainObject(raw)) return {};
627
+ return {
628
+ session: typeof raw.session === "number" ? raw.session : void 0,
629
+ byRun: isPlainObject(raw.byRun) ? raw.byRun : void 0
630
+ };
631
+ }
632
+ function writeCountsBag(session, bag) {
633
+ const clean = {};
634
+ if (typeof bag.session === "number" && bag.session > 0) clean.session = bag.session;
635
+ if (bag.byRun && Object.keys(bag.byRun).length > 0) clean.byRun = bag.byRun;
636
+ session.setMeta(TODO_WRITE_COUNTS_METADATA_KEY, clean);
637
+ }
638
+ /**
639
+ * Bump the call counter for the active slot and return the new value.
640
+ * Top-level runs increment `bag.session` (cumulative across prompts);
641
+ * subagent runs increment their own entry under `bag.byRun`. Same
642
+ * keying as {@link setTodosForRun} so the counter and the data move
643
+ * together — a freshly seeded run's count starts at 1 on its first
644
+ * write, a continuing top-level run's count picks up from where the
645
+ * prior prompt left off.
646
+ */
647
+ function incrementCount(session, runId) {
648
+ const bag = readCountsBag(session);
649
+ let next;
650
+ if (isSubagentRun(session, runId)) {
651
+ const byRun = { ...bag.byRun ?? {} };
652
+ next = (byRun[runId] ?? 0) + 1;
653
+ byRun[runId] = next;
654
+ bag.byRun = byRun;
655
+ } else {
656
+ next = (bag.session ?? 0) + 1;
657
+ bag.session = next;
658
+ }
659
+ writeCountsBag(session, bag);
660
+ return next;
661
+ }
662
+ /** Narrow guard — `Record<string, unknown>` excluding arrays / null. */
663
+ function isPlainObject(v) {
664
+ return !!v && typeof v === "object" && !Array.isArray(v);
665
+ }
666
+ function normalizeItem(item) {
667
+ return {
668
+ id: item.id,
669
+ content: item.content,
670
+ status: item.status
671
+ };
672
+ }
673
+ function sanitizeItems(raw, cap) {
674
+ const out = [];
675
+ for (const item of raw) {
676
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
677
+ const obj = item;
678
+ const id = typeof obj.id === "string" ? obj.id : void 0;
679
+ const content = typeof obj.content === "string" ? obj.content : void 0;
680
+ const status = typeof obj.status === "string" && TODO_STATUS_VALUES.includes(obj.status) ? obj.status : void 0;
681
+ if (!id || !content || !status) continue;
682
+ out.push({
683
+ id,
684
+ content,
685
+ status
686
+ });
687
+ if (out.length >= cap) break;
688
+ }
689
+ return out;
690
+ }
691
+ function summarizeStatuses(items) {
692
+ if (items.length === 0) return void 0;
693
+ const counts = {
694
+ pending: 0,
695
+ in_progress: 0,
696
+ completed: 0,
697
+ cancelled: 0
698
+ };
699
+ for (const item of items) counts[item.status] += 1;
700
+ const parts = [];
701
+ if (counts.completed) parts.push(`${counts.completed} completed`);
702
+ if (counts.in_progress) parts.push(`${counts.in_progress} in progress`);
703
+ if (counts.pending) parts.push(`${counts.pending} pending`);
704
+ if (counts.cancelled) parts.push(`${counts.cancelled} cancelled`);
705
+ return parts.length > 0 ? parts.join(" · ") : void 0;
706
+ }
707
+ /**
708
+ * Order-sensitive structural equality on todo lists. Used by the
709
+ * `dedupIdentical` short-circuit to decide whether an incoming payload
710
+ * matches the current run's stored slot.
711
+ *
712
+ * Order-sensitive on purpose: re-ordering the list is a meaningful state
713
+ * change ("the model reprioritized") and should NOT collapse to a no-op.
714
+ * Three-field comparison is exhaustive for `TodoItem` — `normalizeItem`
715
+ * strips any extra fields before they reach the slot, so we never need to
716
+ * compare beyond `{ id, content, status }`.
717
+ */
718
+ function todosEqual(a, b) {
719
+ if (a.length !== b.length) return false;
720
+ for (let i = 0; i < a.length; i++) {
721
+ const x = a[i];
722
+ const y = b[i];
723
+ if (x.id !== y.id || x.status !== y.status || x.content !== y.content) return false;
724
+ }
725
+ return true;
726
+ }
727
+ const EMPTY_TALLY = {
728
+ pending: 0,
729
+ in_progress: 0,
730
+ completed: 0,
731
+ cancelled: 0
732
+ };
733
+ const EMPTY_ACTIVE = {
734
+ runId: null,
735
+ todos: [],
736
+ archive: [],
737
+ inProgress: null,
738
+ tally: EMPTY_TALLY
739
+ };
740
+ /**
741
+ * Pick the "active" run for UI surfaces that show one slot at a time
742
+ * (todos indicator, todos modal, footer ctx indicator). The resolved
743
+ * runId then routes through {@link getTodosForRun} — top-level runs
744
+ * land on the session-shared slot, subagent runs on their own.
745
+ *
746
+ * - A currently-running run wins (latest-started one if several).
747
+ * - Otherwise the most recently appended run wins.
748
+ * - `null` when the session has no runs.
749
+ *
750
+ * Mirrors the "most recent thing that mattered" policy that drives
751
+ * `lastContextSizeFromTurns` — but walks `session.runs` rather than
752
+ * `session.turns` because the active todo slot is a function of the
753
+ * run, not of message history.
754
+ */
755
+ function pickActiveRunId(session) {
756
+ if (!session) return null;
757
+ const runs = session.runs;
758
+ if (!runs || runs.length === 0) return null;
759
+ let latestRunning = null;
760
+ for (let i = runs.length - 1; i >= 0; i--) if (runs[i].status === "running") {
761
+ latestRunning = runs[i].id;
762
+ break;
763
+ }
764
+ if (latestRunning) return latestRunning;
765
+ return runs[runs.length - 1].id;
766
+ }
767
+ /**
768
+ * Pure selector — derive the active-todos state from a session snapshot.
769
+ * Used by {@link useActiveTodos} and directly testable without React.
770
+ *
771
+ * Resolves both `todos` (live, model-facing) and `archive` (last
772
+ * non-empty snapshot for UI fallback). The tally folds over whichever
773
+ * list is non-empty; `inProgress` is sourced strictly from `todos`
774
+ * because an archived in-progress item is by definition stale.
775
+ */
776
+ function selectActiveTodos(session) {
777
+ const runId = pickActiveRunId(session);
778
+ if (!session || !runId) return EMPTY_ACTIVE;
779
+ const todos = getTodosForRun(session, runId);
780
+ const archive = getArchivedTodosForRun(session, runId);
781
+ const display = todos.length > 0 ? todos : archive;
782
+ if (display.length === 0) return {
783
+ runId,
784
+ todos,
785
+ archive,
786
+ inProgress: null,
787
+ tally: EMPTY_TALLY
788
+ };
789
+ const tally = {
790
+ pending: 0,
791
+ in_progress: 0,
792
+ completed: 0,
793
+ cancelled: 0
794
+ };
795
+ let inProgress = null;
796
+ for (const t of display) tally[t.status] += 1;
797
+ if (todos.length > 0) {
798
+ for (const t of todos) if (t.status === "in_progress") {
799
+ inProgress = t;
800
+ break;
801
+ }
802
+ }
803
+ return {
804
+ runId,
805
+ todos,
806
+ archive,
807
+ inProgress,
808
+ tally
809
+ };
810
+ }
811
+ /**
812
+ * React hook — active-todos state for the supplied session. Recomputes
813
+ * on every parent re-render; the work is O(runs + todos), both bounded
814
+ * by small constants in practice.
815
+ *
816
+ * Why no memoization: `Session.runs` is mutated in place by `completeRun`
817
+ * / `abortRun` (status flips without changing array length or item
818
+ * identity), so any memo keyed off length / bag identity would silently
819
+ * return stale state when e.g. a child subagent run completes and the
820
+ * "active" run should reflow to the parent. The selector is cheap
821
+ * enough that running it every parent render is the simpler, correct
822
+ * answer — see `selectActiveTodos` for the O(n) cost.
823
+ *
824
+ * The hook lives in `chat/` so a GUI consumer can mount it verbatim.
825
+ * Renderer-agnostic — no `@opentui/*` imports.
826
+ */
827
+ function useActiveTodos(session) {
828
+ return selectActiveTodos(session);
829
+ }
830
+ //#endregion
256
831
  //#region src/chat/agents.ts
257
832
  /**
258
833
  * Resolve a profile's `accent` token to a concrete hex color via the
@@ -314,7 +889,9 @@ const DEFAULT_PERSIST_EXCLUDE_TOOLS = [
314
889
  "skills_read",
315
890
  "present_plan",
316
891
  "ask_user",
317
- "spawn"
892
+ "spawn",
893
+ "todowrite",
894
+ "todoread"
318
895
  ];
319
896
  /**
320
897
  * Token-saving `AgentBehavior` defaults shared by the built-in profiles.
@@ -370,7 +947,7 @@ const BUILD_AGENT = {
370
947
  label: "Build",
371
948
  description: "full tool access — read, write, edit, shell, and spawn subagents",
372
949
  accent: "accent",
373
- preset: definePreset({
950
+ preset: composePresets(definePreset({
374
951
  name: "build",
375
952
  system: buildBuildSystem(),
376
953
  behavior: { ...SHARED_BEHAVIOR },
@@ -378,7 +955,7 @@ const BUILD_AGENT = {
378
955
  ...BUILD_TOOLS,
379
956
  spawn: createSpawnTool({ persist: true })
380
957
  }
381
- })
958
+ }), createTodoTools())
382
959
  };
383
960
  /**
384
961
  * Plan agent — read-only exploration mode. Locked down to file reading and
@@ -1548,6 +2125,12 @@ const KEYBINDING_DEFS = [
1548
2125
  label: "effort",
1549
2126
  description: "open the reasoning-effort picker (when the active model supports it)"
1550
2127
  },
2128
+ {
2129
+ action: "openTodos",
2130
+ default: "ctrl+t",
2131
+ label: "todos",
2132
+ description: "open the active run's todo list (the agent's `todowrite` checkpoints)"
2133
+ },
1551
2134
  {
1552
2135
  action: "cycleAgent",
1553
2136
  default: "shift+tab",
@@ -2599,10 +3182,6 @@ function eventsFromTurns(turns, runs = []) {
2599
3182
  pushSpawnStart(run);
2600
3183
  openStack.push(run);
2601
3184
  }
2602
- if (depth === 0 && lastDepthAtEmission === 0) events.push({
2603
- kind: "separator",
2604
- text: ""
2605
- });
2606
3185
  const subagentTag = depth > 0 && turn.runId ? {
2607
3186
  childId: labelFor(turn.runId),
2608
3187
  depth
@@ -2613,11 +3192,17 @@ function eventsFromTurns(turns, runs = []) {
2613
3192
  };
2614
3193
  if (turn.role === "user") {
2615
3194
  for (const block of turn.content) if (block.type === "text" && block.text.trim()) {
2616
- if (depth === 0) events.push({
2617
- kind: "user-prompt",
2618
- text: block.text,
2619
- turnId: turn.id
2620
- });
3195
+ if (depth === 0) {
3196
+ if (lastDepthAtEmission === 0) events.push({
3197
+ kind: "separator",
3198
+ text: ""
3199
+ });
3200
+ events.push({
3201
+ kind: "user-prompt",
3202
+ text: block.text,
3203
+ turnId: turn.id
3204
+ });
3205
+ }
2621
3206
  } else if (block.type === "tool_result") {
2622
3207
  const tool = toolByCallId.get(block.callId);
2623
3208
  const raw = toolResultText(block.output);
@@ -4137,7 +4722,9 @@ const DEFAULT_SETTINGS = {
4137
4722
  showEditDiffs: true,
4138
4723
  targetFps: 60,
4139
4724
  allowInteraction: true,
4140
- smoothStreaming: true
4725
+ smoothStreaming: true,
4726
+ showTodoIndicator: true,
4727
+ showThrobber: false
4141
4728
  };
4142
4729
  /**
4143
4730
  * Hard-clamp a `targetFps` value to a safe range before handing it to
@@ -4252,6 +4839,16 @@ const SETTINGS_TOGGLES = [
4252
4839
  key: "smoothStreaming",
4253
4840
  label: "Smooth streaming",
4254
4841
  description: "drip-feed streamed text character-by-character at a steady cadence (typewriter effect)"
4842
+ },
4843
+ {
4844
+ key: "showTodoIndicator",
4845
+ label: "Todo indicator",
4846
+ description: "show the subtle \"in progress\" todo line above the prompt (modal stays accessible regardless)"
4847
+ },
4848
+ {
4849
+ key: "showThrobber",
4850
+ label: "Streaming throbber",
4851
+ description: "animated gradient glyphs at the transcript tail while the assistant is working"
4255
4852
  }
4256
4853
  ];
4257
4854
  const SETTINGS_CHOICES = [
@@ -5981,7 +6578,7 @@ function getSafelist(dataDir, projectDir) {
5981
6578
  return readProjects(dataDir)[projectDir]?.safelist ?? [];
5982
6579
  }
5983
6580
  /**
5984
- * Tools that always pass without prompting. Two categories:
6581
+ * Tools that always pass without prompting. Three categories:
5985
6582
  *
5986
6583
  * - **Pure reads** (`read_file`, `list_files`, `glob`, `grep`) — no
5987
6584
  * side effects on disk or the model's own state.
@@ -5989,6 +6586,12 @@ function getSafelist(dataDir, projectDir) {
5989
6586
  * itself surfaces a TUI picker the user has to answer, so prompting
5990
6587
  * for approval first would mean "approve this prompt before you see
5991
6588
  * the prompt": redundant + breaks the flow. The picker IS the gate.
6589
+ * - **Todos** (`todowrite`, `todoread`) — write to a slot on
6590
+ * `session.metadata.todos` and nothing else. No shell, no disk, no
6591
+ * network. The model uses these for checkpointing every few steps;
6592
+ * a per-call approval prompt would drown the conversation in
6593
+ * interruptions that buy no safety since the tool's surface is
6594
+ * read/write on a metadata bag we already trust the agent with.
5992
6595
  *
5993
6596
  * Users who want to gate any of these must disable safe-mode entirely
5994
6597
  * (or fork this list in their own embedding).
@@ -5999,7 +6602,9 @@ const IMPLICITLY_SAFE_TOOLS = [
5999
6602
  "glob",
6000
6603
  "grep",
6001
6604
  "ask_user",
6002
- "present_plan"
6605
+ "present_plan",
6606
+ "todowrite",
6607
+ "todoread"
6003
6608
  ];
6004
6609
  /** Common input keys carrying the "primary argument" we scope safelists on. */
6005
6610
  const PRIMARY_ARG_KEYS = [
@@ -6515,15 +7120,16 @@ const TICK_INTERVAL_MS = 16;
6515
7120
  /**
6516
7121
  * Smooth-streaming display rate, in characters per second.
6517
7122
  *
6518
- * - {@link SMOOTH_BASE_CPS} — the cap for "calm" streaming, when the
6519
- * buffer is close to caught-up to the provider. ~200 CPS is a fast
6520
- * typist's pace; comfortably readable without feeling like a deliberate
7123
+ * - {@link SMOOTH_BASE_CPS} — calm-state cap. ~200 CPS is a fast typist's
7124
+ * pace; comfortably readable without feeling like a deliberate
6521
7125
  * typewriter performance.
6522
- * - {@link SMOOTH_BURST_CPS} — when the buffer has been falling behind
6523
- * the provider (backlog > {@link SMOOTH_BURST_BACKLOG_CHARS}), the
6524
- * ticker accelerates to drain ~3x as fast. Keeps the typewriter feel
6525
- * while preventing the display from lagging multiple seconds behind
6526
- * the actual response.
7126
+ * - {@link SMOOTH_BURST_CPS} — cap once the buffer is the full
7127
+ * {@link SMOOTH_BURST_BACKLOG_CHARS} behind the provider. The rate
7128
+ * ramps continuously from base to burst across that range (see
7129
+ * {@link smoothCharsForTick}) so the typewriter cadence never visibly
7130
+ * "steps up" mid-stream — the old step function (BASE for <100,
7131
+ * BURST for ≥100) tripled the chars-per-frame the instant the
7132
+ * threshold was crossed, which read as the text jumping.
6527
7133
  * - {@link SMOOTH_INSTANT_BACKLOG_CHARS} — failsafe. If the buffer
6528
7134
  * somehow accumulates >2K chars (paste-heavy responses, model dumping
6529
7135
  * pre-computed text, etc.) the ticker stops smoothing entirely for
@@ -6532,7 +7138,7 @@ const TICK_INTERVAL_MS = 16;
6532
7138
  */
6533
7139
  const SMOOTH_BASE_CPS = 200;
6534
7140
  const SMOOTH_BURST_CPS = 600;
6535
- const SMOOTH_BURST_BACKLOG_CHARS = 100;
7141
+ const SMOOTH_BURST_BACKLOG_CHARS = 400;
6536
7142
  const SMOOTH_INSTANT_BACKLOG_CHARS = 2e3;
6537
7143
  const PARENT_OWNER = "parent";
6538
7144
  function emptyBucket(owner, depth) {
@@ -6656,11 +7262,19 @@ function turnContextSize(usage) {
6656
7262
  * current backlog of one bucket. Caps at the bucket length (never
6657
7263
  * over-drains) and bypasses smoothing entirely once the backlog goes
6658
7264
  * past {@link SMOOTH_INSTANT_BACKLOG_CHARS}.
7265
+ *
7266
+ * The CPS ramps continuously from {@link SMOOTH_BASE_CPS} at zero backlog
7267
+ * to {@link SMOOTH_BURST_CPS} at {@link SMOOTH_BURST_BACKLOG_CHARS},
7268
+ * then plateaus at burst up to the instant cliff. Continuous (vs. the
7269
+ * legacy step at the burst threshold) so the chars-per-frame count never
7270
+ * jumps mid-stream — visible jumps in the typewriter cadence read as the
7271
+ * text itself jumping.
6659
7272
  */
6660
7273
  function smoothCharsForTick(backlog) {
6661
7274
  if (backlog <= 0) return 0;
6662
7275
  if (backlog >= SMOOTH_INSTANT_BACKLOG_CHARS) return backlog;
6663
- return Math.max(1, Math.min(backlog, Math.ceil((backlog >= SMOOTH_BURST_BACKLOG_CHARS ? SMOOTH_BURST_CPS : SMOOTH_BASE_CPS) * TICK_INTERVAL_MS / 1e3)));
7276
+ const cps = SMOOTH_BASE_CPS + Math.min(1, backlog / SMOOTH_BURST_BACKLOG_CHARS) * (SMOOTH_BURST_CPS - SMOOTH_BASE_CPS);
7277
+ return Math.max(1, Math.min(backlog, Math.ceil(cps * TICK_INTERVAL_MS / 1e3)));
6664
7278
  }
6665
7279
  /**
6666
7280
  * Slice `n` chars off the front of `buf` without splitting a UTF-16
@@ -7044,6 +7658,37 @@ const TOOL_DISPLAY = {
7044
7658
  };
7045
7659
  }
7046
7660
  },
7661
+ todowrite: {
7662
+ displayName: "Todos",
7663
+ format: (input) => {
7664
+ const todos = Array.isArray(input.todos) ? input.todos : null;
7665
+ if (!todos) return null;
7666
+ const counts = {
7667
+ pending: 0,
7668
+ in_progress: 0,
7669
+ completed: 0,
7670
+ cancelled: 0
7671
+ };
7672
+ for (const t of todos) {
7673
+ if (!t || typeof t !== "object") continue;
7674
+ const status = t.status;
7675
+ if (typeof status === "string" && status in counts) counts[status] += 1;
7676
+ }
7677
+ const meta = [];
7678
+ if (counts.completed) meta.push(`${counts.completed} done`);
7679
+ if (counts.in_progress) meta.push(`${counts.in_progress} in progress`);
7680
+ if (counts.pending) meta.push(`${counts.pending} pending`);
7681
+ if (counts.cancelled) meta.push(`${counts.cancelled} cancelled`);
7682
+ return {
7683
+ target: `${todos.length} item${todos.length === 1 ? "" : "s"}`,
7684
+ meta
7685
+ };
7686
+ }
7687
+ },
7688
+ todoread: {
7689
+ displayName: "Todos",
7690
+ format: () => ({ target: "read" })
7691
+ },
7047
7692
  ask_user: {
7048
7693
  displayName: "Ask user",
7049
7694
  format: (input) => {
@@ -7288,6 +7933,6 @@ function countNeighbors(turnIds, turnId) {
7288
7933
  };
7289
7934
  }
7290
7935
  //#endregion
7291
- export { useMcpAuthDispatch as $, bootTick as $n, isVisible as $t, getSafelist as A, KEYBINDING_DEF_BY_ACTION as An, ACTIONS_WITH_CARE_DOCTRINE as Ar, clampFps as At, supportsOAuth as B, uniqueSkillNamesFromReferences as Bn, buildBuildSystem as Br, CATPPUCCIN_MOCHA as Bt, resolveSessionExportTarget as C, maskToOutcomeKinds as Cn, BUILTIN_AGENTS as Cr, shortId as Ct, useSafeModeQueue as D, findGitRoot$1 as Dn, accentColor as Dr, SETTINGS_CHOICES as Dt, useSafeModeActions as E, summarizeOutcomes as En, PLAN_AGENT as Er, DEFAULT_SETTINGS as Et, suggestSafelistEntry as F, parseBindingSpec as Fn, INTERACTION_GUIDANCE_NO_PROMPTS as Fr, resolveTheme as Ft, defaultMcpsConfigPaths as G, collectReferences as Gn, ConfigProvider as Gt, filterModelCatalog as H, createFilesCompletionProvider as Hn, envSection as Hr, DiscoveryProvider as Ht, writeProjects as I, readKeybindings as In, PLAN_MODE_DOCTRINE as Ir, VAPORWAVE_THEME as It, projectUserPaths as J, useCompletion as Jn, createStateStore as Jt, discoverProjectMcps as K, findActiveTrigger as Kn, useConfig as Kt, splitPromptSegments as L, stripJsonComments as Ln, PLAN_MODE_DOCTRINE_NO_PROMPTS as Lr, CATPPUCCIN_FRAPPE as Lt, matchesSafelistEntry as M, keybindingsPath as Mn, DOING_TASKS_DOCTRINE as Mr, BUILTIN_THEMES as Mt, projectsFilePath as N, matchesBinding as Nn, IDENTITY_PREFIX as Nr, DEFAULT_THEME as Nt, IMPLICITLY_SAFE_TOOLS as O, DEFAULT_KEYBINDINGS as On, resolveAgentId as Or, SETTINGS_TOGGLES as Ot, readProjects as P, mergeKeybindings as Pn, INTERACTION_GUIDANCE as Pr, resolveChipColor as Pt, McpAuthProvider as Q, bootProfileEnabled as Qn, isTurnHighlighted as Qt, formatPathForCwd as R, SKILLS_TRIGGER as Rn, SUBAGENT_GUIDANCE as Rr, CATPPUCCIN_LATTE as Rt, renderSession as S, buildEditOutcomesAnnotation as Sn, BUILD_AGENT as Sr, fmtTokens as St, SafeModeProvider as T, resolveApprovalForPayload as Tn, DEFAULT_PERSIST_EXCLUDE_TOOLS as Tr, useEnabledToggleSet as Tt, indexOfEntry as U, uniqueFilesFromReferences as Un, useDiscovery as Ut, buildModelCatalog as V, FILES_TRIGGER as Vn, buildPlanSystem as Vr, createDiscoverySlot as Vt, buildMcpServers as W, applyInsert as Wn, useDiscoveryOptional as Wt, mcpCredentialsPath as X, buildLinearRamp as Xn, eventsFromTurns as Xt, createFileMcpCredentialStore as Y, blendHsl as Yn, deriveSessionTitle as Yt, patchMcpCredential as Z, tryOpenBrowser as Zn, isEditErrorResult as Zt, turnContextSize as _, extractEditPayload as _n, modelSupportsReasoning as _r, truncateTrailing as _t, computeTurnAnchors as a, selectableTurnIds as an, readProviderCredential as ar, InteractionsProvider as at, defaultSkillScanPaths as b, splitLines as bn, openrouterDescriptor as br, ageString as bt, formatToolCall as c, titleFromTurns as cn, writeCredentials as cr, createInteractionTools as ct, useSelectStyle as d, turnSelectionOwnership as dn, anthropicDescriptor as dr, pendingInteractionsFromTurns as dt, lastContextSizeFromTurns as en, shouldAutoCompact as er, useMcpAuthState as et, useSurfaces as f, applyEditPayload as fn, cerebrasDescriptor as fr, serializeInteractionResponse as ft, finalizeStreamingMarkdownForOwner as g, computeLineDiff as gn, getModelInfo as gr, hintsLength as gt, finalizeStreamingMarkdown as h, computeInlineDiff as hn, getContextWindow as hr, clipHintsToWidth as ht, turnAsText as i, saveState as in, readCredentials as ir, ASK_USER_TOOL as it, isOnSafelist as j, ensureKeybindingsFile as jn, COMMUNICATION_DOCTRINE as jr, useSettings as jt, addToSafelist as k, KEYBINDING_DEFS as kn, singleAgentRegistry as kr, SettingsProvider as kt, ThemeProvider as l, toolCallPreview as ln, BUILTIN_PROVIDERS as lr, isInteractionTool as lt, useTheme as m, buildUnifiedDiff as mn, effectiveContextWindow as mr, useInteractionsQueue as mt, deleteTurnSafely as n, loadState as nn, applyApiKeyEnv as nr, reduceMcpAuth as nt, TOOL_DISPLAY as o, stripSpawnTokensLine as on, removeProviderCredential as or, PRESENT_PLAN_TOOL as ot, useSyntaxStyles as p, buildContextualDiff as pn, credKeyOf as pr, useInteractionsActions as pt, parseMcpsFile as q, mergeReferences as qn, resolveConfig as qt, truncateTurnsAt as r, marginTopFor as rn, credentialsPath as rr, splitMarkdownCodeBlocks as rt, displayNameFor as s, sumRunCosts as sn, setProviderCredential as sr, buildResumedToolResultsTurn as st, countNeighbors as t, listSessionMeta as tn, detectAuth as tr, getMcpAuthStatus as tt, useColors as u, toolResultText as un, OUTPUT_RESERVE_TOKENS as ur, makeRequestInteraction as ut, useStreamBuffer as v, filetypeFromPath as vn, modelsForDescriptor as vr, cleanTitle as vt, writeSessionExport as w, parseEditOutcomesFromResult as wn, DEFAULT_AGENT_ID as wr, listProjectFiles as wt, discoverProjectSkills as x, tokenize as xn, piIdOf as xr, compactPath as xt, buildSkillsConfig as y, previewEditPayload as yn, openaiDescriptor as yr, generateSessionTitle as yt, runOAuthLogin as z, createSkillsCompletionProvider as zn, TOKEN_DISCIPLINE_DOCTRINE as zr, CATPPUCCIN_MACCHIATO as zt };
7936
+ export { useMcpAuthDispatch as $, bootTick as $n, TOKEN_DISCIPLINE_DOCTRINE as $r, isVisible as $t, getSafelist as A, KEYBINDING_DEF_BY_ACTION as An, TODOREAD_TOOL as Ar, clampFps as At, supportsOAuth as B, uniqueSkillNamesFromReferences as Bn, pruneTodosByRun as Br, CATPPUCCIN_MOCHA as Bt, resolveSessionExportTarget as C, maskToOutcomeKinds as Cn, BUILTIN_AGENTS as Cr, shortId as Ct, useSafeModeQueue as D, findGitRoot$1 as Dn, accentColor as Dr, SETTINGS_CHOICES as Dt, useSafeModeActions as E, summarizeOutcomes as En, PLAN_AGENT as Er, DEFAULT_SETTINGS as Et, suggestSafelistEntry as F, parseBindingSpec as Fn, createTodoTools as Fr, resolveTheme as Ft, defaultMcpsConfigPaths as G, collectReferences as Gn, COMMUNICATION_DOCTRINE as Gr, ConfigProvider as Gt, filterModelCatalog as H, createFilesCompletionProvider as Hn, setTodosForRun as Hr, DiscoveryProvider as Ht, writeProjects as I, readKeybindings as In, getArchivedTodosForRun as Ir, VAPORWAVE_THEME as It, projectUserPaths as J, useCompletion as Jn, INTERACTION_GUIDANCE as Jr, createStateStore as Jt, discoverProjectMcps as K, findActiveTrigger as Kn, DOING_TASKS_DOCTRINE as Kr, useConfig as Kt, splitPromptSegments as L, stripJsonComments as Ln, getTodosForRun as Lr, CATPPUCCIN_FRAPPE as Lt, matchesSafelistEntry as M, keybindingsPath as Mn, TODOWRITE_TOOL as Mr, BUILTIN_THEMES as Mt, projectsFilePath as N, matchesBinding as Nn, TODO_STATUS_GLYPHS as Nr, DEFAULT_THEME as Nt, IMPLICITLY_SAFE_TOOLS as O, DEFAULT_KEYBINDINGS as On, resolveAgentId as Or, SETTINGS_TOGGLES as Ot, readProjects as P, mergeKeybindings as Pn, TODO_WRITE_COUNTS_METADATA_KEY as Pr, resolveChipColor as Pt, McpAuthProvider as Q, bootProfileEnabled as Qn, SUBAGENT_GUIDANCE as Qr, isTurnHighlighted as Qt, formatPathForCwd as R, SKILLS_TRIGGER as Rn, isTodoTool as Rr, CATPPUCCIN_LATTE as Rt, renderSession as S, buildEditOutcomesAnnotation as Sn, BUILD_AGENT as Sr, fmtTokens as St, SafeModeProvider as T, resolveApprovalForPayload as Tn, DEFAULT_PERSIST_EXCLUDE_TOOLS as Tr, useEnabledToggleSet as Tt, indexOfEntry as U, uniqueFilesFromReferences as Un, useActiveTodos as Ur, useDiscovery as Ut, buildModelCatalog as V, FILES_TRIGGER as Vn, selectActiveTodos as Vr, createDiscoverySlot as Vt, buildMcpServers as W, applyInsert as Wn, ACTIONS_WITH_CARE_DOCTRINE as Wr, useDiscoveryOptional as Wt, mcpCredentialsPath as X, buildLinearRamp as Xn, PLAN_MODE_DOCTRINE as Xr, eventsFromTurns as Xt, createFileMcpCredentialStore as Y, blendHsl as Yn, INTERACTION_GUIDANCE_NO_PROMPTS as Yr, deriveSessionTitle as Yt, patchMcpCredential as Z, tryOpenBrowser as Zn, PLAN_MODE_DOCTRINE_NO_PROMPTS as Zr, isEditErrorResult as Zt, turnContextSize as _, extractEditPayload as _n, modelSupportsReasoning as _r, truncateTrailing as _t, computeTurnAnchors as a, selectableTurnIds as an, readProviderCredential as ar, InteractionsProvider as at, defaultSkillScanPaths as b, splitLines as bn, openrouterDescriptor as br, ageString as bt, formatToolCall as c, titleFromTurns as cn, writeCredentials as cr, createInteractionTools as ct, useSelectStyle as d, turnSelectionOwnership as dn, anthropicDescriptor as dr, pendingInteractionsFromTurns as dt, buildBuildSystem as ei, lastContextSizeFromTurns as en, shouldAutoCompact as er, useMcpAuthState as et, useSurfaces as f, applyEditPayload as fn, cerebrasDescriptor as fr, serializeInteractionResponse as ft, finalizeStreamingMarkdownForOwner as g, computeLineDiff as gn, getModelInfo as gr, hintsLength as gt, finalizeStreamingMarkdown as h, computeInlineDiff as hn, getContextWindow as hr, clipHintsToWidth as ht, turnAsText as i, saveState as in, readCredentials as ir, ASK_USER_TOOL as it, isOnSafelist as j, ensureKeybindingsFile as jn, TODOS_METADATA_KEY as jr, useSettings as jt, addToSafelist as k, KEYBINDING_DEFS as kn, singleAgentRegistry as kr, SettingsProvider as kt, ThemeProvider as l, toolCallPreview as ln, BUILTIN_PROVIDERS as lr, isInteractionTool as lt, useTheme as m, buildUnifiedDiff as mn, effectiveContextWindow as mr, useInteractionsQueue as mt, deleteTurnSafely as n, envSection as ni, loadState as nn, applyApiKeyEnv as nr, reduceMcpAuth as nt, TOOL_DISPLAY as o, stripSpawnTokensLine as on, removeProviderCredential as or, PRESENT_PLAN_TOOL as ot, useSyntaxStyles as p, buildContextualDiff as pn, credKeyOf as pr, useInteractionsActions as pt, parseMcpsFile as q, mergeReferences as qn, IDENTITY_PREFIX as qr, resolveConfig as qt, truncateTurnsAt as r, marginTopFor as rn, credentialsPath as rr, splitMarkdownCodeBlocks as rt, displayNameFor as s, sumRunCosts as sn, setProviderCredential as sr, buildResumedToolResultsTurn as st, countNeighbors as t, buildPlanSystem as ti, listSessionMeta as tn, detectAuth as tr, getMcpAuthStatus as tt, useColors as u, toolResultText as un, OUTPUT_RESERVE_TOKENS as ur, makeRequestInteraction as ut, useStreamBuffer as v, filetypeFromPath as vn, modelsForDescriptor as vr, cleanTitle as vt, writeSessionExport as w, parseEditOutcomesFromResult as wn, DEFAULT_AGENT_ID as wr, listProjectFiles as wt, discoverProjectSkills as x, tokenize as xn, piIdOf as xr, compactPath as xt, buildSkillsConfig as y, previewEditPayload as yn, openaiDescriptor as yr, generateSessionTitle as yt, runOAuthLogin as z, createSkillsCompletionProvider as zn, pickActiveRunId as zr, CATPPUCCIN_MACCHIATO as zt };
7292
7937
 
7293
- //# sourceMappingURL=turn-operations-Y7e15gJf.js.map
7938
+ //# sourceMappingURL=turn-operations-CutZin8X.js.map