zidane 5.4.3 → 5.5.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.
Files changed (76) hide show
  1. package/README.md +30 -1
  2. package/dist/{agent-Yu8uhpy-.d.ts → agent-CvImMxMQ.d.ts} +183 -3
  3. package/dist/agent-CvImMxMQ.d.ts.map +1 -0
  4. package/dist/chat.d.ts +93 -15
  5. package/dist/chat.d.ts.map +1 -1
  6. package/dist/chat.js +3 -2
  7. package/dist/contexts/docker.d.ts +1 -1
  8. package/dist/contexts-DhmMlT2W.js +472 -0
  9. package/dist/contexts-DhmMlT2W.js.map +1 -0
  10. package/dist/contexts.d.ts +3 -3
  11. package/dist/contexts.js +1 -1
  12. package/dist/{index-DklfxeYy.d.ts → index-B0uc2C5x.d.ts} +3 -3
  13. package/dist/{index-DklfxeYy.d.ts.map → index-B0uc2C5x.d.ts.map} +1 -1
  14. package/dist/{index-BiO_5Hm4.d.ts → index-CbS75MD3.d.ts} +2 -2
  15. package/dist/index-CbS75MD3.d.ts.map +1 -0
  16. package/dist/{index-j9tY28ah.d.ts → index-CtXksgqb.d.ts} +60 -4
  17. package/dist/index-CtXksgqb.d.ts.map +1 -0
  18. package/dist/index.d.ts +6 -6
  19. package/dist/index.js +8 -8
  20. package/dist/{interpolate-CmtjEyRJ.js → interpolate-BaaKaKzN.js} +2 -2
  21. package/dist/{interpolate-CmtjEyRJ.js.map → interpolate-BaaKaKzN.js.map} +1 -1
  22. package/dist/{login-DxyAERe1.js → login-iTy-0wYz.js} +2 -2
  23. package/dist/{login-DxyAERe1.js.map → login-iTy-0wYz.js.map} +1 -1
  24. package/dist/mcp.d.ts +1 -1
  25. package/dist/{presets-D9IbaI40.js → presets-h6UWhghO.js} +3 -2
  26. package/dist/presets-h6UWhghO.js.map +1 -0
  27. package/dist/presets.d.ts +2 -2
  28. package/dist/presets.js +1 -1
  29. package/dist/{providers-CEzRFYtS.js → providers-G0VBZK9j.js} +2 -2
  30. package/dist/{providers-CEzRFYtS.js.map → providers-G0VBZK9j.js.map} +1 -1
  31. package/dist/providers.d.ts +1 -1
  32. package/dist/providers.js +1 -1
  33. package/dist/session/sqlite.d.ts +1 -1
  34. package/dist/session/sqlite.d.ts.map +1 -1
  35. package/dist/session/sqlite.js +1 -0
  36. package/dist/session/sqlite.js.map +1 -1
  37. package/dist/{session-kwsNnOmt.js → session-CbkiJDlH.js} +2 -1
  38. package/dist/session-CbkiJDlH.js.map +1 -0
  39. package/dist/session.d.ts +1 -1
  40. package/dist/session.js +1 -1
  41. package/dist/skills.d.ts +2 -2
  42. package/dist/skills.js +1 -1
  43. package/dist/{tools-BK2vG9UX.js → tools-D_icxa-V.js} +668 -256
  44. package/dist/tools-D_icxa-V.js.map +1 -0
  45. package/dist/tools.d.ts +3 -3
  46. package/dist/tools.js +2 -2
  47. package/dist/{transcript-anchors-DnaBcJej.d.ts → transcript-anchors-3FFw2xuk.d.ts} +49 -10
  48. package/dist/transcript-anchors-3FFw2xuk.d.ts.map +1 -0
  49. package/dist/tui.d.ts +27 -5
  50. package/dist/tui.d.ts.map +1 -1
  51. package/dist/tui.js +239 -39
  52. package/dist/tui.js.map +1 -1
  53. package/dist/{turn-operations-OzKEOXul.js → turn-operations-CtgBlBHn.js} +178 -79
  54. package/dist/turn-operations-CtgBlBHn.js.map +1 -0
  55. package/dist/types-IcokUOyC.js.map +1 -1
  56. package/dist/types-KukEp-mi.d.ts +253 -0
  57. package/dist/types-KukEp-mi.d.ts.map +1 -0
  58. package/dist/types.d.ts +4 -4
  59. package/docs/ARCHITECTURE.md +21 -0
  60. package/docs/CHAT.md +3 -1
  61. package/docs/RUN_IN_BACKGROUND.md +612 -0
  62. package/docs/SKILL.md +59 -0
  63. package/docs/TUI.md +16 -2
  64. package/package.json +2 -2
  65. package/dist/agent-Yu8uhpy-.d.ts.map +0 -1
  66. package/dist/contexts-BwiHIr2w.js +0 -129
  67. package/dist/contexts-BwiHIr2w.js.map +0 -1
  68. package/dist/index-BiO_5Hm4.d.ts.map +0 -1
  69. package/dist/index-j9tY28ah.d.ts.map +0 -1
  70. package/dist/presets-D9IbaI40.js.map +0 -1
  71. package/dist/session-kwsNnOmt.js.map +0 -1
  72. package/dist/tools-BK2vG9UX.js.map +0 -1
  73. package/dist/transcript-anchors-DnaBcJej.d.ts.map +0 -1
  74. package/dist/turn-operations-OzKEOXul.js.map +0 -1
  75. package/dist/types-Ce78ds4h.d.ts +0 -88
  76. package/dist/types-Ce78ds4h.d.ts.map +0 -1
