negotium 0.22.8 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -6559,7 +6559,7 @@ var exports_version = {};
6559
6559
  __export(exports_version, {
6560
6560
  NEGOTIUM_VERSION: () => NEGOTIUM_VERSION
6561
6561
  });
6562
- var NEGOTIUM_VERSION = "0.22.8";
6562
+ var NEGOTIUM_VERSION = "0.23.0";
6563
6563
 
6564
6564
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
6565
6565
  import { spawn as spawn2 } from "child_process";
@@ -41929,7 +41929,10 @@ function createInitialState(userId) {
41929
41929
  decisionGraphOffset: { x: 0, y: 0 },
41930
41930
  decisionGraphSpacing: DEFAULT_DECISION_GRAPH_SPACING,
41931
41931
  askChoiceIndex: 0,
41932
- taskSidebarEnabled: true,
41932
+ taskPanelScrollOffset: 0,
41933
+ topicsPanelEnabled: true,
41934
+ topicsPanelScrollOffset: 0,
41935
+ topicsPanelSort: "activity",
41933
41936
  overlay: null,
41934
41937
  topicPickerRoot: false,
41935
41938
  vaultEntries: [],
@@ -41941,8 +41944,27 @@ function createInitialState(userId) {
41941
41944
  vaultRequestSubmitting: false
41942
41945
  };
41943
41946
  }