package/dist/tui.js CHANGED
@@ -1,15 +1,15 @@
1
- import { E as cleanupPersistedSession, O as resolvePersistDir, p as createAgent } from "./tools-BK2vG9UX.js";
1
+ import { A as resolvePersistDir, B as formatTaskStatus, H as previewLine, I as ageString, L as compactPath, O as cleanupPersistedSession, R as fmtTokens, U as shortId, V as formatTaskSummary, j as resolveTasksDir, p as createAgent, z as formatDuration } from "./tools-D_icxa-V.js";
2
2
  import { s as errorMessage } from "./errors-CDwtPIMX.js";
3
3
  import { s as McpOAuthProvider, t as connectMcpServers } from "./mcp-CNUbvbsy.js";
4
- import { C as summaryToTurn, a as selectFilesFromSession, r as buildPostCompactAttachments, s as compactConversation, t as loginMcpServer } from "./login-DxyAERe1.js";
4
+ import { C as summaryToTurn, a as selectFilesFromSession, r as buildPostCompactAttachments, s as compactConversation, t as loginMcpServer } from "./login-iTy-0wYz.js";
5
5
  import { n as formatTokenUsage } from "./stats-DgOvY7wd.js";
6
- import { n as loadSession, t as createSession } from "./session-kwsNnOmt.js";
6
+ import { n as loadSession, t as createSession } from "./session-CbkiJDlH.js";
7
7
  import { createTuiStore } from "./session/sqlite.js";
8
- import { $ as useMcpAuthDispatch, $t as isTurnHighlighted, A as getSafelist, An as rewriteMultiEditHeader, At as clampFps, B as supportsOAuth, Cn as summarizeEditPayload, Cr as modelSupportsReasoning, Ct as shortId, D as useSafeModeQueue, Dn as mergeApprovalAndBodyOutcomes, Dr as piIdOf, Dt as SETTINGS_CHOICES, E as useSafeModeActions, Et as DEFAULT_SETTINGS, F as suggestSafelistEntry, Ft as resolveTheme, Gn as createSkillsCompletionProvider, Gt as ConfigProvider, H as filterModelCatalog, Ht as DiscoveryProvider, Jn as createFilesCompletionProvider, Jt as EDIT_TOOL_NAMES, K as discoverProjectMcps, Kn as uniqueSkillNamesFromReferences, Kt as useConfig, L as splitPromptSegments, Ln as ensureKeybindingsFile, Mn as summarizeOutcomes, Nn as findGitRoot, Nr as accentColor, On as parseEditOutcomesFromResult, Ot as SETTINGS_TOGGLES, Pt as resolveChipColor, Q as McpAuthProvider, Qt as isEditErrorResult, R as formatPathForCwd, St as fmtTokens, T as SafeModeProvider, Tn as buildEditOutcomesAnnotation, Tt as useEnabledToggleSet, U as indexOfEntry, Ut as useDiscovery, V as buildModelCatalog, Vt as createDiscoverySlot, W as buildMcpServers, Wt as useDiscoveryOptional, Xt as deriveSessionTitle, Y as createFileMcpCredentialStore, Yr as useActiveTodos, Zt as eventsFromTurns, _ as turnContextSize, _t as truncateTrailing, a as computeTurnAnchors, ar as bootTick, at as InteractionsProvider, b as defaultSkillScanPaths, bn as filetypeFromPath, bt as ageString, c as formatToolCall, cn as sumRunCosts, ct as createInteractionTools, d as useSelectStyle, dn as toolResultText, dt as pendingInteractionsFromTurns, en as isVisible, er as useCompletion, et as useMcpAuthState, f as useSurfaces, fn as turnSelectionOwnership, g as finalizeStreamingMarkdownForOwner, gn as buildUnifiedDiff, gt as hintsLength, h as finalizeStreamingMarkdown, hn as buildContextualDiff, ht as clipHintsToWidth, i as turnAsText, in as marginTopFor, j as isOnSafelist, jn as stripEditOutcomesAnnotation, jt as useSettings, k as addToSafelist, kn as resolveApprovalForPayload, kt as SettingsProvider, l as ThemeProvider, m as useTheme, mt as useInteractionsQueue, n as deleteTurnSafely, nn as listSessionMeta, nr as buildLinearRamp, o as TOOL_DISPLAY, oi as buildBuildSystem, on as selectableTurnIds, or as shouldAutoCompact, pn as updateToolEventOutcomes, pr as setProviderCredential, pt as useInteractionsActions, qt as resolveConfig, r as truncateTurnsAt, rr as tryOpenBrowser, rt as splitMarkdownCodeBlocks, s as displayNameFor, si as buildPlanSystem, sn as stripSpawnTokensLine, sr as detectAuth, st as buildResumedToolResultsTurn, tn as lastContextSizeFromTurns, tr as blendHsl, tt as getMcpAuthStatus, u as useColors, un as toolCallPreview, ut as makeRequestInteraction, v as useStreamBuffer, w as writeSessionExport, wt as listProjectFiles, x as discoverProjectSkills, xn as previewEditPayload, xr as getContextWindow, xt as compactPath, y as buildSkillsConfig, yn as extractEditPayload, yt as generateSessionTitle, z as runOAuthLogin, zn as matchesBinding, zr as TODO_STATUS_GLYPHS } from "./turn-operations-OzKEOXul.js";
8
+ import { $ as useMcpAuthDispatch, $n as tryOpenBrowser, A as getSafelist, At as resolveChipColor, B as supportsOAuth, Bt as useDiscoveryOptional, Cn as mergeApprovalAndBodyOutcomes, Cr as piIdOf, Ct as SETTINGS_CHOICES, D as useSafeModeQueue, Dn as stripEditOutcomesAnnotation, Dt as useSettings, E as useSafeModeActions, En as rewriteMultiEditHeader, Et as clampFps, F as suggestSafelistEntry, Fn as matchesBinding, Fr as TODO_STATUS_GLYPHS, Gr as useActiveTodos, H as filterModelCatalog, Hn as uniqueSkillNamesFromReferences, Ht as useConfig, Jt as isEditErrorResult, K as discoverProjectMcps, Kt as deriveSessionTitle, L as splitPromptSegments, Lt as createDiscoverySlot, Nn as ensureKeybindingsFile, On as summarizeOutcomes, Q as McpAuthProvider, Qn as buildLinearRamp, Qt as listSessionMeta, R as formatPathForCwd, Rt as DiscoveryProvider, St as DEFAULT_SETTINGS, T as SafeModeProvider, Tn as resolveApprovalForPayload, Tt as SettingsProvider, U as indexOfEntry, Ut as resolveConfig, V as buildModelCatalog, Vn as createSkillsCompletionProvider, Vt as ConfigProvider, W as buildMcpServers, Wn as createFilesCompletionProvider, Wt as EDIT_TOOL_NAMES, Xn as useCompletion, Xt as isVisible, Y as createFileMcpCredentialStore, Yt as isTurnHighlighted, Zn as blendHsl, Zt as lastContextSizeFromTurns, _ as turnContextSize, _n as previewEditPayload, _r as getContextWindow, _t as truncateTrailing, a as computeTurnAnchors, at as InteractionsProvider, b as defaultSkillScanPaths, bt as listProjectFiles, c as formatToolCall, cn as turnSelectionOwnership, ct as createInteractionTools, d as useSelectStyle, dn as buildContextualDiff, dt as pendingInteractionsFromTurns, en as marginTopFor, et as useMcpAuthState, f as useSurfaces, fn as buildUnifiedDiff, g as finalizeStreamingMarkdownForOwner, gn as filetypeFromPath, gt as hintsLength, h as finalizeStreamingMarkdown, hn as extractEditPayload, ht as clipHintsToWidth, i as turnAsText, in as sumRunCosts, j as isOnSafelist, jt as resolveTheme, k as addToSafelist, kn as findGitRoot, kr as accentColor, l as ThemeProvider, ln as updateToolEventOutcomes, lr as setProviderCredential, m as useTheme, mt as useInteractionsQueue, n as deleteTurnSafely, ni as buildBuildSystem, nn as selectableTurnIds, nr as shouldAutoCompact, o as TOOL_DISPLAY, on as toolCallPreview, pt as useInteractionsActions, qt as eventsFromTurns, r as truncateTurnsAt, ri as buildPlanSystem, rn as stripSpawnTokensLine, rr as detectAuth, rt as splitMarkdownCodeBlocks, s as displayNameFor, sn as toolResultText, st as buildResumedToolResultsTurn, tr as bootTick, tt as getMcpAuthStatus, u as useColors, ut as makeRequestInteraction, v as useStreamBuffer, w as writeSessionExport, wn as parseEditOutcomesFromResult, wt as SETTINGS_TOGGLES, x as discoverProjectSkills, xn as buildEditOutcomesAnnotation, xt as useEnabledToggleSet, y as buildSkillsConfig, yn as summarizeEditPayload, yr as modelSupportsReasoning, yt as generateSessionTitle, z as runOAuthLogin, zt as useDiscovery } from "./turn-operations-CtgBlBHn.js";
9
+ import { homedir } from "node:os";
9
10
  import { spawn } from "node:child_process";
10
- import { Buffer } from "node:buffer";
11
11
  import * as fs from "node:fs";