41944
- function toggleTaskSidebar(state) {
41945
- return { ...state, taskSidebarEnabled: !state.taskSidebarEnabled };
41947
+ function toggleTopicsPanel(state) {
41948
+ return { ...state, topicsPanelEnabled: !state.topicsPanelEnabled };
41949
+ }
41950
+ function cycleTopicsPanelSort(state) {
41951
+ return { ...state, topicsPanelSort: state.topicsPanelSort === "activity" ? "name" : "activity" };
41952
+ }
41953
+ function scrollTopicsPanel(state, delta, maxOffset) {
41954
+ const next = Math.min(maxOffset, Math.max(0, state.topicsPanelScrollOffset + delta));
41955
+ return next === state.topicsPanelScrollOffset ? state : { ...state, topicsPanelScrollOffset: next };
41956
+ }
41957
+ function scrollTaskPanel(state, delta, maxOffset) {
41958
+ const next = Math.min(maxOffset, Math.max(0, state.taskPanelScrollOffset + delta));
41959
+ return next === state.taskPanelScrollOffset ? state : { ...state, taskPanelScrollOffset: next };
41960
+ }
41961
+ function setTopicsPanelScrollOffset(state, offset, maxOffset) {
41962
+ const next = Math.min(maxOffset, Math.max(0, offset));
41963
+ return next === state.topicsPanelScrollOffset ? state : { ...state, topicsPanelScrollOffset: next };
41964
+ }
41965
+ function setTaskPanelScrollOffset(state, offset, maxOffset) {
41966
+ const next = Math.min(maxOffset, Math.max(0, offset));
41967
+ return next === state.taskPanelScrollOffset ? state : { ...state, taskPanelScrollOffset: next };
41946
41968
  }
41947
41969
  function activeTopic(state) {
41948
41970
  return state.topics.find((topic) => topic.id === state.activeTopicId) ?? null;
@@ -41959,6 +41981,16 @@ function setTopicUsage(state, usage) {
41959
41981
  function activeQuestion(state) {
41960
41982
  return activeMessages(state).slice().reverse().find((message2) => message2.kind === "ask_user_question" && message2.askUserQuestion && !message2.askUserQuestion.expired && !message2.askUserQuestion.selectedLabel) ?? null;
41961
41983
  }
41984
+ function blockedQuestionText(state, topicId) {
41985
+ const messages = state.messages[topicId] ?? [];
41986
+ for (let index = messages.length - 1;index >= 0; index -= 1) {
41987
+ const message2 = messages[index];
41988
+ if (message2.kind === "ask_user_question" && message2.askUserQuestion && !message2.askUserQuestion.expired && !message2.askUserQuestion.selectedLabel) {
41989
+ return message2.askUserQuestion.question;
41990
+ }
41991
+ }
41992
+ return;
41993
+ }
41962
41994
  function activeVaultRequest(state) {
41963
41995
  return activeMessages(state).find((message2) => message2.kind === "vault_secret_request" && message2.vaultSecretRequest?.status === "pending") ?? null;
41964
41996
  }
@@ -42062,12 +42094,43 @@ function visibleTopicPickerIds(state) {
42062
42094
  function visibleBackgroundSessions(state) {
42063
42095
  return state.backgroundSessions.filter((session) => topicPickerQueryMatches(session.title, state.topicFilter));
42064
42096
  }
42097
+ function topicRank(state, topic) {
42098
+ if (blockedQuestionText(state, topic.id) !== undefined)
42099
+ return 0;
42100
+ if (state.activity[topic.id]?.running)
42101
+ return 1;
42102
+ return 2;
42103
+ }
42104
+ function groupTopicsIntoUnits(entries) {
42105
+ const units = [];
42106
+ for (const entry of entries) {
42107
+ const last = units.at(-1);
42108
+ if (entry.topic.isSubagent && last)
42109
+ last.push(entry);
42110
+ else
42111
+ units.push([entry]);
42112
+ }
42113
+ return units;
42114
+ }
42115
+ function sortedTopicEntries(state, entries) {
42116
+ const units = groupTopicsIntoUnits(entries);
42117
+ if (state.topicsPanelSort === "name") {
42118
+ return units.slice().sort((a, b) => (a[0]?.topic.title ?? "").localeCompare(b[0]?.topic.title ?? "")).flat();
42119
+ }
42120
+ return units.map((unit, order) => ({
42121
+ unit,
42122
+ order,
42123
+ rank: Math.min(...unit.map(({ topic }) => topicRank(state, topic)))
42124
+ })).sort((a, b) => a.rank - b.rank || a.order - b.order).flatMap(({ unit }) => unit);
42125
+ }
42065
42126
  function topicPickerItems(state) {
42066
42127
  const visible = visibleTopicPickerIds(state);
42067
42128
  const indexedTopics = state.topics.map((topic, index) => ({ topic, index })).filter(({ topic }) => visible.has(topic.id));
42129
+ const managerEntries = sortedTopicEntries(state, indexedTopics.filter(({ topic }) => topic.kind === "manager"));
42130
+ const regularEntries = sortedTopicEntries(state, indexedTopics.filter(({ topic }) => topic.kind !== "manager"));
42068
42131
  return [
42069
- ...indexedTopics.filter(({ topic }) => topic.kind === "manager").map(({ topic, index }) => ({ kind: "topic", id: topic.id, index })),
42070
- ...indexedTopics.filter(({ topic }) => topic.kind !== "manager").map(({ topic, index }) => ({ kind: "topic", id: topic.id, index })),
42132
+ ...managerEntries.map(({ topic, index }) => ({ kind: "topic", id: topic.id, index })),
42133
+ ...regularEntries.map(({ topic, index }) => ({ kind: "topic", id: topic.id, index })),
42071
42134
  ...visibleBackgroundSessions(state).map((session) => ({
42072
42135
  kind: "background",
42073
42136
  id: session.id
@@ -42359,6 +42422,22 @@ function failedToolText(text3) {
42359
42422
  return [title, ...mapped].join(`
42360
42423
  `);
42361
42424
  }
42425
+ function appendToolOutput(text3, content) {
42426
+ if (!content.trim())
42427
+ return text3;
42428
+ const [title = "Tool"] = text3.split(`
42429
+ `);
42430
+ if (TOOL_OUTPUT_EXCLUDED_PREFIXES.some((prefix) => title.startsWith(prefix)))
42431
+ return text3;
42432
+ if (text3.includes(`
42433
+ `))
42434
+ return text3;
42435
+ const lines = content.replace(/\r\n?/g, `
42436
+ `).replace(/\n+$/, "").split(`
42437
+ `);
42438
+ return [title, ...lines].join(`
42439
+ `);
42440
+ }
42362
42441
  function toolMessageId(status, toolUseId) {
42363
42442
  return `terminal-tool:${String(status.queryId ?? "query")}:${toolUseId}`;
42364
42443
  }
@@ -42458,14 +42537,17 @@ function applyAiStatus(state, topicId, raw, createdAt) {
42458
42537
  return state;
42459
42538
  const id = String(status.toolUseId ?? "");
42460
42539
  const failed = status.isError === true;
42461
- const tools = current3.tools.map((tool) => tool.id === id ? { ...tool, output: String(status.content ?? ""), status: failed ? "error" : "done" } : tool);
42540
+ const content = String(status.content ?? "");
42541
+ const tools = current3.tools.map((tool) => tool.id === id ? { ...tool, output: content, status: failed ? "error" : "done" } : tool);
42462
42542
  const withActivity = setActivity(state, topicId, { ...liveCurrent, tools });
42463
42543
  const messageId = toolMessageId(status, id);
42464
42544
  const existing = (withActivity.messages[topicId] ?? []).find((message2) => message2.id === messageId);
42545
+ const withOutput = existing ? appendToolOutput(existing.text, content) : undefined;
42546
+ const nextText = failed && (!existing || withOutput === existing.text) ? failedToolText(existing?.text ?? "") : withOutput;
42465
42547
  return patchMessage(withActivity, topicId, messageId, {
42466
42548
  editedAt: createdAt ?? new Date().toISOString(),
42467
42549
  toolResult: failed ? "error" : "ok",
42468
- ...failed && existing ? { text: failedToolText(existing.text) } : {}
42550
+ ...nextText !== undefined ? { text: nextText } : {}
42469
42551
  });
42470
42552
  }
42471
42553
  if (kind === "tool_status") {
@@ -42498,11 +42580,18 @@ function applyRuntimeEvent(state, event) {
42498
42580
  }
42499
42581
  return state;
42500
42582
  }
42501
- var NO_TOPIC_PICKER_SELECTION = -1;
42583
+ var NO_TOPIC_PICKER_SELECTION = -1, TOOL_OUTPUT_EXCLUDED_PREFIXES;
42502
42584
  var init_state2 = __esm(async () => {
42503
42585
  init_decision_graph();
42504
42586
  init_subagent_graph();
42505
42587
  await init_src();
42588
+ TOOL_OUTPUT_EXCLUDED_PREFIXES = [
42589
+ "Edit \xB7 ",
42590
+ "Write \xB7 ",
42591
+ "Delete \xB7 ",
42592
+ "Ask session \xB7 ",
42593
+ "Tell session \xB7 "
42594
+ ];
42506
42595
  });
42507
42596
 
42508
42597
  // ../../adapters/terminal/src/context-usage.ts
@@ -43615,7 +43704,6 @@ function toolMessageLines(message2, width) {
43615
43704
  `);
43616
43705
  const failed = message2.toolResult === "error";
43617
43706
  const done = failed || Boolean(message2.editedAt);
43618
- const fileMutation = /^(?:Edit|Write|Delete) \u00B7 /.test(title);
43619
43707
  const wrapWidth = Math.max(4, width - 6);
43620
43708
  const marker = failed ? "!" : done ? "\u2713" : "\u25CF";
43621
43709
  const headerText = ` ${marker} ${title}`;
@@ -43631,14 +43719,8 @@ function toolMessageLines(message2, width) {
43631
43719
  { text: ")" }
43632
43720
  ] : [{ text: headerText, fg: headerColor }]
43633
43721
  });
43634
- if (!fileMutation) {
43635
- return [
43636
- header,
43637
- ...details.slice(0, 2).flatMap((detail) => wrapText(detail, wrapWidth).slice(0, 1).map((text3) => line(` ${text3}`, { fg: toolDetailColor(detail), dim: !failed })))
43638
- ];
43639
- }
43640
43722
  const wrapped = details.flatMap((detail) => wrapText(detail, wrapWidth).map((text3) => ({ detail, text: text3 })));
43641
- const visible = wrapped.slice(0, FILE_PREVIEW_MAX_LINES);
43723
+ const visible = wrapped.slice(0, TOOL_PREVIEW_MAX_LINES);
43642
43724
  const hidden = wrapped.length - visible.length;
43643
43725
  return [
43644
43726
  header,
@@ -43743,50 +43825,255 @@ function taskLines(state, width) {
43743
43825
  const visibleTasks = tasks3.slice(0, INLINE_TASK_LIMIT);
43744
43826
  const hidden = tasks3.length - visibleTasks.length;
43745
43827
  return [
43746
- line(" \u25EB Tasks \xB7 Ctrl-T sidebar", { fg: theme.amber, bold: true }),
43828
+ line(" \u25EB Tasks \xB7 Ctrl-P sidebar", { fg: theme.amber, bold: true }),
43747
43829
  ...visibleTasks.flatMap((task) => wrapText(task.trimStart(), Math.max(4, width - 6)).map((text3) => line(` ${text3}`, { fg: theme.muted }))),
43748
43830
  ...hidden > 0 ? [line(` +${hidden} more`, { fg: theme.muted, dim: true })] : [],
43749
43831
  line("")
43750
43832
  ];
43751
43833
  }
43752
43834
  function terminalBodyLayout(state, width) {
43753
- const showTaskSidebar = width >= TASK_SIDEBAR_MIN_WIDTH && state.taskSidebarEnabled && !state.overlay && !state.creatingTopic && taskItems(state).length > 0;
43835
+ const wideEnough = width >= SIDE_PANEL_MIN_WIDTH && state.topicsPanelEnabled && (!state.overlay || state.overlay === "background-session") && !state.creatingTopic;
43836
+ const showTaskPanel = wideEnough && taskItems(state).length > 0;
43837
+ const showTopicsPanel = wideEnough && (state.topics.length > 1 || visibleBackgroundSessions(state).length > 0);
43838
+ const showSide = showTaskPanel || showTopicsPanel;
43754
43839
  return {
43755
- conversationWidth: showTaskSidebar ? width - TASK_SIDEBAR_WIDTH - TASK_SIDEBAR_GAP : width,
43756
- taskWidth: showTaskSidebar ? TASK_SIDEBAR_WIDTH : 0,
43757
- showTaskSidebar
43758
- };
43840
+ conversationWidth: showSide ? width - SIDE_PANEL_WIDTH - SIDE_PANEL_GAP : width,
43841
+ sideWidth: showSide ? SIDE_PANEL_WIDTH : 0,
43842
+ showTaskPanel,
43843
+ showTopicsPanel
43844
+ };
43845
+ }
43846
+ function topicStatusIndicator(state, topic, animationFrame) {
43847
+ const blockedText = blockedQuestionText(state, topic.id);
43848
+ if (blockedText !== undefined)
43849
+ return { icon: "?", fg: theme.amber, blockedText };
43850
+ if (state.activity[topic.id]?.running) {
43851
+ return { icon: workingFrame(animationFrame), fg: theme.green };
43852
+ }
43853
+ return { icon: "\u25CB", fg: theme.muted };
43854
+ }
43855
+ function topicPanelRowLines(state, topic, topicIndex, animationFrame) {
43856
+ const selected = topic.id === state.activeTopicId;
43857
+ const hovered = !selected && topic.id === state.sidePanelHoverTopicId;
43858
+ const status = topicStatusIndicator(state, topic, animationFrame);
43859
+ const childPrefix = subagentTreePrefix(topic, state.topics);
43860
+ const bg2 = selected || hovered ? theme.selected : theme.canvas;
43861
+ const header = line(` ${selected ? "\u203A" : " "} ${childPrefix}${status.icon} ${topic.title}`, {
43862
+ fg: selected ? theme.text : status.fg,
43863
+ bg: bg2,
43864
+ bold: selected,
43865
+ pickerTarget: { topicIndex }
43866
+ });
43867
+ if (status.blockedText === undefined)
43868
+ return [header];
43869
+ return [
43870
+ header,
43871
+ line(` ${status.blockedText}`, {
43872
+ fg: theme.amber,
43873
+ bg: bg2,
43874
+ pickerTarget: { topicIndex }
43875
+ })
43876
+ ];
43877
+ }
43878
+ function topicPanelGroupLines(state, label, entries, animationFrame) {
43879
+ if (entries.length === 0)
43880
+ return [];
43881
+ return [
43882
+ line(` ${label}`, { fg: theme.cyan, dim: true, bold: true, bg: theme.canvas }),
43883
+ ...sortedTopicEntries(state, entries).flatMap(({ topic, topicIndex }) => topicPanelRowLines(state, topic, topicIndex, animationFrame))
43884
+ ];
43885
+ }
43886
+ function topicsPanelRows(state, animationFrame) {
43887
+ const indexed = state.topics.map((topic, topicIndex) => ({ topic, topicIndex }));
43888
+ const managerEntries = indexed.filter(({ topic }) => topic.kind === "manager");
43889
+ const regularEntries = indexed.filter(({ topic }) => topic.kind !== "manager");
43890
+ const topicRows = [
43891
+ ...topicPanelGroupLines(state, "manager", managerEntries, animationFrame),
43892
+ ...topicPanelGroupLines(state, "topics", regularEntries, animationFrame)
43893
+ ];
43894
+ const sessionRows = ["cron", "memory", "compact"].flatMap((kind) => {
43895
+ const matching = visibleBackgroundSessions(state).filter((session) => session.kind === kind);
43896
+ if (matching.length === 0)
43897
+ return [];
43898
+ return [
43899
+ line(` ${kind}`, { fg: theme.cyan, dim: true, bold: true, bg: theme.canvas }),
43900
+ ...matching.map((session) => {
43901
+ const hovered = session.id === state.sidePanelHoverBackgroundSessionId;
43902
+ return line(` ${session.active ? workingFrame(animationFrame) : "\u25CB"} ${session.title}`, {
43903
+ fg: hovered ? theme.text : session.active ? theme.green : theme.muted,
43904
+ bg: hovered ? theme.selected : theme.canvas,
43905
+ pickerTarget: { backgroundSessionId: session.id }
43906
+ });
43907
+ })
43908
+ ];
43909
+ });
43910
+ return [...topicRows, ...sessionRows];
43759
43911
  }
43760
- function taskSidebarLines(state, width, height) {
43912
+ function taskPanelRows(state, width) {
43761
43913
  const items = taskItems(state);
43762
- const groups = items.map((task) => {
43914
+ return items.flatMap((task) => {
43763
43915
  const completed = /^\s*(?:\[x\]|\u2612|\u2713|\u2705)/i.test(task);
43764
- return wrapText(task.trimStart(), Math.max(4, width - 4)).map((text3) => line(` ${text3}`, {
43916
+ return wrapText(task.trimStart(), Math.max(4, width - 2)).map((text3) => line(` ${text3}`, {
43765
43917
  fg: completed ? theme.muted : theme.text,
43766
- dim: completed
43918
+ dim: completed,
43919
+ bg: theme.canvas
43767
43920
  }));
43768
43921
  });
43769
- const capacity = Math.max(1, height);
43770
- const total = groups.reduce((sum, group) => sum + group.length, 0);
43771
- if (total <= capacity)
43772
- return groups.flat();
43773
- if (capacity === 1)
43774
- return groups[0]?.slice(0, 1) ?? [];
43775
- const rows = [];
43776
- let shown = 0;
43777
- for (const group of groups) {
43778
- if (rows.length + group.length > capacity - 1) {
43779
- if (shown === 0) {
43780
- rows.push(...group.slice(0, capacity - 1));
43781
- shown = 1;
43782
- }
43783
- break;
43922
+ }
43923
+ function panelHeaderHeight(sectionHeight) {
43924
+ return Math.min(2, sectionHeight);
43925
+ }
43926
+ function panelContentHeight(sectionHeight) {
43927
+ return Math.max(0, sectionHeight - panelHeaderHeight(sectionHeight));
43928
+ }
43929
+ function scrollbarThumb(totalRows, viewportRows, offset) {
43930
+ if (viewportRows <= 0 || totalRows <= 0)
43931
+ return { top: 0, len: 0 };
43932
+ const len = Math.max(1, Math.min(viewportRows, Math.round(viewportRows * viewportRows / totalRows)));
43933
+ const maxThumbTop = Math.max(0, viewportRows - len);
43934
+ const maxOffset = Math.max(1, totalRows - viewportRows);
43935
+ const top = maxThumbTop === 0 ? 0 : Math.round(Math.min(offset, maxOffset) * maxThumbTop / maxOffset);
43936
+ return { top, len };
43937
+ }
43938
+ function scrollOffsetFromDragRow(totalRows, viewportRows, trackRow, grabRowOffset) {
43939
+ if (viewportRows <= 0 || totalRows <= 0)
43940
+ return 0;
43941
+ const { len } = scrollbarThumb(totalRows, viewportRows, 0);
43942
+ const maxThumbTop = Math.max(0, viewportRows - len);
43943
+ if (maxThumbTop === 0)
43944
+ return 0;
43945
+ const clampedRow = Math.max(0, Math.min(trackRow, viewportRows - 1));
43946
+ const desiredTop = Math.min(maxThumbTop, Math.max(0, clampedRow - grabRowOffset));
43947
+ const maxOffset = Math.max(0, totalRows - viewportRows);
43948
+ return Math.round(desiredTop * maxOffset / maxThumbTop);
43949
+ }
43950
+ function scrollbarCell(totalRows, viewportRows, offset, rowIndex, accent) {
43951
+ const thumb = scrollbarThumb(totalRows, viewportRows, offset);
43952
+ const isThumb = rowIndex >= thumb.top && rowIndex < thumb.top + thumb.len;
43953
+ return paint(isThumb ? "\u2590" : "\u2595", { fg: isThumb ? accent : theme.muted, bg: theme.canvas });
43954
+ }
43955
+ function renderPanelSection(label, rows, width, height, scrollOffset, accent) {
43956
+ if (height <= 0 || width <= 0)
43957
+ return { lines: [], rowTargets: [] };
43958
+ const headerHeight = panelHeaderHeight(height);
43959
+ const contentHeight = panelContentHeight(height);
43960
+ const overflow = rows.length > contentHeight;
43961
+ const textWidth = overflow ? Math.max(1, width - 1) : width;
43962
+ const clampedOffset = Math.max(0, Math.min(scrollOffset, Math.max(0, rows.length - contentHeight)));
43963
+ const visible = rows.slice(clampedOffset, clampedOffset + contentHeight);
43964
+ const lines = [];
43965
+ if (headerHeight >= 1)
43966
+ lines.push(paint("\u2500".repeat(width), { fg: theme.border, bg: theme.canvas }));
43967
+ if (headerHeight >= 2) {
43968
+ lines.push(paint(fit(` ${label}`, width), { fg: accent, bg: theme.canvas, bold: true }));
43969
+ }
43970
+ const rowTargets = [];
43971
+ for (let index = 0;index < contentHeight; index += 1) {
43972
+ const item = visible[index];
43973
+ const text3 = paint(fit(item?.text ?? "", textWidth), {
43974
+ fg: item?.fg ?? theme.text,
43975
+ bg: item?.bg ?? theme.canvas,
43976
+ bold: item?.bold,
43977
+ dim: item?.dim
43978
+ });
43979
+ const scrollCell = overflow ? scrollbarCell(rows.length, contentHeight, clampedOffset, index, accent) : "";
43980
+ lines.push(`${text3}${scrollCell}`);
43981
+ if (item?.pickerTarget)
43982
+ rowTargets.push({ y: headerHeight + index, pickerTarget: item.pickerTarget });
43983
+ }
43984
+ return {
43985
+ lines,
43986
+ rowTargets,
43987
+ track: overflow ? { x: width - 1, viewportRows: contentHeight, rowCount: rows.length } : undefined
43988
+ };
43989
+ }
43990
+ function sidePanelSectionHeights(layout, bodyHeight) {
43991
+ if (layout.showTopicsPanel && layout.showTaskPanel) {
43992
+ const topicsHeight = Math.max(3, Math.floor(bodyHeight / 2));
43993
+ return { topicsHeight, taskHeight: Math.max(0, bodyHeight - topicsHeight) };
43994
+ }
43995
+ if (layout.showTopicsPanel)
43996
+ return { topicsHeight: bodyHeight, taskHeight: 0 };
43997
+ if (layout.showTaskPanel)
43998
+ return { topicsHeight: 0, taskHeight: bodyHeight };
43999
+ return { topicsHeight: 0, taskHeight: 0 };
44000
+ }
44001
+ function renderSideColumn(state, layout, bodyHeight, animationFrame) {
44002
+ const { showTaskPanel, showTopicsPanel, sideWidth } = layout;
44003
+ if (!showTaskPanel && !showTopicsPanel)
44004
+ return { lines: [], targets: [], tracks: [] };
44005
+ const { topicsHeight, taskHeight } = sidePanelSectionHeights(layout, bodyHeight);
44006
+ const sections = [];
44007
+ if (showTopicsPanel) {
44008
+ sections.push({
44009
+ panel: "topics",
44010
+ label: state.topicsPanelSort === "activity" ? "ACTIVITY \xB7 name" : "activity \xB7 NAME",
44011
+ rows: topicsPanelRows(state, animationFrame),
44012
+ accent: theme.accent,
44013
+ height: topicsHeight,
44014
+ offset: 0,
44015
+ scrollOffset: state.topicsPanelScrollOffset
44016
+ });
44017
+ }
44018
+ if (showTaskPanel) {
44019
+ sections.push({
44020
+ panel: "tasks",
44021
+ label: "tasks",
44022
+ rows: taskPanelRows(state, sideWidth),
44023
+ accent: theme.taskBorder,
44024
+ height: taskHeight,
44025
+ offset: showTopicsPanel ? topicsHeight : 0,
44026
+ scrollOffset: state.taskPanelScrollOffset
44027
+ });
44028
+ }
44029
+ const lines = [];
44030
+ const targets = [];
44031
+ const tracks = [];
44032
+ for (const section of sections) {
44033
+ const result = renderPanelSection(section.label, section.rows, sideWidth, section.height, section.scrollOffset, section.accent);
44034
+ lines.push(...result.lines);
44035
+ for (const target of result.rowTargets) {
44036
+ targets.push({ y: section.offset + target.y, pickerTarget: target.pickerTarget });
44037
+ }
44038
+ if (result.track) {
44039
+ tracks.push({
44040
+ panel: section.panel,
44041
+ x: result.track.x,
44042
+ yStart: section.offset + panelHeaderHeight(section.height),
44043
+ viewportRows: result.track.viewportRows,
44044
+ rowCount: result.track.rowCount
44045
+ });
43784
44046
  }
43785
- rows.push(...group);
43786
- shown += 1;
43787
44047
  }
43788
- rows.push(line(` +${items.length - shown} more`, { fg: theme.muted, dim: true }));
43789
- return rows;
44048
+ while (lines.length < bodyHeight)
44049
+ lines.push(paint(" ".repeat(sideWidth), { bg: theme.canvas }));
44050
+ return { lines: lines.slice(0, bodyHeight), targets, tracks };
44051
+ }
44052
+ function maxTopicsPanelScrollOffset(state, columns, rows) {
44053
+ const width = Math.max(1, columns);
44054
+ const height = Math.max(14, rows);
44055
+ if (state.overlay)
44056
+ return 0;
44057
+ const layout = terminalBodyLayout(state, width);
44058
+ if (!layout.showTopicsPanel)
44059
+ return 0;
44060
+ const bodyHeight = Math.max(3, height - footerLines(state, width).length - decisionPane(state, width).length - vaultRequestPane(state, width).length - composerPane(state, layout.conversationWidth).lines.length);
44061
+ const { topicsHeight } = sidePanelSectionHeights(layout, bodyHeight);
44062
+ const viewport = panelContentHeight(topicsHeight);
44063
+ return Math.max(0, topicsPanelRows(state, 0).length - viewport);
44064
+ }
44065
+ function maxTaskPanelScrollOffset(state, columns, rows) {
44066
+ const width = Math.max(1, columns);
44067
+ const height = Math.max(14, rows);
44068
+ if (state.overlay)
44069
+ return 0;
44070
+ const layout = terminalBodyLayout(state, width);
44071
+ if (!layout.showTaskPanel)
44072
+ return 0;
44073
+ const bodyHeight = Math.max(3, height - footerLines(state, width).length - decisionPane(state, width).length - vaultRequestPane(state, width).length - composerPane(state, layout.conversationWidth).lines.length);
44074
+ const { taskHeight } = sidePanelSectionHeights(layout, bodyHeight);
44075
+ const viewport = panelContentHeight(taskHeight);
44076
+ return Math.max(0, taskPanelRows(state, layout.sideWidth).length - viewport);
43790
44077
  }
43791
44078
  function helpLines() {
43792
44079
  return [
@@ -43796,7 +44083,9 @@ function helpLines() {
43796
44083
  line(" \u2190 \u2192 move \xB7 Ctrl/Alt-\u2190 \u2192 move by word \xB7 \u2191 \u2193 history"),
43797
44084
  line(" Alt-Backspace delete word \xB7 Ctrl-U clear before cursor"),
43798
44085
  line(" Cmd-Backspace works when forwarded \xB7 Ctrl-W/K aliases"),
43799
- line(" Mouse wheel / PgUp/PgDn scroll \xB7 Ctrl-E load older \xB7 Ctrl-T tasks"),
44086
+ line(" Mouse wheel / PgUp/PgDn scroll \xB7 Ctrl-E load older"),
44087
+ line(" Ctrl-P topics + tasks panel \xB7 click/hover a row to switch \xB7 drag scrollbar"),
44088
+ line(" Ctrl-R sort topics panel (activity / name)"),
43800
44089
  line(" Ctrl-O topics \xB7 Ctrl-G subagents \xB7 Ctrl-D decisions \xB7 Ctrl-X abort"),
43801
44090
  line(" In topics: type to filter \xB7 Ctrl-N new \xB7 Ctrl-D delete"),
43802
44091
  line(" Esc/Ctrl-C stop active turn \xB7 Ctrl-C twice when idle to quit"),
@@ -43948,8 +44237,8 @@ function topicOverlayCursor(state, width, bodyHeight) {
43948
44237
  function topicOverlayLines(state, width, height, animationFrame = 0) {
43949
44238
  const visibleIds = visibleTopicPickerIds(state);
43950
44239
  const indexedTopics = state.topics.map((topic, topicIndex) => ({ topic, topicIndex })).filter(({ topic }) => visibleIds.has(topic.id));
43951
- const managerTopics = indexedTopics.filter(({ topic }) => topic.kind === "manager");
43952
- const regularTopics = indexedTopics.filter(({ topic }) => topic.kind !== "manager");
44240
+ const managerTopics = sortedTopicEntries(state, indexedTopics.filter(({ topic }) => topic.kind === "manager"));
44241
+ const regularTopics = sortedTopicEntries(state, indexedTopics.filter(({ topic }) => topic.kind !== "manager"));
43953
44242
  const entries = [];
43954
44243
  for (const [label, topics] of [
43955
44244
  ["Manager", managerTopics],
@@ -43997,30 +44286,48 @@ function topicOverlayLines(state, width, height, animationFrame = 0) {
43997
44286
  query2.length > 0 ? line(`${TOPIC_FILTER_LABEL}${safeText(query2)}`, { fg: theme.cyan, bold: true }) : line(""),
43998
44287
  ...entries.length === 0 ? [
43999
44288
  line(query2.length > 0 ? ` No topics match \u201C${safeText(query2)}\u201D \xB7 Esc clears the filter` : " No topics yet \xB7 Press Ctrl-N to create one", { fg: theme.muted })
44000
- ] : entries.slice(start, start + visibleCount).map((entry) => {
44289
+ ] : entries.slice(start, start + visibleCount).flatMap((entry) => {
44001
44290
  if (entry.kind === "heading") {
44002
- return line(` ${entry.label}`, { fg: theme.cyan, bold: true });
44291
+ return [line(` ${entry.label}`, { fg: theme.cyan, bold: true })];
44003
44292
  }
44004
44293
  if (entry.kind === "separator") {
44005
- return line(` ${"\u2500".repeat(Math.max(1, width - 4))}`, { fg: theme.border });
44294
+ return [line(` ${"\u2500".repeat(Math.max(1, width - 4))}`, { fg: theme.border })];
44006
44295
  }
44007
44296
  if (entry.kind === "background") {
44008
44297
  const selected2 = entry.sessionId === state.topicPickerBackgroundId;
44009
- return line(` ${selected2 ? "\u203A" : " "} ${entry.active ? workingFrame(animationFrame) : "\u25CB"} ${entry.label} \xB7 ${entry.status}`, {
44010
- fg: selected2 ? theme.text : entry.active ? theme.green : theme.muted,
44011
- bg: selected2 ? theme.selected : theme.canvas,
44012
- bold: selected2
44013
- });
44298
+ return [
44299
+ line(` ${selected2 ? "\u203A" : " "} ${entry.active ? workingFrame(animationFrame) : "\u25CB"} ${entry.label} \xB7 ${entry.status}`, {
44300
+ fg: selected2 ? theme.text : entry.active ? theme.green : theme.muted,
44301
+ bg: selected2 ? theme.selected : theme.canvas,
44302
+ bold: selected2,
44303
+ pickerTarget: { backgroundSessionId: entry.sessionId }
44304
+ })
44305
+ ];
44014
44306
  }
44015
44307
  const { topic, topicIndex } = entry;
44016
44308
  const selected = topicIndex === state.topicPickerIndex;
44017
- const running = state.activity[topic.id]?.running;
44309
+ const status = topicStatusIndicator(state, topic, animationFrame);
44018
44310
  const childPrefix = subagentTreePrefix(topic, state.topics);
44019
- return line(` ${selected ? "\u203A" : " "} ${childPrefix}${running ? workingFrame(animationFrame) : "\u25CB"} ${topic.title} \xB7 ${topic.agent ?? "no agent"} \xB7 ${effectiveTopicModel(topic)} \xB7 ${effectiveTopicEffort(topic)}`, {
44020
- fg: selected ? theme.text : running ? theme.green : theme.muted,
44021
- bg: selected ? theme.selected : theme.canvas,
44022
- bold: selected
44311
+ const bg2 = selected ? theme.selected : theme.canvas;
44312
+ const header = line(` ${selected ? "\u203A" : " "} ${childPrefix}${status.icon} ${topic.title} \xB7 ${topic.agent ?? "no agent"} \xB7 ${effectiveTopicModel(topic)} \xB7 ${effectiveTopicEffort(topic)}`, {
44313
+ fg: selected ? theme.text : status.fg,
44314
+ bg: bg2,
44315
+ bold: selected,
44316
+ pickerTarget: { topicIndex }
44023
44317
  });
44318
+ const running = state.activity[topic.id]?.running;
44319
+ const subtitle = status.blockedText ?? (running ? activityDetail(state.activity[topic.id]?.status) : undefined);
44320
+ if (!subtitle)
44321
+ return [header];
44322
+ return [
44323
+ header,
44324
+ line(` ${subtitle}`, {
44325
+ fg: status.blockedText !== undefined ? theme.amber : theme.muted,
44326
+ dim: status.blockedText === undefined,
44327
+ bg: bg2,
44328
+ pickerTarget: { topicIndex }
44329
+ })
44330
+ ];
44024
44331
  })
44025
44332
  ];
44026
44333
  }
@@ -44366,6 +44673,16 @@ function conversationViewport(lineCount, height, requestedOffset) {
44366
44673
  offset: Math.min(maxOffset, Math.max(0, requestedOffset))
44367
44674
  };
44368
44675
  }
44676
+ function popupPaneWidth(width) {
44677
+ return Math.max(0, Math.min(POPUP_PANE_MAX_WIDTH, width - 4));
44678
+ }
44679
+ function centerPaneRows(rows, boxWidth, totalWidth) {
44680
+ const left = Math.max(0, Math.floor((totalWidth - boxWidth) / 2));
44681
+ const right = Math.max(0, totalWidth - boxWidth - left);
44682
+ const leftPad = left > 0 ? paint(" ".repeat(left), { bg: theme.canvas }) : "";
44683
+ const rightPad = right > 0 ? paint(" ".repeat(right), { bg: theme.canvas }) : "";
44684
+ return rows.map((row) => `${leftPad}${row}${rightPad}`);
44685
+ }
44369
44686
  function decisionPane(state, width) {
44370
44687
  if (state.overlay)
44371
44688
  return [];
@@ -44376,6 +44693,7 @@ function decisionPane(state, width) {
44376
44693
  if (!ask || !question?.choices.length)
44377
44694
  return [];
44378
44695
  const selected = Math.min(state.askChoiceIndex, question.choices.length - 1);
44696
+ const boxWidth = popupPaneWidth(width);
44379
44697
  const content = [
44380
44698
  line(` ${question.question}`, { bold: true }),
44381
44699
  ...question.choices.map((choice, index) => line(` ${index === selected ? "\u25CF" : "\u25CB"} ${choice.label}${choice.description ? ` \u2014 ${choice.description}` : ""}`, {
@@ -44384,7 +44702,8 @@ function decisionPane(state, width) {
44384
44702
  bold: index === selected
44385
44703
  }))
44386
44704
  ];
44387
- return framePane("decision required \xB7 \u2191\u2193 select \xB7 Enter confirm", content, width, Math.min(8, Math.max(4, content.length + 2)), { active: true, accent: theme.amber });
44705
+ const rows = framePane("decision required \xB7 \u2191\u2193 select \xB7 Enter confirm", content, boxWidth, Math.min(8, Math.max(4, content.length + 2)), { active: true, accent: theme.amber });
44706
+ return centerPaneRows(rows, boxWidth, width);
44388
44707
  }
44389
44708
  function vaultRequestPane(state, width) {
44390
44709
  if (state.overlay)
@@ -44393,15 +44712,17 @@ function vaultRequestPane(state, width) {
44393
44712
  if (!request)
44394
44713
  return [];
44395
44714
  const declining = state.vaultRequestMode === "confirm-decline";
44715
+ const boxWidth = popupPaneWidth(width);
44396
44716
  const content = [
44397
44717
  line(` ${request.key}`, { bold: true }),
44398
- ...request.description ? wrapText(request.description, Math.max(4, width - 6)).map((text3) => line(` ${text3}`, { fg: theme.muted })) : [],
44718
+ ...request.description ? wrapText(request.description, Math.max(4, boxWidth - 6)).map((text3) => line(` ${text3}`, { fg: theme.muted })) : [],
44399
44719
  declining ? line(" Decline this request? y / n", { fg: theme.red, bold: true }) : line(" Enter save to Vault \xB7 Esc clear / decline \xB7 Alt-Enter newline", {
44400
44720
  fg: theme.subtle
44401
44721
  }),
44402
44722
  ...state.vaultRequestNotice ? [line(` ! ${state.vaultRequestNotice}`, { fg: theme.amber })] : []
44403
44723
  ];
44404
- return framePane(`secret requested by ${state.aiName} \xB7 never shown to the agent${state.vaultRequestSubmitting ? ` \xB7 ${workingFrame(0)} saving` : ""}`, content, width, Math.min(8, Math.max(4, content.length + 2)), { active: true, accent: theme.accent });
44724
+ const rows = framePane(`secret requested by ${state.aiName} \xB7 never shown to the agent${state.vaultRequestSubmitting ? ` \xB7 ${workingFrame(0)} saving` : ""}`, content, boxWidth, Math.min(8, Math.max(4, content.length + 2)), { active: true, accent: theme.accent });
44725
+ return centerPaneRows(rows, boxWidth, width);
44405
44726
  }
44406
44727
  function wrapLineWithCursor(value, codePointColumn, width) {
44407
44728
  const chars = Array.from(safeText(value));
@@ -44475,6 +44796,20 @@ function inputVisualLines(state, width) {
44475
44796
  }
44476
44797
  return { lines: result, cursorLine, cursorColumn };
44477
44798
  }
44799
+ function shiftLines(lines, marginWidth) {
44800
+ if (marginWidth <= 0)
44801
+ return lines;
44802
+ const margin = paint(" ".repeat(marginWidth), { bg: theme.canvas });
44803
+ return lines.map((row) => `${margin}${row}`);
44804
+ }
44805
+ function shiftComposerPane(pane, marginWidth) {
44806
+ if (marginWidth <= 0)
44807
+ return pane;
44808
+ return {
44809
+ lines: shiftLines(pane.lines, marginWidth),
44810
+ cursor: { x: pane.cursor.x + marginWidth, y: pane.cursor.y }
44811
+ };
44812
+ }
44478
44813
  function composerPane(state, width) {
44479
44814
  const title = state.creatingTopic ? "new topic \xB7 type a name \xB7 Enter create" : state.overlay === "vault" ? state.vaultMode === "list" ? "Vault command \xB7 Enter run \xB7 Esc close" : `${state.vaultMode === "key" ? "key" : state.vaultMode === "value" ? "secret value" : "description"} \xB7 Enter continue \xB7 Esc cancel` : "Ctrl-O topics \xB7 Ctrl-G subagents \xB7 Ctrl-D decisions";
44480
44815
  const visual = inputVisualLines(state, width);
@@ -44718,30 +45053,62 @@ function renderApp(state, columns, rows, animationFrame = 0, nowMs = terminalNow
44718
45053
  function renderAppFrame(state, columns, rows, animationFrame = 0, nowMs = terminalNowMs()) {
44719
45054
  const width = Math.max(1, columns);
44720
45055
  const height = Math.max(14, rows);
44721
- const footer = footerLines(state, width);
44722
- const decision = [...decisionPane(state, width), ...vaultRequestPane(state, width)];
45056
+ const layout = terminalBodyLayout(state, width);
45057
+ const conversationXStart = layout.sideWidth > 0 ? layout.sideWidth + SIDE_PANEL_GAP + 1 : 1;
45058
+ const footer = shiftLines(footerLines(state, layout.conversationWidth), conversationXStart - 1);
45059
+ const decision = shiftLines([
45060
+ ...decisionPane(state, layout.conversationWidth),
45061
+ ...vaultRequestPane(state, layout.conversationWidth)
45062
+ ], conversationXStart - 1);
44723
45063
  const hideComposer = state.overlay === "background-session" || state.overlay === "topics" || state.overlay === "subagents" || state.overlay === "decisions" || state.overlay === "vault" && state.vaultMode === "confirm-delete";
44724
- const composer = hideComposer ? { lines: [], cursor: null } : composerPane(state, width);
45064
+ const composer = hideComposer ? { lines: [], cursor: null } : shiftComposerPane(composerPane(state, layout.conversationWidth), conversationXStart - 1);
44725
45065
  const bodyHeight = Math.max(3, height - footer.length - decision.length - composer.lines.length);
44726
- const layout = terminalBodyLayout(state, width);
44727
- const conversation = conversationLines(state, layout.conversationWidth, bodyHeight, animationFrame, nowMs, !layout.showTaskSidebar);
45066
+ const conversation = conversationLines(state, layout.conversationWidth, bodyHeight, animationFrame, nowMs, !layout.showTaskPanel);
44728
45067
  const conversationBody = renderBody(conversation, layout.conversationWidth, bodyHeight);
44729
- const body = layout.showTaskSidebar ? (() => {
44730
- const taskPane = framePane("Tasks \xB7 Ctrl-T", taskSidebarLines(state, layout.taskWidth, Math.max(0, bodyHeight - 2)), layout.taskWidth, bodyHeight, { active: true, accent: theme.taskBorder });
44731
- if (taskPane.length === 0)
44732
- return renderBody(conversation, width, bodyHeight);
44733
- const gap = paint(" ".repeat(TASK_SIDEBAR_GAP), { bg: theme.canvas });
44734
- return conversationBody.map((row, index) => `${row}${gap}${taskPane[index] ?? ""}`);
45068
+ const sideColumn = layout.showTaskPanel || layout.showTopicsPanel ? renderSideColumn(state, layout, bodyHeight, animationFrame) : null;
45069
+ const body = sideColumn?.lines.length ? (() => {
45070
+ const gap = paint(" ".repeat(SIDE_PANEL_GAP), { bg: theme.canvas });
45071
+ return sideColumn.lines.map((row, index) => `${row}${gap}${conversationBody[index] ?? ""}`);
44735
45072
  })() : conversationBody;
44736
45073
  const codeCopyTargets = conversation.flatMap((item, index) => {
44737
45074
  if (item.codeCopy === undefined || index >= bodyHeight)
44738
45075
  return [];
44739
45076
  const trimmed = item.text.trimEnd();
44740
45077
  const leading = trimmed.length - trimmed.trimStart().length;
44741
- const xStart = displayWidth(trimmed.slice(0, leading)) + 1;
44742
- const xEnd = Math.min(displayWidth(trimmed), layout.conversationWidth);
45078
+ const xStart = conversationXStart + displayWidth(trimmed.slice(0, leading));
45079
+ const xEnd = Math.min(conversationXStart - 1 + displayWidth(trimmed), width);
44743
45080
  return xEnd >= xStart ? [{ xStart, xEnd, y: index + 1, text: item.codeCopy }] : [];
44744
45081
  });
45082
+ const overlayTargets = conversation.flatMap((item, index) => {
45083
+ if (!item.pickerTarget || index >= bodyHeight)
45084
+ return [];
45085
+ return [{ xStart: 1, xEnd: layout.conversationWidth, y: index + 1, ...item.pickerTarget }];
45086
+ });
45087
+ const sidePanelTargets = (sideColumn?.targets ?? []).map((target) => ({
45088
+ xStart: 1,
45089
+ xEnd: layout.sideWidth,
45090
+ y: target.y + 1,
45091
+ ...target.pickerTarget
45092
+ }));
45093
+ const topicPickerTargets = [...overlayTargets, ...sidePanelTargets];
45094
+ const sidePanelTracks = (sideColumn?.tracks ?? []).map((track) => ({
45095
+ ...track,
45096
+ x: 1 + track.x,
45097
+ yStart: track.yStart + 1
45098
+ }));
45099
+ const sideColumnBounds = sideColumn ? (() => {
45100
+ const { topicsHeight, taskHeight } = sidePanelSectionHeights(layout, bodyHeight);
45101
+ const sections = [];
45102
+ let offset = 0;
45103
+ if (layout.showTopicsPanel) {
45104
+ sections.push({ panel: "topics", yStart: offset + 1, yEnd: offset + topicsHeight });
45105
+ offset += topicsHeight;
45106
+ }
45107
+ if (layout.showTaskPanel) {
45108
+ sections.push({ panel: "tasks", yStart: offset + 1, yEnd: offset + taskHeight });
45109
+ }
45110
+ return { xStart: 1, xEnd: layout.sideWidth, sections };
45111
+ })() : null;
44745
45112
  const cursorY = composer.cursor ? body.length + decision.length + composer.cursor.y : Number.POSITIVE_INFINITY;
44746
45113
  const overlayCursor = topicOverlayCursor(state, layout.conversationWidth, body.length);
44747
45114
  const cursor = composer.cursor && cursorY <= height ? { x: Math.min(composer.cursor.x, width), y: cursorY } : overlayCursor && overlayCursor.y <= height ? overlayCursor : null;
@@ -44749,7 +45116,10 @@ function renderAppFrame(state, columns, rows, animationFrame = 0, nowMs = termin
44749
45116
  frame: linkifyUrls([...body, ...decision, ...composer.lines, ...footer].slice(0, height).join(`
44750
45117
  `)),
44751
45118
  cursor,
44752
- codeCopyTargets
45119
+ codeCopyTargets,
45120
+ topicPickerTargets,
45121
+ sidePanelTracks,
45122
+ sideColumnBounds
44753
45123
  };
44754
45124
  }
44755
45125
  function maxConversationScrollOffset(state, columns, rows) {
@@ -44761,9 +45131,9 @@ function maxConversationScrollOffset(state, columns, rows) {
44761
45131
  }
44762
45132
  if (state.overlay)
44763
45133
  return 0;
44764
- const bodyHeight = Math.max(3, height - footerLines(state, width).length - decisionPane(state, width).length - vaultRequestPane(state, width).length - composerPane(state, width).lines.length);
44765
45134
  const layout = terminalBodyLayout(state, width);
44766
- const lineCount = conversationContentLines(state, layout.conversationWidth, 0, terminalNowMs(), !layout.showTaskSidebar).length;
45135
+ const bodyHeight = Math.max(3, height - footerLines(state, width).length - decisionPane(state, width).length - vaultRequestPane(state, width).length - composerPane(state, layout.conversationWidth).lines.length);
45136
+ const lineCount = conversationContentLines(state, layout.conversationWidth, 0, terminalNowMs(), !layout.showTaskPanel).length;
44767
45137
  return conversationViewport(lineCount, bodyHeight, state.scrollOffset).maxOffset;
44768
45138
  }
44769
45139
  function preserveConversationScrollAnchor(previous, next, columns, rows) {
@@ -44801,7 +45171,7 @@ var theme, WORKING_FRAMES, CODE_COPY_MARKER = "\u29C9", WORKING_FRAME_INTERVAL_M
44801
45171
  default:
44802
45172
  return `${ESC}48;2;${rgb[0]};${rgb[1]};${rgb[2]}m`;
44803
45173
  }
44804
- }, BARE_URL_PATTERN, URL_TRAILING_PUNCTUATION, RENDERED_TAB_WIDTH = 4, CIRCLED_DIGIT_START = 9312, CIRCLED_DIGIT_END = 9331, CIRCLED_ZERO = 9450, isWrapSpace = (character) => character === " " || character === "\t", isWideBreak = (character) => runeWidth(character) === 2, NO_ROW_START, NO_ROW_END, WRAP_MIN_WIDTH = 2, FRAME_MIN_INNER_WIDTH = 4, FRAME_TITLE_MIN_INNER_WIDTH = 8, TABLE_MAX_WIDTH_FRACTION = 0.92, TABLE_MIN_COL_WIDTH = 4, FILE_PREVIEW_MAX_LINES = 12, messageLayoutCache, INLINE_TASK_LIMIT = 5, TASK_SIDEBAR_MIN_WIDTH = 110, TASK_SIDEBAR_WIDTH = 36, TASK_SIDEBAR_GAP = 1, TOPIC_FILTER_ROW_INDEX = 2, TOPIC_FILTER_LABEL = " Filter: ", COMPOSER_GUTTER = 4, COMPOSER_RIGHT_MARGIN = 1, COMPOSER_MIN_CONTENT_WIDTH = 4, NOTICE_STYLE;
45174
+ }, BARE_URL_PATTERN, URL_TRAILING_PUNCTUATION, RENDERED_TAB_WIDTH = 4, CIRCLED_DIGIT_START = 9312, CIRCLED_DIGIT_END = 9331, CIRCLED_ZERO = 9450, isWrapSpace = (character) => character === " " || character === "\t", isWideBreak = (character) => runeWidth(character) === 2, NO_ROW_START, NO_ROW_END, WRAP_MIN_WIDTH = 2, FRAME_MIN_INNER_WIDTH = 4, FRAME_TITLE_MIN_INNER_WIDTH = 8, TABLE_MAX_WIDTH_FRACTION = 0.92, TABLE_MIN_COL_WIDTH = 4, TOOL_PREVIEW_MAX_LINES = 12, messageLayoutCache, INLINE_TASK_LIMIT = 5, SIDE_PANEL_MIN_WIDTH = 110, SIDE_PANEL_WIDTH = 36, SIDE_PANEL_GAP = 1, TOPIC_FILTER_ROW_INDEX = 2, TOPIC_FILTER_LABEL = " Filter: ", POPUP_PANE_MAX_WIDTH = 72, COMPOSER_GUTTER = 4, COMPOSER_RIGHT_MARGIN = 1, COMPOSER_MIN_CONTENT_WIDTH = 4, NOTICE_STYLE;
44805
45175
  var init_render = __esm(async () => {
44806
45176
  init_color_depth();
44807
45177
  init_commands();
@@ -45059,6 +45429,7 @@ function consumeMouseInput(raw) {
45059
45429
  let scrollDelta = 0;
45060
45430
  let horizontalScrollDelta = 0;
45061
45431
  const events = [];
45432
+ const wheelEvents = [];
45062
45433
  const input = raw.replace(SGR_MOUSE_PATTERN, (_sequence, rawButton, rawX, rawY, suffix) => {
45063
45434
  const button = Number.parseInt(rawButton, 10);
45064
45435
  if (Number.isFinite(button) && (button & 64) !== 0) {
@@ -45068,6 +45439,10 @@ function consumeMouseInput(raw) {
45068
45439
  horizontalScrollDelta += delta;
45069
45440
  else
45070
45441
  scrollDelta += delta;
45442
+ const x = Number.parseInt(rawX, 10);
45443
+ const y = Number.parseInt(rawY, 10);
45444
+ if (Number.isFinite(x) && Number.isFinite(y))
45445
+ wheelEvents.push({ x, y, delta, horizontal });
45071
45446
  } else {
45072
45447
  const x = Number.parseInt(rawX, 10);
45073
45448
  const y = Number.parseInt(rawY, 10);
@@ -45082,11 +45457,14 @@ function consumeMouseInput(raw) {
45082
45457
  }
45083
45458
  return "";
45084
45459
  });
45085
- return { input, scrollDelta, horizontalScrollDelta, events };
45460
+ return { input, scrollDelta, horizontalScrollDelta, events, wheelEvents };
45086
45461
  }
45087
45462
  function codeCopyTargetAt(targets, point) {
45088
45463
  return targets.find((target) => target.y === point.y && point.x >= target.xStart && point.x <= target.xEnd);
45089
45464
  }
45465
+ function topicPickerTargetAt(targets, point) {
45466
+ return targets.find((target) => target.y === point.y && point.x >= target.xStart && point.x <= target.xEnd);
45467
+ }
45090
45468
  function escapeStopsActiveTurn(state) {
45091
45469
  if (state.overlay || state.creatingTopic)
45092
45470
  return false;
@@ -46816,9 +47194,9 @@ function altScreenSequences(env = process.env, savedDepth) {
46816
47194
  const setBackground = colored ? "\x1B]11;#0a0b0f\x07" : "";
46817
47195
  const fillCanvas = canvasFill(depth2);
46818
47196
  const resetBackground = colored ? "\x1B]111\x07" : "";
46819
- const modeResets = "\x1B[0m\x1B[?7h\x1B[?1l\x1B[?1006l\x1B[?1002l\x1B[?2004l\x1B[?25h\x1B[?1049l";
47197
+ const modeResets = "\x1B[0m\x1B[?7h\x1B[?1l\x1B[?1006l\x1B[?1003l\x1B[?2004l\x1B[?25h\x1B[?1049l";
46820
47198
  return {
46821
- enter: `${setBackground}\x1B[?1049h${fillCanvas}\x1B[2J\x1B[H\x1B[?25l\x1B[?2004h\x1B[?1002h\x1B[?1006h\x1B[?1l${push}`,
47199
+ enter: `${setBackground}\x1B[?1049h${fillCanvas}\x1B[2J\x1B[H\x1B[?25l\x1B[?2004h\x1B[?1003h\x1B[?1006h\x1B[?1l${push}`,
46822
47200
  exit: `${END_SYNCHRONIZED_UPDATE}${pop}${modeResets}${resetBackground}`,
46823
47201
  abortEnter: `${END_SYNCHRONIZED_UPDATE}${modeResets}`
46824
47202
  };
@@ -46861,6 +47239,10 @@ class TerminalApp {
46861
47239
  #subagentGraphDragPoint = null;
46862
47240
  #plainFrameLines = [];
46863
47241
  #codeCopyTargets = [];
47242
+ #topicPickerTargets = [];
47243
+ #sidePanelTracks = [];
47244
+ #sideColumnBounds = null;
47245
+ #sidePanelDrag = null;
46864
47246
  #lastInterruptAt = 0;
46865
47247
  #subagentGraphGeneration = 0;
46866
47248
  #subagentGraphAbortController = null;
@@ -47236,6 +47618,9 @@ class TerminalApp {
47236
47618
  const rows = process.stdout.rows ?? 30;
47237
47619
  const rendered = renderAppFrame(this.#state, columns, rows, this.#animationFrame);
47238
47620
  this.#codeCopyTargets = rendered.codeCopyTargets;
47621
+ this.#topicPickerTargets = rendered.topicPickerTargets;
47622
+ this.#sidePanelTracks = rendered.sidePanelTracks;
47623
+ this.#sideColumnBounds = rendered.sideColumnBounds;
47239
47624
  const baseFrame = rendered.frame;
47240
47625
  this.#plainFrameLines = stripAnsi(baseFrame).split(`
47241
47626
  `);
@@ -47623,19 +48008,25 @@ class TerminalApp {
47623
48008
  }
47624
48009
  #handleKeySegment(segment) {
47625
48010
  const mouse = consumeMouseInput(segment);
47626
- if (mouse.horizontalScrollDelta !== 0 && (this.#state.overlay === "subagents" || this.#state.overlay === "decisions")) {
48011
+ const { scrollDelta, horizontalScrollDelta } = this.#routeWheelEvents(mouse.wheelEvents);
48012
+ if (horizontalScrollDelta !== 0 && (this.#state.overlay === "subagents" || this.#state.overlay === "decisions")) {
47627
48013
  this.#selection = null;
47628
- this.#panActiveGraph(-mouse.horizontalScrollDelta, 0);
48014
+ this.#panActiveGraph(-horizontalScrollDelta, 0);
47629
48015
  }
47630
- if (mouse.scrollDelta !== 0) {
48016
+ if (scrollDelta !== 0) {
47631
48017
  this.#selection = null;
47632
- this.#scroll(mouse.scrollDelta);
48018
+ this.#scroll(scrollDelta);
47633
48019
  }
47634
48020
  for (const event of mouse.events) {
47635
48021
  if (this.#state.overlay === "subagents" || this.#state.overlay === "decisions") {
47636
48022
  this.#handleGraphMouse(event);
47637
- } else
47638
- this.#handleMouseSelection(event);
48023
+ continue;
48024
+ }
48025
+ if (this.#handleSidePanelScrollbarDrag(event))
48026
+ continue;
48027
+ if (this.#handlePickableRowMouse(event))
48028
+ continue;
48029
+ this.#handleMouseSelection(event);
47639
48030
  }
47640
48031
  const chunk = normalizeKeySequences(mouse.input);
47641
48032
  if (!chunk)
@@ -47674,10 +48065,17 @@ class TerminalApp {
47674
48065
  this.#toggleDecisionGraph();
47675
48066
  return;
47676
48067
  }
47677
- if (chunk === "\x14") {
48068
+ if (chunk === "\x10") {
47678
48069
  if (editingVaultSecret)
47679
48070
  return;
47680
- this.#state = toggleTaskSidebar(this.#state);
48071
+ this.#state = toggleTopicsPanel(this.#state);
48072
+ this.#queueRender();
48073
+ return;
48074
+ }
48075
+ if (chunk === "\x12") {
48076
+ if (editingVaultSecret)
48077
+ return;
48078
+ this.#state = cycleTopicsPanelSort(this.#state);
47681
48079
  this.#queueRender();
47682
48080
  return;
47683
48081
  }
@@ -48815,6 +49213,140 @@ class TerminalApp {
48815
49213
  }
48816
49214
  this.#queueRender();
48817
49215
  }
49216
+ #routeWheelEvents(events) {
49217
+ let scrollDelta = 0;
49218
+ let horizontalScrollDelta = 0;
49219
+ if (events.length === 0)
49220
+ return { scrollDelta, horizontalScrollDelta };
49221
+ const bounds = this.#sideColumnBounds;
49222
+ let touchedPanel = false;
49223
+ for (const event of events) {
49224
+ const section = bounds && event.x >= bounds.xStart && event.x <= bounds.xEnd ? bounds.sections.find((s) => event.y >= s.yStart && event.y <= s.yEnd) : undefined;
49225
+ if (!section) {
49226
+ if (event.horizontal)
49227
+ horizontalScrollDelta += event.delta;
49228
+ else
49229
+ scrollDelta += event.delta;
49230
+ continue;
49231
+ }
49232
+ touchedPanel = true;
49233
+ const columns = process.stdout.columns ?? 100;
49234
+ const rows = process.stdout.rows ?? 30;
49235
+ this.#state = section.panel === "topics" ? scrollTopicsPanel(this.#state, -event.delta, maxTopicsPanelScrollOffset(this.#state, columns, rows)) : scrollTaskPanel(this.#state, -event.delta, maxTaskPanelScrollOffset(this.#state, columns, rows));
49236
+ }
49237
+ if (touchedPanel)
49238
+ this.#queueRender();
49239
+ return { scrollDelta, horizontalScrollDelta };
49240
+ }
49241
+ #handleSidePanelScrollbarDrag(event) {
49242
+ if (event.kind === "press" && (event.button & 3) === 0) {
49243
+ const track2 = this.#sidePanelTracks.find((t) => event.x === t.x && event.y >= t.yStart && event.y < t.yStart + t.viewportRows);
49244
+ if (!track2)
49245
+ return false;
49246
+ const current3 = track2.panel === "topics" ? this.#state.topicsPanelScrollOffset : this.#state.taskPanelScrollOffset;
49247
+ const thumb = scrollbarThumb(track2.rowCount, track2.viewportRows, current3);
49248
+ this.#sidePanelDrag = {
49249
+ panel: track2.panel,
49250
+ track: track2,
49251
+ grabRowOffset: event.y - (track2.yStart + thumb.top)
49252
+ };
49253
+ return true;
49254
+ }
49255
+ const drag = this.#sidePanelDrag;
49256
+ if (!drag)
49257
+ return false;
49258
+ if (event.kind === "release") {
49259
+ this.#sidePanelDrag = null;
49260
+ return true;
49261
+ }
49262
+ const { track, grabRowOffset, panel } = drag;
49263
+ const offset = scrollOffsetFromDragRow(track.rowCount, track.viewportRows, event.y - track.yStart, grabRowOffset);
49264
+ const columns = process.stdout.columns ?? 100;
49265
+ const rows = process.stdout.rows ?? 30;
49266
+ this.#state = panel === "topics" ? setTopicsPanelScrollOffset(this.#state, offset, maxTopicsPanelScrollOffset(this.#state, columns, rows)) : setTaskPanelScrollOffset(this.#state, offset, maxTaskPanelScrollOffset(this.#state, columns, rows));
49267
+ this.#queueRender();
49268
+ return true;
49269
+ }
49270
+ #handlePickableRowMouse(event) {
49271
+ const inOverlay = this.#state.overlay === "topics";
49272
+ const hovering = event.kind === "drag" && (event.button & 3) === 3;
49273
+ const isClick = event.kind === "press" && (event.button & 3) === 0;
49274
+ if (!inOverlay && !hovering && !isClick)
49275
+ return false;
49276
+ const target = topicPickerTargetAt(this.#topicPickerTargets, { x: event.x, y: event.y });
49277
+ if (inOverlay) {
49278
+ if (!target)
49279
+ return true;
49280
+ const highlighted = this.#highlightPickedTarget(target);
49281
+ if (!hovering && highlighted)
49282
+ this.#selectPickedTopic();
49283
+ return true;
49284
+ }
49285
+ if (target?.backgroundSessionId !== undefined) {
49286
+ const sessionId = target.backgroundSessionId;
49287
+ if (hovering) {
49288
+ this.#setSidePanelHover(undefined, sessionId);
49289
+ return true;
49290
+ }
49291
+ this.#setSidePanelHover(undefined, undefined);
49292
+ this.#state = {
49293
+ ...this.#state,
49294
+ overlay: "background-session",
49295
+ topicPickerBackgroundId: sessionId,
49296
+ backgroundScrollOffset: 0,
49297
+ notice: undefined,
49298
+ noticeLevel: undefined
49299
+ };
49300
+ this.#queueRender();
49301
+ return true;
49302
+ }
49303
+ if (target?.topicIndex === undefined) {
49304
+ this.#setSidePanelHover(undefined, undefined);
49305
+ return false;
49306
+ }
49307
+ const topic = this.#state.topics[target.topicIndex];
49308
+ if (!topic)
49309
+ return false;
49310
+ if (hovering) {
49311
+ this.#setSidePanelHover(topic.id, undefined);
49312
+ return true;
49313
+ }
49314
+ this.#setSidePanelHover(undefined, undefined);
49315
+ if (topic.id !== this.#state.activeTopicId)
49316
+ this.#activateTopic(topic.id);
49317
+ return true;
49318
+ }
49319
+ #setSidePanelHover(topicId, backgroundSessionId) {
49320
+ if (this.#state.sidePanelHoverTopicId === topicId && this.#state.sidePanelHoverBackgroundSessionId === backgroundSessionId) {
49321
+ return;
49322
+ }
49323
+ this.#state = {
49324
+ ...this.#state,
49325
+ sidePanelHoverTopicId: topicId,
49326
+ sidePanelHoverBackgroundSessionId: backgroundSessionId
49327
+ };
49328
+ this.#queueRender();
49329
+ }
49330
+ #highlightPickedTarget(target) {
49331
+ if (target.backgroundSessionId !== undefined) {
49332
+ if (this.#state.topicPickerBackgroundId === target.backgroundSessionId)
49333
+ return true;
49334
+ this.#state = { ...this.#state, topicPickerBackgroundId: target.backgroundSessionId };
49335
+ } else if (target.topicIndex !== undefined) {
49336
+ if (!this.#state.topicPickerBackgroundId && this.#state.topicPickerIndex === target.topicIndex) {
49337
+ return true;
49338
+ }
49339
+ this.#state = {
49340
+ ...this.#state,
49341
+ topicPickerIndex: target.topicIndex,
49342
+ topicPickerBackgroundId: undefined
49343
+ };
49344
+ } else {
49345
+ return false;
49346
+ }
49347
+ this.#queueRender();
49348
+ return true;
49349
+ }
48818
49350
  #handleMouseSelection(event) {
48819
49351
  if ((event.button & 3) !== 0 && event.kind !== "release")
48820
49352
  return;
@@ -59178,4 +59710,4 @@ switch (command) {
59178
59710
  }
59179
59711
  }
59180
59712
 
59181
- //# debugId=CF7D25563A652B9264756E2164756E21
59713
+ //# debugId=A04216FF3BF92ED864756E2164756E21