12
- import { homedir } from "node:os";
12
+ import { Buffer } from "node:buffer";
13
13
  import { createContext, createElement, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
14
14
  import { BoxRenderable, CodeRenderable, RGBA, SyntaxStyle, TextRenderable, addDefaultParsers, createCliRenderer, decodePasteBytes, defaultTextareaKeyBindings, getTreeSitterClient, stripAnsiSequences } from "@opentui/core";
15
15
  import { createRoot, useKeyboard, useRenderer, useSelectionHandler, useTerminalDimensions } from "@opentui/react";
@@ -250,7 +250,10 @@ function CancelToolModal({ inFlight, onCancel, onCancelAll, onClose }) {
250
250
  }
251
251
  if (key.name === "return") {
252
252
  const row = rows[safeIndex];
253
- if (row) onCancel(row.callId, "user-clicked-cancel");
253
+ if (row) {
254
+ const result = onCancel(row, "user-clicked-cancel");
255
+ if (result instanceof Promise) result.catch(() => {});
256
+ }
254
257
  onClose();
255
258
  return;
256
259
  }
@@ -322,6 +325,7 @@ function CancelToolRow({ row, isFocused, highlightBg, elapsedColWidth, callIdCol
322
325
  const elapsed = formatElapsed(Date.now() - row.startedAt).padStart(elapsedColWidth, " ");
323
326
  const idLabel = truncate(row.callId, callIdColWidth).padEnd(callIdColWidth, " ");
324
327
  const childLabel = (row.childId ? `· ${row.childId}` : "").padEnd(childColWidth, " ");
328
+ const kindGlyph = row.kind === "task" ? "⌁" : "·";
325
329
  return /* @__PURE__ */ jsx("box", {
326
330
  style: {
327
331
  height: 1,
@@ -341,6 +345,14 @@ function CancelToolRow({ row, isFocused, highlightBg, elapsedColWidth, callIdCol
341
345
  fg: COLOR.mute,
342
346
  children: " "
343
347
  }),
348
+ /* @__PURE__ */ jsx("span", {
349
+ fg: isFocused ? COLOR.warn : COLOR.mute,
350
+ children: kindGlyph
351
+ }),
352
+ /* @__PURE__ */ jsx("span", {
353
+ fg: COLOR.mute,
354
+ children: " "
355
+ }),
344
356
  /* @__PURE__ */ jsx("span", {
345
357
  fg: isFocused ? COLOR.brand : COLOR.dim,
346
358
  children: row.tool
@@ -571,8 +583,15 @@ function CrushThrobber({ label, size = 15, from, to, labelColor }) {
571
583
  * embedded Tree-sitter language tokens (`keyword`, `string`, `function`,
572
584
  * …) — OpenTUI's `<markdown>` re-uses the same `SyntaxStyle` for the
573
585
  * fenced-code renderable, so one table drives both surfaces.
586
+ *
587
+ * `overrides`, when set, replaces individual entries by token name —
588
+ * each override is merged INTO the resolved entry (shallow). Used by
589
+ * the "on-selection" variant to swap `markup.raw.bg` for the row's
590
+ * selection surface so inline code chips blend into the highlight
591
+ * rather than reading as "punched out" rectangles. Top-level keys
592
+ * absent from the base theme become brand-new entries.
574
593
  */
575
- function buildMdStyle(theme) {
594
+ function buildMdStyle(theme, overrides) {
576
595
  const styles = {};
577
596
  for (const [token, style] of Object.entries(theme.syntax)) {
578
597
  const out = {};
@@ -584,13 +603,26 @@ function buildMdStyle(theme) {
584
603
  if (style.dim) out.dim = true;
585
604
  styles[token] = out;
586
605
  }
606
+ if (overrides) for (const [token, patch] of Object.entries(overrides)) styles[token] = {
607
+ ...styles[token] ?? {},
608
+ ...patch
609
+ };
587
610
  return SyntaxStyle.fromStyles(styles);
588
611
  }
589
612
  const MdStyleContext = createContext(null);
590
613
  function MdStyleProvider({ children }) {
591
614
  const theme = useTheme();
592
- const style = useMemo(() => buildMdStyle(theme), [theme]);
593
- return createElement(MdStyleContext.Provider, { value: style }, children);
615
+ const bundle = useMemo(() => {
616
+ const selectionBg = RGBA.fromHex(theme.surfaces.selection);
617
+ return {
618
+ regular: buildMdStyle(theme),
619
+ selected: buildMdStyle(theme, {
620
+ "markup.raw": { bg: selectionBg },
621
+ "markup.raw.block": { bg: selectionBg }
622
+ })
623
+ };
624
+ }, [theme]);
625
+ return createElement(MdStyleContext.Provider, { value: bundle }, children);
594
626
  }
595
627
  /**
596
628
  * Active markdown / syntax-highlighting style. Returns a single shared
@@ -598,13 +630,18 @@ function MdStyleProvider({ children }) {
598
630
  * mount, re-built on theme switch. A `Settings.theme` flip re-paints every
599
631
  * `<markdown>` that reads this hook.
600
632
  *
633
+ * Pass `{ selected: true }` to get the on-selection variant where inline
634
+ * code chips' background matches the row's selection surface (so the
635
+ * chips blend into the highlight rather than reading as punched-out
636
+ * rectangles).
637
+ *
601
638
  * Throws if used outside `<MdStyleProvider>` so a missing wiring shows up
602
639
  * loudly in development rather than silently rendering plain text.
603
640
  */
604
- function useMdStyle() {
605
- const style = useContext(MdStyleContext);
606
- if (!style) throw new Error("useMdStyle must be used inside <MdStyleProvider>");
607
- return style;
641
+ function useMdStyle(opts = {}) {
642
+ const bundle = useContext(MdStyleContext);
643
+ if (!bundle) throw new Error("useMdStyle must be used inside <MdStyleProvider>");
644
+ return opts.selected ? bundle.selected : bundle.regular;
608
645
  }
609
646
  const CHIP_TOKEN_PREFIX = "completion.reference";
610
647
  /** Per-kind token name in the chip `SyntaxStyle` — e.g. `completion.reference.skills`. */
@@ -718,7 +755,7 @@ function useChipHighlights(textareaRef, references, chipStyle) {
718
755
  * same-turn events read as one continuous highlighted block instead of a
719
756
  * striped list.
720
757
  */
721
- const EventLine = memo(({ event, previous, depthOffset = 0, selected = false, anchorId }) => {
758
+ const EventLine = memo(({ event, previous, depthOffset = 0, selected = false, anchorId, hideChildLabel = false }) => {
722
759
  const SURFACE = useSurfaces();
723
760
  const gap = marginTopFor(event, previous);
724
761
  return /* @__PURE__ */ jsx("box", {
@@ -733,7 +770,9 @@ const EventLine = memo(({ event, previous, depthOffset = 0, selected = false, an
733
770
  },
734
771
  children: /* @__PURE__ */ jsx(EventLineImpl, {
735
772
  event,
736
- depthOffset
773
+ depthOffset,
774
+ hideChildLabel,
775
+ selected
737
776
  })
738
777
  });
739
778
  });
@@ -1229,11 +1268,12 @@ function SubagentBlock({ events, previous, selectedTurnId = null, anchorIds }) {
1229
1268
  }, [events]);
1230
1269
  const title = childIds.length === 0 ? " subagent " : childIds.length === 1 ? ` ${childIds[0]} ` : ` subagents · ${childIds.join(", ")} `;
1231
1270
  const marginTop = previous ? 1 : 0;
1271
+ const hideChildLabel = childIds.length === 1;
1232
1272
  return /* @__PURE__ */ jsx("box", {
1233
1273
  title,
1234
1274
  style: {
1235
1275
  border: true,
1236
- borderColor: COLOR.mute,
1276
+ borderColor: COLOR.brand,
1237
1277
  paddingLeft: 1,
1238
1278
  paddingRight: 1,
1239
1279
  paddingTop: 0,
@@ -1248,7 +1288,8 @@ function SubagentBlock({ events, previous, selectedTurnId = null, anchorIds }) {
1248
1288
  previous: events[i - 1],
1249
1289
  depthOffset: 1,
1250
1290
  selected: selectedTurnId !== null && evt.turnId === selectedTurnId,
1251
- anchorId: anchorIds?.[i]
1291
+ anchorId: anchorIds?.[i],
1292
+ hideChildLabel
1252
1293
  }, i))
1253
1294
  });
1254
1295
  }
@@ -1290,7 +1331,7 @@ function rowStyle(paddingLeft) {
1290
1331
  alignSelf: "stretch"
1291
1332
  };
1292
1333
  }
1293
- function EventLineImpl({ event, depthOffset = 0 }) {
1334
+ function EventLineImpl({ event, depthOffset = 0, hideChildLabel = false, selected = false }) {
1294
1335
  const COLOR = useColors();
1295
1336
  const { settings } = useSettings();
1296
1337
  const safeText = event.text === "" ? " " : event.text;
@@ -1353,7 +1394,8 @@ function EventLineImpl({ event, depthOffset = 0 }) {
1353
1394
  style: row,
1354
1395
  children: /* @__PURE__ */ jsx(MarkdownBlock, {
1355
1396
  text: event.text,
1356
- dim: child
1397
+ dim: child,
1398
+ selected
1357
1399
  })
1358
1400
  });
1359
1401
  case "spawn-start": return /* @__PURE__ */ jsx("box", {
@@ -1365,10 +1407,13 @@ function EventLineImpl({ event, depthOffset = 0 }) {
1365
1407
  fg: COLOR.accent,
1366
1408
  children: "🌱 "
1367
1409
  }),
1368
- /* @__PURE__ */ jsx("span", {
1369
- fg: COLOR.dim,
1370
- children: `[${event.childId ?? "child"}] `
1371
- }),
1410
+ !hideChildLabel && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
1411
+ fg: COLOR.brand,
1412
+ children: event.childId ?? "child"
1413
+ }), /* @__PURE__ */ jsx("span", {
1414
+ fg: COLOR.mute,
1415
+ children: " · "
1416
+ })] }),
1372
1417
  /* @__PURE__ */ jsx("span", {
1373
1418
  fg: COLOR.dim,
1374
1419
  children: safeText
@@ -1385,10 +1430,13 @@ function EventLineImpl({ event, depthOffset = 0 }) {
1385
1430
  fg: COLOR.accent,
1386
1431
  children: "🌳 "
1387
1432
  }),
1388
- /* @__PURE__ */ jsx("span", {
1389
- fg: COLOR.dim,
1390
- children: `[${event.childId ?? "child"}] `
1391
- }),
1433
+ !hideChildLabel && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
1434
+ fg: COLOR.brand,
1435
+ children: event.childId ?? "child"
1436
+ }), /* @__PURE__ */ jsx("span", {
1437
+ fg: COLOR.mute,
1438
+ children: " · "
1439
+ })] }),
1392
1440
  /* @__PURE__ */ jsx("span", {
1393
1441
  fg: COLOR.mute,
1394
1442
  children: safeText
@@ -1400,10 +1448,80 @@ function EventLineImpl({ event, depthOffset = 0 }) {
1400
1448
  event,
1401
1449
  indent: row.paddingLeft
1402
1450
  });
1451
+ case "task-notification": return /* @__PURE__ */ jsx(TaskNotificationBlock, {
1452
+ event,
1453
+ indent: row.paddingLeft
1454
+ });
1403
1455
  default: return /* @__PURE__ */ jsx("text", { children: safeText });
1404
1456
  }
1405
1457
  }
1406
1458
  /**
1459
+ * One-line completion banner for a background task that exited or was
1460
+ * killed. Visual contract:
1461
+ *
1462
+ * - Glyph color reflects exit cleanliness:
1463
+ * `'exited'` exit code 0 → success (subtle accent — quiet).
1464
+ * `'exited'` non-zero → warn (model / user should notice).
1465
+ * `'killed'` → warn (we issued SIGTERM).
1466
+ * - Task id reads in the agent's brand accent so the banner stands
1467
+ * apart from surrounding markdown without screaming.
1468
+ * - Status label takes the same accent as the glyph — color-coupling
1469
+ * reinforces the "this is what happened" mental model at a glance.
1470
+ * - Output path is `compactPath()`-formatted (`~/.zidane/...` when
1471
+ * under `$HOME`) so the column doesn't blow past terminal width
1472
+ * just to show the user's home prefix they already know about.
1473
+ *
1474
+ * Vertical spacing comes from `marginTopFor` — banners get a `tool`-like
1475
+ * gap before (and the next non-task event provides the gap after), but
1476
+ * consecutive banners stack tightly so a burst of completions doesn't
1477
+ * scatter the transcript.
1478
+ */
1479
+ function TaskNotificationBlock({ event, indent }) {
1480
+ const COLOR = useColors();
1481
+ const task = event.task;
1482
+ const statusAccent = (task ? task.status === "killed" || task.exitCode !== 0 : false) ? COLOR.warn : COLOR.brand;
1483
+ const statusLabel = task ? formatTaskStatus(task) : "done";
1484
+ const displayPath = task?.outputPath ? compactPath(task.outputPath) : "";
1485
+ return /* @__PURE__ */ jsx("box", {
1486
+ style: { paddingLeft: indent },
1487
+ children: /* @__PURE__ */ jsxs("text", {
1488
+ wrapMode: "none",
1489
+ children: [
1490
+ /* @__PURE__ */ jsx("span", {
1491
+ fg: statusAccent,
1492
+ children: "⌁ "
1493
+ }),
1494
+ /* @__PURE__ */ jsx("span", {
1495
+ fg: COLOR.brand,
1496
+ children: task?.taskId ?? "?"
1497
+ }),
1498
+ /* @__PURE__ */ jsx("span", {
1499
+ fg: COLOR.mute,
1500
+ children: " · "
1501
+ }),
1502
+ /* @__PURE__ */ jsx("span", {
1503
+ fg: statusAccent,
1504
+ children: statusLabel
1505
+ }),
1506
+ task && task.durationMs > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
1507
+ fg: COLOR.mute,
1508
+ children: " · "
1509
+ }), /* @__PURE__ */ jsx("span", {
1510
+ fg: COLOR.dim,
1511
+ children: formatDuration(task.durationMs)
1512
+ })] }),
1513
+ displayPath && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
1514
+ fg: COLOR.mute,
1515
+ children: " · "
1516
+ }), /* @__PURE__ */ jsx("span", {
1517
+ fg: COLOR.dim,
1518
+ children: displayPath
1519
+ })] })
1520
+ ]
1521
+ })
1522
+ });
1523
+ }
1524
+ /**
1407
1525
  * Boundary card for `compact-summary` events. Two visual rows:
1408
1526
  *
1409
1527
  * 1. Meta line — emoji + replaced-turn count + model + token usage,
@@ -1821,10 +1939,10 @@ function parseBlockIndex(id) {
1821
1939
  const parsed = Number.parseInt(match[1] ?? "", 10);
1822
1940
  return Number.isFinite(parsed) ? parsed : 0;
1823
1941
  }
1824
- const MarkdownBlock = memo(({ text, dim }) => {
1942
+ const MarkdownBlock = memo(({ text, dim, selected = false }) => {
1825
1943
  const COLOR = useColors();
1826
1944
  const SURFACE = useSurfaces();
1827
- const mdStyle = useMdStyle();
1945
+ const mdStyle = useMdStyle({ selected });
1828
1946
  const renderer = useRenderer();
1829
1947
  const content = text.replace(/^\n+|\n+$/g, "");
1830
1948
  const bag = useRef({
@@ -1846,7 +1964,7 @@ const MarkdownBlock = memo(({ text, dim }) => {
1846
1964
  streaming: true,
1847
1965
  internalBlockMode: "coalesced",
1848
1966
  fg: dim ? COLOR.dim : void 0,
1849
- bg: SURFACE.background,
1967
+ bg: selected ? SURFACE.selection : SURFACE.background,
1850
1968
  renderNode
1851
1969
  });
1852
1970
  });
@@ -8699,6 +8817,26 @@ function AppShell() {
8699
8817
  const inFlightToolsRef = useRef([]);
8700
8818
  inFlightToolsRef.current = inFlightTools;
8701
8819
  /**
8820
+ * Live registry of running background tasks — populated by
8821
+ * `background:start` (and `child:background:start`), drained by
8822
+ * `background:exit` (and the child sibling). Same shape as
8823
+ * {@link InFlightToolCall} so it merges cleanly into the cancel-tool
8824
+ * picker's snapshot; the `kind: 'task'` discriminator routes the
8825
+ * cancel callback to `agent.killBackgroundTask(taskId)` instead of
8826
+ * `agent.cancelTool(callId)`.
8827
+ *
8828
+ * Tracked separately from `inFlightTools` because the two have
8829
+ * different lifetimes: a tool call is in-flight only while
8830
+ * `tool:before` and `tool:after` straddle, but a background task
8831
+ * survives PAST its spawning tool call — the `shell` body returns
8832
+ * the handle and `tool:after` fires while the task keeps running.
8833
+ * Without this separate registry, `ctrl+k` would never see a task
8834
+ * because the tool entry has already drained.
8835
+ */
8836
+ const [backgroundTasks, setBackgroundTasks] = useState([]);
8837
+ const backgroundTasksRef = useRef([]);
8838
+ backgroundTasksRef.current = backgroundTasks;
8839
+ /**
8702
8840
  * Names of currently-active skills, tracked via `skills:activate` /
8703
8841
  * `skills:deactivate` hooks. Drives the footer's "✦ N skill(s)"
8704
8842
  * chip — the user's only passive surface for noticing that a skill
@@ -8816,6 +8954,10 @@ function AppShell() {
8816
8954
  persistThreshold: 0,
8817
8955
  persistDir
8818
8956
  } : { persistDir };
8957
+ const tasksDir = resolveTasksDir({
8958
+ userDir: dataDir,
8959
+ sessionId: session.id
8960
+ });
8819
8961
  const agent = createAgent({
8820
8962
  ...profile.preset,
8821
8963
  ...builtInSystem ? { system: builtInSystem } : {},
@@ -8830,7 +8972,8 @@ function AppShell() {
8830
8972
  },
8831
8973
  behavior: {
8832
8974
  ...profile.preset.behavior ?? {},
8833
- ...persistBehavior
8975
+ ...persistBehavior,
8976
+ tasksDir
8834
8977
  },
8835
8978
  provider: descriptor.factory(),
8836
8979
  session,
@@ -9022,6 +9165,44 @@ function AppShell() {
9022
9165
  agent.hooks.hook("tool:cancelled", ({ callId }) => {
9023
9166
  unregisterInFlightTool(callId);
9024
9167
  });
9168
+ const registerBackgroundTask = (ctx) => {
9169
+ setBackgroundTasks((prev) => [...prev, {
9170
+ kind: "task",
9171
+ callId: ctx.taskId,
9172
+ tool: `shell (background): ${previewLine(ctx.command, 60)}`,
9173
+ startedAt: ctx.startedAt,
9174
+ ...ctx.childId ? { childId: ctx.childId } : {}
9175
+ }]);
9176
+ };
9177
+ const dropBackgroundTaskAndEmitBanner = (ctx) => {
9178
+ setBackgroundTasks((prev) => prev.filter((t) => t.callId !== ctx.taskId));
9179
+ streamRef.current?.appendImmediate({
9180
+ kind: "task-notification",
9181
+ text: formatTaskSummary(ctx),
9182
+ task: {
9183
+ taskId: ctx.taskId,
9184
+ status: ctx.status,
9185
+ exitCode: ctx.exitCode,
9186
+ outputPath: ctx.outputPath,
9187
+ command: ctx.command,
9188
+ durationMs: ctx.durationMs
9189
+ },
9190
+ ...ctx.childId ? { childId: ctx.childId } : {},
9191
+ ...typeof ctx.depth === "number" ? { depth: ctx.depth } : {}
9192
+ });
9193
+ };
9194
+ agent.hooks.hook("background:start", (ctx) => registerBackgroundTask(ctx));
9195
+ agent.hooks.hook("background:exit", (ctx) => dropBackgroundTaskAndEmitBanner(ctx));
9196
+ agent.hooks.hook("background:reassign", (ctx) => {
9197
+ setBackgroundTasks((prev) => prev.map((entry) => entry.callId === ctx.taskId ? {
9198
+ kind: "task",
9199
+ callId: entry.callId,
9200
+ tool: entry.tool,
9201
+ startedAt: entry.startedAt
9202
+ } : entry));
9203
+ });
9204
+ agent.hooks.hook("child:background:start", (ctx) => registerBackgroundTask(ctx));
9205
+ agent.hooks.hook("child:background:exit", (ctx) => dropBackgroundTaskAndEmitBanner(ctx));
9025
9206
  agent.hooks.hook("skills:activate", ({ skill }) => {
9026
9207
  setActiveSkillNames((prev) => {
9027
9208
  if (prev.has(skill.name)) return prev;
@@ -9063,7 +9244,7 @@ function AppShell() {
9063
9244
  stream.flushAndUpdate(finalizeStreamingMarkdown);
9064
9245
  });
9065
9246
  agent.hooks.hook("spawn:before", ({ id, task, depth }) => {
9066
- const taskPreview = task.length > 80 ? `${task.slice(0, 80)}…` : task;
9247
+ const taskPreview = previewLine(task, 80);
9067
9248
  stream.appendImmediate({
9068
9249
  kind: "spawn-start",
9069
9250
  text: taskPreview,
@@ -9201,6 +9382,7 @@ function AppShell() {
9201
9382
  sessionSafelistRef.current.clear();
9202
9383
  pendingAnnotationsRef.current.clear();
9203
9384
  setInFlightTools([]);
9385
+ setBackgroundTasks([]);
9204
9386
  setActiveSkillNames(/* @__PURE__ */ new Set());
9205
9387
  }, [
9206
9388
  stream,
@@ -9929,6 +10111,10 @@ function AppShell() {
9929
10111
  userDir: dataDir,
9930
10112
  sessionId: id
9931
10113
  }));
10114
+ cleanupPersistedSession(resolveTasksDir({
10115
+ userDir: dataDir,
10116
+ sessionId: id
10117
+ }));
9932
10118
  const wasCurrent = id === currentSession?.id;
9933
10119
  if (wasCurrent) {
9934
10120
  await teardown();
@@ -10320,16 +10506,24 @@ function AppShell() {
10320
10506
  onCycleAgent();
10321
10507
  return;
10322
10508
  }
10323
- if (matchesBinding(key, keybindings.cancelToolCall) && screen === "chat" && busy && !pendingApproval) {
10324
- const snapshot = inFlightToolsRef.current;
10509
+ if (matchesBinding(key, keybindings.cancelToolCall) && screen === "chat" && !pendingApproval) {
10510
+ const tools = inFlightToolsRef.current.filter((entry) => entry.childId === void 0);
10511
+ const tasks = backgroundTasksRef.current.filter((entry) => entry.childId === void 0);
10512
+ const snapshot = [...tools, ...tasks];
10325
10513
  if (snapshot.length === 0) return;
10326
10514
  modal.open(/* @__PURE__ */ jsx(CancelToolModal, {
10327
10515
  inFlight: snapshot,
10328
- onCancel: (callId, reason) => agentRef.current?.cancelTool(callId, reason) ?? false,
10516
+ onCancel: (entry, reason) => {
10517
+ const agent = agentRef.current;
10518
+ if (!agent) return false;
10519
+ if (entry.kind === "task") return agent.killBackgroundTask(entry.callId);
10520
+ return agent.cancelTool(entry.callId, reason);
10521
+ },
10329
10522
  onCancelAll: () => {
10330
10523
  const agent = agentRef.current;
10331
10524
  if (!agent) return;
10332
- for (const entry of inFlightToolsRef.current) agent.cancelTool(entry.callId, "user-cancelled-all");
10525
+ for (const entry of snapshot) if (entry.kind === "task") agent.killBackgroundTask(entry.callId);
10526
+ else agent.cancelTool(entry.callId, "user-cancelled-all");
10333
10527
  },
10334
10528
  onClose: () => modal.close()
10335
10529
  }));
@@ -10367,7 +10561,7 @@ function AppShell() {
10367
10561
  agentLabel: pickedAgent.label,
10368
10562
  agentColor: accentColor(pickedAgent.accent, COLOR),
10369
10563
  keybindings,
10370
- inFlightToolCount: inFlightTools.length,
10564
+ inFlightToolCount: inFlightTools.reduce((n, entry) => entry.childId === void 0 ? n + 1 : n, 0) + backgroundTasks.reduce((n, entry) => entry.childId === void 0 ? n + 1 : n, 0),
10371
10565
  activeSkillCount: activeSkillNames.size,
10372
10566
  skillsChipColor: COLOR.brand
10373
10567
  }), [
@@ -10384,6 +10578,7 @@ function AppShell() {
10384
10578
  modelHasReasoning,
10385
10579
  keybindings,
10386
10580
  inFlightTools,
10581
+ backgroundTasks,
10387
10582
  activeSkillNames
10388
10583
  ]);
10389
10584
  const queuedMessagePreviews = useMemo(() => messageQueue.map((m) => ({
@@ -10550,7 +10745,7 @@ function buildHints({ screen, busy, pending, pendingInteractionLive, pendingInte
10550
10745
  const baseBusyHints = [];
10551
10746
  if (inFlightToolCount > 0) baseBusyHints.push({
10552
10747
  key: keybindings.cancelToolCall,
10553
- label: inFlightToolCount === 1 ? "cancel tool" : `cancel tool (${inFlightToolCount})`
10748
+ label: inFlightToolCount === 1 ? "cancel" : `cancel (${inFlightToolCount})`
10554
10749
  });
10555
10750
  baseBusyHints.push({
10556
10751
  key: "esc",
@@ -10611,6 +10806,10 @@ function buildHints({ screen, busy, pending, pendingInteractionLive, pendingInte
10611
10806
  label: activeSkillCount === 1 ? "1 skill" : `${activeSkillCount} skills`,
10612
10807
  labelColor: skillsChipColor
10613
10808
  } : null;
10809
+ const cancelTaskChip = inFlightToolCount > 0 ? {
10810
+ key: keybindings.cancelToolCall,
10811
+ label: inFlightToolCount === 1 ? "cancel task" : `cancel task (${inFlightToolCount})`
10812
+ } : null;
10614
10813
  return [
10615
10814
  ...hasMultipleAgents ? [{
10616
10815
  key: keybindings.cycleAgent,
@@ -10619,6 +10818,7 @@ function buildHints({ screen, busy, pending, pendingInteractionLive, pendingInte
10619
10818
  }] : [],
10620
10819
  ...modelHint ? [modelHint] : [],
10621
10820
  ...skillsChip ? [skillsChip] : [],
10821
+ ...cancelTaskChip ? [cancelTaskChip] : [],
10622
10822
  ...currentSession ? [{
10623
10823
  key: keybindings.openSessionDetails,
10624
10824
  label: "session"