negotium 0.22.8 → 0.23.1

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.1";
6563
6563
 
6564
6564
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
6565
6565
  import { spawn as spawn2 } from "child_process";
@@ -41575,6 +41575,15 @@ function commandSuggestions(input) {
41575
41575
  const normalized = token.toLowerCase();
41576
41576
  return TERMINAL_COMMANDS.filter((command) => command.name.startsWith(normalized));
41577
41577
  }
41578
+ function isKnownTerminalCommand(text3) {
41579
+ if (!text3.startsWith("/"))
41580
+ return false;
41581
+ const firstLine = text3.split(`
41582
+ `, 1)[0] ?? "";
41583
+ const [token = ""] = firstLine.slice(1).trim().split(/\s+/);
41584
+ const normalized = token.toLowerCase();
41585
+ return TERMINAL_COMMANDS.some((command) => command.name === normalized);
41586
+ }
41578
41587
  function completeCommand(input, index) {
41579
41588
  const suggestions = commandSuggestions(input);
41580
41589
  if (suggestions.length === 0)
@@ -41929,7 +41938,14 @@ function createInitialState(userId) {
41929
41938
  decisionGraphOffset: { x: 0, y: 0 },
41930
41939
  decisionGraphSpacing: DEFAULT_DECISION_GRAPH_SPACING,
41931
41940
  askChoiceIndex: 0,
41932
- taskSidebarEnabled: true,
41941
+ taskPanelScrollOffset: 0,
41942
+ topicsPanelEnabled: true,
41943
+ topicsPanelScrollOffset: 0,
41944
+ sidePanelWidth: SIDE_PANEL_WIDTH_DEFAULT,
41945
+ terminalPaneOpen: false,
41946
+ terminalPaneFocused: false,
41947
+ terminalPaneWidth: null,
41948
+ topicsPanelSort: "activity",
41933
41949
  overlay: null,
41934
41950
  topicPickerRoot: false,
41935
41951
  vaultEntries: [],
@@ -41941,8 +41957,45 @@ function createInitialState(userId) {
41941
41957
  vaultRequestSubmitting: false
41942
41958
  };
41943
41959
  }
41944
- function toggleTaskSidebar(state) {
41945
- return { ...state, taskSidebarEnabled: !state.taskSidebarEnabled };
41960
+ function toggleTopicsPanel(state) {
41961
+ return { ...state, topicsPanelEnabled: !state.topicsPanelEnabled };
41962
+ }
41963
+ function cycleTopicsPanelSort(state) {
41964
+ return { ...state, topicsPanelSort: state.topicsPanelSort === "activity" ? "name" : "activity" };
41965
+ }
41966
+ function openTerminalPane(state) {
41967
+ return { ...state, terminalPaneOpen: true, terminalPaneFocused: true };
41968
+ }
41969
+ function closeTerminalPane(state) {
41970
+ return { ...state, terminalPaneOpen: false, terminalPaneFocused: false };
41971
+ }
41972
+ function focusTerminalPane(state, focused) {
41973
+ const next = focused && state.terminalPaneOpen;
41974
+ return next === state.terminalPaneFocused ? state : { ...state, terminalPaneFocused: next };
41975
+ }
41976
+ function setTerminalPaneWidth(state, width, maxWidth) {
41977
+ const next = Math.max(TERMINAL_PANE_WIDTH_MIN, Math.min(Math.round(width), maxWidth));
41978
+ return next === state.terminalPaneWidth ? state : { ...state, terminalPaneWidth: next };
41979
+ }
41980
+ function setSidePanelWidth(state, width, maxWidth) {
41981
+ const next = Math.min(SIDE_PANEL_WIDTH_MAX, Math.max(SIDE_PANEL_WIDTH_MIN, Math.min(Math.round(width), maxWidth)));
41982
+ return next === state.sidePanelWidth ? state : { ...state, sidePanelWidth: next };
41983
+ }
41984
+ function scrollTopicsPanel(state, delta, maxOffset) {
41985
+ const next = Math.min(maxOffset, Math.max(0, state.topicsPanelScrollOffset + delta));
41986
+ return next === state.topicsPanelScrollOffset ? state : { ...state, topicsPanelScrollOffset: next };
41987
+ }
41988
+ function scrollTaskPanel(state, delta, maxOffset) {
41989
+ const next = Math.min(maxOffset, Math.max(0, state.taskPanelScrollOffset + delta));
41990
+ return next === state.taskPanelScrollOffset ? state : { ...state, taskPanelScrollOffset: next };
41991
+ }
41992
+ function setTopicsPanelScrollOffset(state, offset, maxOffset) {
41993
+ const next = Math.min(maxOffset, Math.max(0, offset));
41994
+ return next === state.topicsPanelScrollOffset ? state : { ...state, topicsPanelScrollOffset: next };
41995
+ }
41996
+ function setTaskPanelScrollOffset(state, offset, maxOffset) {
41997
+ const next = Math.min(maxOffset, Math.max(0, offset));
41998
+ return next === state.taskPanelScrollOffset ? state : { ...state, taskPanelScrollOffset: next };
41946
41999
  }
41947
42000
  function activeTopic(state) {
41948
42001
  return state.topics.find((topic) => topic.id === state.activeTopicId) ?? null;
@@ -41959,6 +42012,16 @@ function setTopicUsage(state, usage) {
41959
42012
  function activeQuestion(state) {
41960
42013
  return activeMessages(state).slice().reverse().find((message2) => message2.kind === "ask_user_question" && message2.askUserQuestion && !message2.askUserQuestion.expired && !message2.askUserQuestion.selectedLabel) ?? null;
41961
42014
  }
42015
+ function blockedQuestionText(state, topicId) {
42016
+ const messages = state.messages[topicId] ?? [];
42017
+ for (let index = messages.length - 1;index >= 0; index -= 1) {
42018
+ const message2 = messages[index];
42019
+ if (message2.kind === "ask_user_question" && message2.askUserQuestion && !message2.askUserQuestion.expired && !message2.askUserQuestion.selectedLabel) {
42020
+ return message2.askUserQuestion.question;
42021
+ }
42022
+ }
42023
+ return;
42024
+ }
41962
42025
  function activeVaultRequest(state) {
41963
42026
  return activeMessages(state).find((message2) => message2.kind === "vault_secret_request" && message2.vaultSecretRequest?.status === "pending") ?? null;
41964
42027
  }
@@ -42062,12 +42125,43 @@ function visibleTopicPickerIds(state) {
42062
42125
  function visibleBackgroundSessions(state) {
42063
42126
  return state.backgroundSessions.filter((session) => topicPickerQueryMatches(session.title, state.topicFilter));
42064
42127
  }
42128
+ function topicRank(state, topic) {
42129
+ if (blockedQuestionText(state, topic.id) !== undefined)
42130
+ return 0;
42131
+ if (state.activity[topic.id]?.running)
42132
+ return 1;
42133
+ return 2;
42134
+ }
42135
+ function groupTopicsIntoUnits(entries) {
42136
+ const units = [];
42137
+ for (const entry of entries) {
42138
+ const last = units.at(-1);
42139
+ if (entry.topic.isSubagent && last)
42140
+ last.push(entry);
42141
+ else
42142
+ units.push([entry]);
42143
+ }
42144
+ return units;
42145
+ }
42146
+ function sortedTopicEntries(state, entries) {
42147
+ const units = groupTopicsIntoUnits(entries);
42148
+ if (state.topicsPanelSort === "name") {
42149
+ return units.slice().sort((a, b) => (a[0]?.topic.title ?? "").localeCompare(b[0]?.topic.title ?? "")).flat();
42150
+ }
42151
+ return units.map((unit, order) => ({
42152
+ unit,
42153
+ order,
42154
+ rank: Math.min(...unit.map(({ topic }) => topicRank(state, topic)))
42155
+ })).sort((a, b) => a.rank - b.rank || a.order - b.order).flatMap(({ unit }) => unit);
42156
+ }
42065
42157
  function topicPickerItems(state) {
42066
42158
  const visible = visibleTopicPickerIds(state);
42067
42159
  const indexedTopics = state.topics.map((topic, index) => ({ topic, index })).filter(({ topic }) => visible.has(topic.id));
42160
+ const managerEntries = sortedTopicEntries(state, indexedTopics.filter(({ topic }) => topic.kind === "manager"));
42161
+ const regularEntries = sortedTopicEntries(state, indexedTopics.filter(({ topic }) => topic.kind !== "manager"));
42068
42162
  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 })),
42163
+ ...managerEntries.map(({ topic, index }) => ({ kind: "topic", id: topic.id, index })),
42164
+ ...regularEntries.map(({ topic, index }) => ({ kind: "topic", id: topic.id, index })),
42071
42165
  ...visibleBackgroundSessions(state).map((session) => ({
42072
42166
  kind: "background",
42073
42167
  id: session.id
@@ -42359,6 +42453,22 @@ function failedToolText(text3) {
42359
42453
  return [title, ...mapped].join(`
42360
42454
  `);
42361
42455
  }
42456
+ function appendToolOutput(text3, content) {
42457
+ if (!content.trim())
42458
+ return text3;
42459
+ const [title = "Tool"] = text3.split(`
42460
+ `);
42461
+ if (TOOL_OUTPUT_EXCLUDED_PREFIXES.some((prefix) => title.startsWith(prefix)))
42462
+ return text3;
42463
+ if (text3.includes(`
42464
+ `))
42465
+ return text3;
42466
+ const lines = content.replace(/\r\n?/g, `
42467
+ `).replace(/\n+$/, "").split(`
42468
+ `);
42469
+ return [title, ...lines].join(`
42470
+ `);
42471
+ }
42362
42472
  function toolMessageId(status, toolUseId) {
42363
42473
  return `terminal-tool:${String(status.queryId ?? "query")}:${toolUseId}`;
42364
42474
  }
@@ -42458,14 +42568,17 @@ function applyAiStatus(state, topicId, raw, createdAt) {
42458
42568
  return state;
42459
42569
  const id = String(status.toolUseId ?? "");
42460
42570
  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);
42571
+ const content = String(status.content ?? "");
42572
+ const tools = current3.tools.map((tool) => tool.id === id ? { ...tool, output: content, status: failed ? "error" : "done" } : tool);
42462
42573
  const withActivity = setActivity(state, topicId, { ...liveCurrent, tools });
42463
42574
  const messageId = toolMessageId(status, id);
42464
42575
  const existing = (withActivity.messages[topicId] ?? []).find((message2) => message2.id === messageId);
42576
+ const withOutput = existing ? appendToolOutput(existing.text, content) : undefined;
42577
+ const nextText = failed && (!existing || withOutput === existing.text) ? failedToolText(existing?.text ?? "") : withOutput;
42465
42578
  return patchMessage(withActivity, topicId, messageId, {
42466
42579
  editedAt: createdAt ?? new Date().toISOString(),
42467
42580
  toolResult: failed ? "error" : "ok",
42468
- ...failed && existing ? { text: failedToolText(existing.text) } : {}
42581
+ ...nextText !== undefined ? { text: nextText } : {}
42469
42582
  });
42470
42583
  }
42471
42584
  if (kind === "tool_status") {
@@ -42498,11 +42611,18 @@ function applyRuntimeEvent(state, event) {
42498
42611
  }
42499
42612
  return state;
42500
42613
  }
42501
- var NO_TOPIC_PICKER_SELECTION = -1;
42614
+ var SIDE_PANEL_WIDTH_DEFAULT = 36, SIDE_PANEL_WIDTH_MIN = 22, SIDE_PANEL_WIDTH_MAX = 64, TERMINAL_PANE_WIDTH_MIN = 30, NO_TOPIC_PICKER_SELECTION = -1, TOOL_OUTPUT_EXCLUDED_PREFIXES;
42502
42615
  var init_state2 = __esm(async () => {
42503
42616
  init_decision_graph();
42504
42617
  init_subagent_graph();
42505
42618
  await init_src();
42619
+ TOOL_OUTPUT_EXCLUDED_PREFIXES = [
42620
+ "Edit \xB7 ",
42621
+ "Write \xB7 ",
42622
+ "Delete \xB7 ",
42623
+ "Ask session \xB7 ",
42624
+ "Tell session \xB7 "
42625
+ ];
42506
42626
  });
42507
42627
 
42508
42628
  // ../../adapters/terminal/src/context-usage.ts
@@ -43615,7 +43735,6 @@ function toolMessageLines(message2, width) {
43615
43735
  `);
43616
43736
  const failed = message2.toolResult === "error";
43617
43737
  const done = failed || Boolean(message2.editedAt);
43618
- const fileMutation = /^(?:Edit|Write|Delete) \u00B7 /.test(title);
43619
43738
  const wrapWidth = Math.max(4, width - 6);
43620
43739
  const marker = failed ? "!" : done ? "\u2713" : "\u25CF";
43621
43740
  const headerText = ` ${marker} ${title}`;
@@ -43631,14 +43750,8 @@ function toolMessageLines(message2, width) {
43631
43750
  { text: ")" }
43632
43751
  ] : [{ text: headerText, fg: headerColor }]
43633
43752
  });
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
43753
  const wrapped = details.flatMap((detail) => wrapText(detail, wrapWidth).map((text3) => ({ detail, text: text3 })));
43641
- const visible = wrapped.slice(0, FILE_PREVIEW_MAX_LINES);
43754
+ const visible = wrapped.slice(0, TOOL_PREVIEW_MAX_LINES);
43642
43755
  const hidden = wrapped.length - visible.length;
43643
43756
  return [
43644
43757
  header,
@@ -43743,50 +43856,269 @@ function taskLines(state, width) {
43743
43856
  const visibleTasks = tasks3.slice(0, INLINE_TASK_LIMIT);
43744
43857
  const hidden = tasks3.length - visibleTasks.length;
43745
43858
  return [
43746
- line(" \u25EB Tasks \xB7 Ctrl-T sidebar", { fg: theme.amber, bold: true }),
43859
+ line(" \u25EB Tasks \xB7 Ctrl-P sidebar", { fg: theme.amber, bold: true }),
43747
43860
  ...visibleTasks.flatMap((task) => wrapText(task.trimStart(), Math.max(4, width - 6)).map((text3) => line(` ${text3}`, { fg: theme.muted }))),
43748
43861
  ...hidden > 0 ? [line(` +${hidden} more`, { fg: theme.muted, dim: true })] : [],
43749
43862
  line("")
43750
43863
  ];
43751
43864
  }
43865
+ function maxSidePanelWidth(columns) {
43866
+ return Math.max(SIDE_PANEL_WIDTH_MIN, columns - SIDE_PANEL_GAP - CONVERSATION_MIN_WIDTH);
43867
+ }
43752
43868
  function terminalBodyLayout(state, width) {
43753
- const showTaskSidebar = width >= TASK_SIDEBAR_MIN_WIDTH && state.taskSidebarEnabled && !state.overlay && !state.creatingTopic && taskItems(state).length > 0;
43869
+ const terminalReserve = state.terminalPaneOpen ? TERMINAL_PANE_WIDTH_MIN + TERMINAL_PANE_GAP : 0;
43870
+ const wideEnough = width - terminalReserve >= SIDE_PANEL_MIN_WIDTH && state.topicsPanelEnabled && (!state.overlay || state.overlay === "background-session") && !state.creatingTopic;
43871
+ const showTaskPanel = wideEnough && taskItems(state).length > 0;
43872
+ const showTopicsPanel = wideEnough && (state.topics.length > 1 || visibleBackgroundSessions(state).length > 0);
43873
+ const showSide = showTaskPanel || showTopicsPanel;
43874
+ const maxSideWidth = maxSidePanelWidth(width) - terminalReserve;
43875
+ const sideWidth = showSide ? Math.min(SIDE_PANEL_WIDTH_MAX, Math.max(SIDE_PANEL_WIDTH_MIN, state.sidePanelWidth), maxSideWidth) : 0;
43876
+ const afterSide = showSide ? width - sideWidth - SIDE_PANEL_GAP : width;
43877
+ const maxTerminalWidth = afterSide - TERMINAL_PANE_GAP - CONVERSATION_MIN_WIDTH;
43878
+ const terminalWidth = state.terminalPaneOpen && maxTerminalWidth >= TERMINAL_PANE_WIDTH_MIN ? Math.min(maxTerminalWidth, Math.max(TERMINAL_PANE_WIDTH_MIN, state.terminalPaneWidth ?? Math.floor(afterSide * 0.32))) : 0;
43754
43879
  return {
43755
- conversationWidth: showTaskSidebar ? width - TASK_SIDEBAR_WIDTH - TASK_SIDEBAR_GAP : width,
43756
- taskWidth: showTaskSidebar ? TASK_SIDEBAR_WIDTH : 0,
43757
- showTaskSidebar
43758
- };
43880
+ conversationWidth: terminalWidth > 0 ? afterSide - terminalWidth - TERMINAL_PANE_GAP : afterSide,
43881
+ sideWidth,
43882
+ showTaskPanel,
43883
+ showTopicsPanel,
43884
+ terminalWidth,
43885
+ maxTerminalWidth: Math.max(TERMINAL_PANE_WIDTH_MIN, maxTerminalWidth)
43886
+ };
43887
+ }
43888
+ function maxTerminalPaneWidth(state, columns) {
43889
+ return terminalBodyLayout(state, Math.max(1, columns)).maxTerminalWidth;
43890
+ }
43891
+ function topicStatusIndicator(state, topic, animationFrame) {
43892
+ const blockedText = blockedQuestionText(state, topic.id);
43893
+ if (blockedText !== undefined)
43894
+ return { icon: "?", fg: theme.amber, blockedText };
43895
+ if (state.activity[topic.id]?.running) {
43896
+ return { icon: workingFrame(animationFrame), fg: theme.green };
43897
+ }
43898
+ return { icon: "\u25CB", fg: theme.muted };
43899
+ }
43900
+ function topicPanelRowLines(state, topic, topicIndex, animationFrame) {
43901
+ const selected = topic.id === state.activeTopicId;
43902
+ const hovered = !selected && topic.id === state.sidePanelHoverTopicId;
43903
+ const status = topicStatusIndicator(state, topic, animationFrame);
43904
+ const childPrefix = subagentTreePrefix(topic, state.topics);
43905
+ const bg2 = selected || hovered ? theme.selected : theme.canvas;
43906
+ const header = line(` ${selected ? "\u203A" : " "} ${childPrefix}${status.icon} ${topic.title}`, {
43907
+ fg: selected ? theme.text : status.fg,
43908
+ bg: bg2,
43909
+ bold: selected,
43910
+ pickerTarget: { topicIndex }
43911
+ });
43912
+ if (status.blockedText === undefined)
43913
+ return [header];
43914
+ return [
43915
+ header,
43916
+ line(` ${status.blockedText}`, {
43917
+ fg: theme.amber,
43918
+ bg: bg2,
43919
+ pickerTarget: { topicIndex }
43920
+ })
43921
+ ];
43759
43922
  }
43760
- function taskSidebarLines(state, width, height) {
43923
+ function topicPanelGroupLines(state, label, entries, animationFrame) {
43924
+ if (entries.length === 0)
43925
+ return [];
43926
+ return [
43927
+ line(` ${label}`, { fg: theme.cyan, dim: true, bold: true, bg: theme.canvas }),
43928
+ ...sortedTopicEntries(state, entries).flatMap(({ topic, topicIndex }) => topicPanelRowLines(state, topic, topicIndex, animationFrame))
43929
+ ];
43930
+ }
43931
+ function topicsPanelRows(state, animationFrame) {
43932
+ const indexed = state.topics.map((topic, topicIndex) => ({ topic, topicIndex }));
43933
+ const managerEntries = indexed.filter(({ topic }) => topic.kind === "manager");
43934
+ const regularEntries = indexed.filter(({ topic }) => topic.kind !== "manager");
43935
+ const topicRows = [
43936
+ ...topicPanelGroupLines(state, "manager", managerEntries, animationFrame),
43937
+ ...topicPanelGroupLines(state, "topics", regularEntries, animationFrame)
43938
+ ];
43939
+ const sessionRows = ["cron", "memory", "compact"].flatMap((kind) => {
43940
+ const matching = visibleBackgroundSessions(state).filter((session) => session.kind === kind);
43941
+ if (matching.length === 0)
43942
+ return [];
43943
+ return [
43944
+ line(` ${kind}`, { fg: theme.cyan, dim: true, bold: true, bg: theme.canvas }),
43945
+ ...matching.map((session) => {
43946
+ const hovered = session.id === state.sidePanelHoverBackgroundSessionId;
43947
+ return line(` ${session.active ? workingFrame(animationFrame) : "\u25CB"} ${session.title}`, {
43948
+ fg: hovered ? theme.text : session.active ? theme.green : theme.muted,
43949
+ bg: hovered ? theme.selected : theme.canvas,
43950
+ pickerTarget: { backgroundSessionId: session.id }
43951
+ });
43952
+ })
43953
+ ];
43954
+ });
43955
+ return [...topicRows, ...sessionRows];
43956
+ }
43957
+ function taskPanelRows(state, width) {
43761
43958
  const items = taskItems(state);
43762
- const groups = items.map((task) => {
43959
+ return items.flatMap((task) => {
43763
43960
  const completed = /^\s*(?:\[x\]|\u2612|\u2713|\u2705)/i.test(task);
43764
- return wrapText(task.trimStart(), Math.max(4, width - 4)).map((text3) => line(` ${text3}`, {
43961
+ return wrapText(task.trimStart(), Math.max(4, width - 2)).map((text3) => line(` ${text3}`, {
43765
43962
  fg: completed ? theme.muted : theme.text,
43766
- dim: completed
43963
+ dim: completed,
43964
+ bg: theme.canvas
43767
43965
  }));
43768
43966
  });
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;
43967
+ }
43968
+ function panelHeaderHeight(sectionHeight) {
43969
+ return Math.min(2, sectionHeight);
43970
+ }
43971
+ function panelContentHeight(sectionHeight) {
43972
+ return Math.max(0, sectionHeight - panelHeaderHeight(sectionHeight));
43973
+ }
43974
+ function scrollbarThumb(totalRows, viewportRows, offset) {
43975
+ if (viewportRows <= 0 || totalRows <= 0)
43976
+ return { top: 0, len: 0 };
43977
+ const len = Math.max(1, Math.min(viewportRows, Math.round(viewportRows * viewportRows / totalRows)));
43978
+ const maxThumbTop = Math.max(0, viewportRows - len);
43979
+ const maxOffset = Math.max(1, totalRows - viewportRows);
43980
+ const top = maxThumbTop === 0 ? 0 : Math.round(Math.min(offset, maxOffset) * maxThumbTop / maxOffset);
43981
+ return { top, len };
43982
+ }
43983
+ function scrollOffsetFromDragRow(totalRows, viewportRows, trackRow, grabRowOffset) {
43984
+ if (viewportRows <= 0 || totalRows <= 0)
43985
+ return 0;
43986
+ const { len } = scrollbarThumb(totalRows, viewportRows, 0);
43987
+ const maxThumbTop = Math.max(0, viewportRows - len);
43988
+ if (maxThumbTop === 0)
43989
+ return 0;
43990
+ const clampedRow = Math.max(0, Math.min(trackRow, viewportRows - 1));
43991
+ const desiredTop = Math.min(maxThumbTop, Math.max(0, clampedRow - grabRowOffset));
43992
+ const maxOffset = Math.max(0, totalRows - viewportRows);
43993
+ return Math.round(desiredTop * maxOffset / maxThumbTop);
43994
+ }
43995
+ function scrollbarCell(totalRows, viewportRows, offset, rowIndex, accent) {
43996
+ const thumb = scrollbarThumb(totalRows, viewportRows, offset);
43997
+ const isThumb = rowIndex >= thumb.top && rowIndex < thumb.top + thumb.len;
43998
+ return paint(isThumb ? "\u2590" : "\u2595", { fg: isThumb ? accent : theme.muted, bg: theme.canvas });
43999
+ }
44000
+ function renderPanelSection(label, rows, width, height, scrollOffset, accent) {
44001
+ if (height <= 0 || width <= 0)
44002
+ return { lines: [], rowTargets: [] };
44003
+ const headerHeight = panelHeaderHeight(height);
44004
+ const contentHeight = panelContentHeight(height);
44005
+ const overflow = rows.length > contentHeight;
44006
+ const textWidth = overflow ? Math.max(1, width - 1) : width;
44007
+ const clampedOffset = Math.max(0, Math.min(scrollOffset, Math.max(0, rows.length - contentHeight)));
44008
+ const visible = rows.slice(clampedOffset, clampedOffset + contentHeight);
44009
+ const lines = [];
44010
+ if (headerHeight >= 1)
44011
+ lines.push(paint("\u2500".repeat(width), { fg: theme.border, bg: theme.canvas }));
44012
+ if (headerHeight >= 2) {
44013
+ lines.push(paint(fit(` ${label}`, width), { fg: accent, bg: theme.canvas, bold: true }));
44014
+ }
44015
+ const rowTargets = [];
44016
+ for (let index = 0;index < contentHeight; index += 1) {
44017
+ const item = visible[index];
44018
+ const text3 = paint(fit(item?.text ?? "", textWidth), {
44019
+ fg: item?.fg ?? theme.text,
44020
+ bg: item?.bg ?? theme.canvas,
44021
+ bold: item?.bold,
44022
+ dim: item?.dim
44023
+ });
44024
+ const scrollCell = overflow ? scrollbarCell(rows.length, contentHeight, clampedOffset, index, accent) : "";
44025
+ lines.push(`${text3}${scrollCell}`);
44026
+ if (item?.pickerTarget)
44027
+ rowTargets.push({ y: headerHeight + index, pickerTarget: item.pickerTarget });
44028
+ }
44029
+ return {
44030
+ lines,
44031
+ rowTargets,
44032
+ track: overflow ? { x: width - 1, viewportRows: contentHeight, rowCount: rows.length } : undefined
44033
+ };
44034
+ }
44035
+ function sidePanelSectionHeights(layout, bodyHeight) {
44036
+ if (layout.showTopicsPanel && layout.showTaskPanel) {
44037
+ const topicsHeight = Math.max(3, Math.floor(bodyHeight / 2));
44038
+ return { topicsHeight, taskHeight: Math.max(0, bodyHeight - topicsHeight) };
44039
+ }
44040
+ if (layout.showTopicsPanel)
44041
+ return { topicsHeight: bodyHeight, taskHeight: 0 };
44042
+ if (layout.showTaskPanel)
44043
+ return { topicsHeight: 0, taskHeight: bodyHeight };
44044
+ return { topicsHeight: 0, taskHeight: 0 };
44045
+ }
44046
+ function renderSideColumn(state, layout, bodyHeight, animationFrame) {
44047
+ const { showTaskPanel, showTopicsPanel, sideWidth } = layout;
44048
+ if (!showTaskPanel && !showTopicsPanel)
44049
+ return { lines: [], targets: [], tracks: [] };
44050
+ const { topicsHeight, taskHeight } = sidePanelSectionHeights(layout, bodyHeight);
44051
+ const sections = [];
44052
+ if (showTopicsPanel) {
44053
+ sections.push({
44054
+ panel: "topics",
44055
+ label: state.topicsPanelSort === "activity" ? "ACTIVITY \xB7 name" : "activity \xB7 NAME",
44056
+ rows: topicsPanelRows(state, animationFrame),
44057
+ accent: theme.accent,
44058
+ height: topicsHeight,
44059
+ offset: 0,
44060
+ scrollOffset: state.topicsPanelScrollOffset
44061
+ });
44062
+ }
44063
+ if (showTaskPanel) {
44064
+ sections.push({
44065
+ panel: "tasks",
44066
+ label: "tasks",
44067
+ rows: taskPanelRows(state, sideWidth),
44068
+ accent: theme.taskBorder,
44069
+ height: taskHeight,
44070
+ offset: showTopicsPanel ? topicsHeight : 0,
44071
+ scrollOffset: state.taskPanelScrollOffset
44072
+ });
44073
+ }
44074
+ const lines = [];
44075
+ const targets = [];
44076
+ const tracks = [];
44077
+ for (const section of sections) {
44078
+ const result = renderPanelSection(section.label, section.rows, sideWidth, section.height, section.scrollOffset, section.accent);
44079
+ lines.push(...result.lines);
44080
+ for (const target of result.rowTargets) {
44081
+ targets.push({ y: section.offset + target.y, pickerTarget: target.pickerTarget });
44082
+ }
44083
+ if (result.track) {
44084
+ tracks.push({
44085
+ panel: section.panel,
44086
+ x: result.track.x,
44087
+ yStart: section.offset + panelHeaderHeight(section.height),
44088
+ viewportRows: result.track.viewportRows,
44089
+ rowCount: result.track.rowCount
44090
+ });
43784
44091
  }
43785
- rows.push(...group);
43786
- shown += 1;
43787
44092
  }
43788
- rows.push(line(` +${items.length - shown} more`, { fg: theme.muted, dim: true }));
43789
- return rows;
44093
+ while (lines.length < bodyHeight)
44094
+ lines.push(paint(" ".repeat(sideWidth), { bg: theme.canvas }));
44095
+ return { lines: lines.slice(0, bodyHeight), targets, tracks };
44096
+ }
44097
+ function maxTopicsPanelScrollOffset(state, columns, rows) {
44098
+ const width = Math.max(1, columns);
44099
+ const height = Math.max(14, rows);
44100
+ if (state.overlay)
44101
+ return 0;
44102
+ const layout = terminalBodyLayout(state, width);
44103
+ if (!layout.showTopicsPanel)
44104
+ return 0;
44105
+ const bodyHeight = Math.max(3, height - footerLines(state, width).length - decisionPane(state, width).length - vaultRequestPane(state, width).length - composerPane(state, layout.conversationWidth).lines.length);
44106
+ const { topicsHeight } = sidePanelSectionHeights(layout, bodyHeight);
44107
+ const viewport = panelContentHeight(topicsHeight);
44108
+ return Math.max(0, topicsPanelRows(state, 0).length - viewport);
44109
+ }
44110
+ function maxTaskPanelScrollOffset(state, columns, rows) {
44111
+ const width = Math.max(1, columns);
44112
+ const height = Math.max(14, rows);
44113
+ if (state.overlay)
44114
+ return 0;
44115
+ const layout = terminalBodyLayout(state, width);
44116
+ if (!layout.showTaskPanel)
44117
+ return 0;
44118
+ const bodyHeight = Math.max(3, height - footerLines(state, width).length - decisionPane(state, width).length - vaultRequestPane(state, width).length - composerPane(state, layout.conversationWidth).lines.length);
44119
+ const { taskHeight } = sidePanelSectionHeights(layout, bodyHeight);
44120
+ const viewport = panelContentHeight(taskHeight);
44121
+ return Math.max(0, taskPanelRows(state, layout.sideWidth).length - viewport);
43790
44122
  }
43791
44123
  function helpLines() {
43792
44124
  return [
@@ -43796,7 +44128,10 @@ function helpLines() {
43796
44128
  line(" \u2190 \u2192 move \xB7 Ctrl/Alt-\u2190 \u2192 move by word \xB7 \u2191 \u2193 history"),
43797
44129
  line(" Alt-Backspace delete word \xB7 Ctrl-U clear before cursor"),
43798
44130
  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"),
44131
+ line(" Mouse wheel / PgUp/PgDn scroll \xB7 Ctrl-E load older"),
44132
+ line(" Ctrl-P topics + tasks panel \xB7 click/hover a row to switch \xB7 drag scrollbar"),
44133
+ line(" Ctrl-R sort topics panel (activity / name) \xB7 drag a \u2502 divider to resize"),
44134
+ line(" Ctrl-T open/close a shell on the right (closing ends it) \xB7 click it to type"),
43800
44135
  line(" Ctrl-O topics \xB7 Ctrl-G subagents \xB7 Ctrl-D decisions \xB7 Ctrl-X abort"),
43801
44136
  line(" In topics: type to filter \xB7 Ctrl-N new \xB7 Ctrl-D delete"),
43802
44137
  line(" Esc/Ctrl-C stop active turn \xB7 Ctrl-C twice when idle to quit"),
@@ -43948,8 +44283,8 @@ function topicOverlayCursor(state, width, bodyHeight) {
43948
44283
  function topicOverlayLines(state, width, height, animationFrame = 0) {
43949
44284
  const visibleIds = visibleTopicPickerIds(state);
43950
44285
  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");
44286
+ const managerTopics = sortedTopicEntries(state, indexedTopics.filter(({ topic }) => topic.kind === "manager"));
44287
+ const regularTopics = sortedTopicEntries(state, indexedTopics.filter(({ topic }) => topic.kind !== "manager"));
43953
44288
  const entries = [];
43954
44289
  for (const [label, topics] of [
43955
44290
  ["Manager", managerTopics],
@@ -43997,30 +44332,48 @@ function topicOverlayLines(state, width, height, animationFrame = 0) {
43997
44332
  query2.length > 0 ? line(`${TOPIC_FILTER_LABEL}${safeText(query2)}`, { fg: theme.cyan, bold: true }) : line(""),
43998
44333
  ...entries.length === 0 ? [
43999
44334
  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) => {
44335
+ ] : entries.slice(start, start + visibleCount).flatMap((entry) => {
44001
44336
  if (entry.kind === "heading") {
44002
- return line(` ${entry.label}`, { fg: theme.cyan, bold: true });
44337
+ return [line(` ${entry.label}`, { fg: theme.cyan, bold: true })];
44003
44338
  }
44004
44339
  if (entry.kind === "separator") {
44005
- return line(` ${"\u2500".repeat(Math.max(1, width - 4))}`, { fg: theme.border });
44340
+ return [line(` ${"\u2500".repeat(Math.max(1, width - 4))}`, { fg: theme.border })];
44006
44341
  }
44007
44342
  if (entry.kind === "background") {
44008
44343
  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
- });
44344
+ return [
44345
+ line(` ${selected2 ? "\u203A" : " "} ${entry.active ? workingFrame(animationFrame) : "\u25CB"} ${entry.label} \xB7 ${entry.status}`, {
44346
+ fg: selected2 ? theme.text : entry.active ? theme.green : theme.muted,
44347
+ bg: selected2 ? theme.selected : theme.canvas,
44348
+ bold: selected2,
44349
+ pickerTarget: { backgroundSessionId: entry.sessionId }
44350
+ })
44351
+ ];
44014
44352
  }
44015
44353
  const { topic, topicIndex } = entry;
44016
44354
  const selected = topicIndex === state.topicPickerIndex;
44017
- const running = state.activity[topic.id]?.running;
44355
+ const status = topicStatusIndicator(state, topic, animationFrame);
44018
44356
  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
44357
+ const bg2 = selected ? theme.selected : theme.canvas;
44358
+ const header = line(` ${selected ? "\u203A" : " "} ${childPrefix}${status.icon} ${topic.title} \xB7 ${topic.agent ?? "no agent"} \xB7 ${effectiveTopicModel(topic)} \xB7 ${effectiveTopicEffort(topic)}`, {
44359
+ fg: selected ? theme.text : status.fg,
44360
+ bg: bg2,
44361
+ bold: selected,
44362
+ pickerTarget: { topicIndex }
44023
44363
  });
44364
+ const running = state.activity[topic.id]?.running;
44365
+ const subtitle = status.blockedText ?? (running ? activityDetail(state.activity[topic.id]?.status) : undefined);
44366
+ if (!subtitle)
44367
+ return [header];
44368
+ return [
44369
+ header,
44370
+ line(` ${subtitle}`, {
44371
+ fg: status.blockedText !== undefined ? theme.amber : theme.muted,
44372
+ dim: status.blockedText === undefined,
44373
+ bg: bg2,
44374
+ pickerTarget: { topicIndex }
44375
+ })
44376
+ ];
44024
44377
  })
44025
44378
  ];
44026
44379
  }
@@ -44366,6 +44719,16 @@ function conversationViewport(lineCount, height, requestedOffset) {
44366
44719
  offset: Math.min(maxOffset, Math.max(0, requestedOffset))
44367
44720
  };
44368
44721
  }
44722
+ function popupPaneWidth(width) {
44723
+ return Math.max(0, Math.min(POPUP_PANE_MAX_WIDTH, width - 4));
44724
+ }
44725
+ function centerPaneRows(rows, boxWidth, totalWidth) {
44726
+ const left = Math.max(0, Math.floor((totalWidth - boxWidth) / 2));
44727
+ const right = Math.max(0, totalWidth - boxWidth - left);
44728
+ const leftPad = left > 0 ? paint(" ".repeat(left), { bg: theme.canvas }) : "";
44729
+ const rightPad = right > 0 ? paint(" ".repeat(right), { bg: theme.canvas }) : "";
44730
+ return rows.map((row) => `${leftPad}${row}${rightPad}`);
44731
+ }
44369
44732
  function decisionPane(state, width) {
44370
44733
  if (state.overlay)
44371
44734
  return [];
@@ -44376,6 +44739,7 @@ function decisionPane(state, width) {
44376
44739
  if (!ask || !question?.choices.length)
44377
44740
  return [];
44378
44741
  const selected = Math.min(state.askChoiceIndex, question.choices.length - 1);
44742
+ const boxWidth = popupPaneWidth(width);
44379
44743
  const content = [
44380
44744
  line(` ${question.question}`, { bold: true }),
44381
44745
  ...question.choices.map((choice, index) => line(` ${index === selected ? "\u25CF" : "\u25CB"} ${choice.label}${choice.description ? ` \u2014 ${choice.description}` : ""}`, {
@@ -44384,7 +44748,8 @@ function decisionPane(state, width) {
44384
44748
  bold: index === selected
44385
44749
  }))
44386
44750
  ];
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 });
44751
+ 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 });
44752
+ return centerPaneRows(rows, boxWidth, width);
44388
44753
  }
44389
44754
  function vaultRequestPane(state, width) {
44390
44755
  if (state.overlay)
@@ -44393,15 +44758,17 @@ function vaultRequestPane(state, width) {
44393
44758
  if (!request)
44394
44759
  return [];
44395
44760
  const declining = state.vaultRequestMode === "confirm-decline";
44761
+ const boxWidth = popupPaneWidth(width);
44396
44762
  const content = [
44397
44763
  line(` ${request.key}`, { bold: true }),
44398
- ...request.description ? wrapText(request.description, Math.max(4, width - 6)).map((text3) => line(` ${text3}`, { fg: theme.muted })) : [],
44764
+ ...request.description ? wrapText(request.description, Math.max(4, boxWidth - 6)).map((text3) => line(` ${text3}`, { fg: theme.muted })) : [],
44399
44765
  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
44766
  fg: theme.subtle
44401
44767
  }),
44402
44768
  ...state.vaultRequestNotice ? [line(` ! ${state.vaultRequestNotice}`, { fg: theme.amber })] : []
44403
44769
  ];
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 });
44770
+ 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 });
44771
+ return centerPaneRows(rows, boxWidth, width);
44405
44772
  }
44406
44773
  function wrapLineWithCursor(value, codePointColumn, width) {
44407
44774
  const chars = Array.from(safeText(value));
@@ -44475,8 +44842,25 @@ function inputVisualLines(state, width) {
44475
44842
  }
44476
44843
  return { lines: result, cursorLine, cursorColumn };
44477
44844
  }
44845
+ function shiftLines(lines, marginWidth) {
44846
+ if (marginWidth <= 0)
44847
+ return lines;
44848
+ const margin = paint(" ".repeat(marginWidth), { bg: theme.canvas });
44849
+ return lines.map((row) => `${margin}${row}`);
44850
+ }
44851
+ function shiftComposerPane(pane, marginWidth) {
44852
+ if (marginWidth <= 0)
44853
+ return pane;
44854
+ return {
44855
+ lines: shiftLines(pane.lines, marginWidth),
44856
+ cursor: { x: pane.cursor.x + marginWidth, y: pane.cursor.y }
44857
+ };
44858
+ }
44859
+ function shortcutHint(width) {
44860
+ return SHORTCUT_HINTS.find((hint) => displayWidth(hint) + 2 <= width) ?? SHORTCUT_HINTS[2];
44861
+ }
44478
44862
  function composerPane(state, width) {
44479
- 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";
44863
+ 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` : shortcutHint(width);
44480
44864
  const visual = inputVisualLines(state, width);
44481
44865
  let inputStart = Math.max(0, visual.lines.length - 5);
44482
44866
  if (visual.cursorLine < inputStart)
@@ -44715,43 +45099,173 @@ function renderBody(lines, width, height) {
44715
45099
  function renderApp(state, columns, rows, animationFrame = 0, nowMs = terminalNowMs()) {
44716
45100
  return renderAppFrame(state, columns, rows, animationFrame, nowMs).frame;
44717
45101
  }
44718
- function renderAppFrame(state, columns, rows, animationFrame = 0, nowMs = terminalNowMs()) {
45102
+ function renderAppFrame(state, columns, rows, animationFrame = 0, nowMs = terminalNowMs(), extras = {}) {
44719
45103
  const width = Math.max(1, columns);
44720
45104
  const height = Math.max(14, rows);
44721
- const footer = footerLines(state, width);
44722
- const decision = [...decisionPane(state, width), ...vaultRequestPane(state, width)];
45105
+ const layout = terminalBodyLayout(state, width);
45106
+ const conversationXStart = layout.sideWidth > 0 ? layout.sideWidth + SIDE_PANEL_GAP + 1 : 1;
45107
+ const footer = shiftLines(footerLines(state, layout.conversationWidth), conversationXStart - 1);
45108
+ const decision = shiftLines([
45109
+ ...decisionPane(state, layout.conversationWidth),
45110
+ ...vaultRequestPane(state, layout.conversationWidth)
45111
+ ], conversationXStart - 1);
44723
45112
  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);
45113
+ const composer = hideComposer ? { lines: [], cursor: null } : shiftComposerPane(composerPane(state, layout.conversationWidth), conversationXStart - 1);
44725
45114
  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);
45115
+ const conversation = conversationLines(state, layout.conversationWidth, bodyHeight, animationFrame, nowMs, !layout.showTaskPanel);
44728
45116
  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] ?? ""}`);
45117
+ const sideColumn = layout.showTaskPanel || layout.showTopicsPanel ? renderSideColumn(state, layout, bodyHeight, animationFrame) : null;
45118
+ const body = sideColumn?.lines.length ? (() => {
45119
+ const gap = paint("\u2502".repeat(SIDE_PANEL_GAP), { fg: theme.border, bg: theme.canvas });
45120
+ return sideColumn.lines.map((row, index) => `${row}${gap}${conversationBody[index] ?? ""}`);
44735
45121
  })() : conversationBody;
44736
45122
  const codeCopyTargets = conversation.flatMap((item, index) => {
44737
45123
  if (item.codeCopy === undefined || index >= bodyHeight)
44738
45124
  return [];
44739
45125
  const trimmed = item.text.trimEnd();
44740
45126
  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);
45127
+ const xStart = conversationXStart + displayWidth(trimmed.slice(0, leading));
45128
+ const xEnd = Math.min(conversationXStart - 1 + displayWidth(trimmed), width);
44743
45129
  return xEnd >= xStart ? [{ xStart, xEnd, y: index + 1, text: item.codeCopy }] : [];
44744
45130
  });
45131
+ const overlayTargets = conversation.flatMap((item, index) => {
45132
+ if (!item.pickerTarget || index >= bodyHeight)
45133
+ return [];
45134
+ return [{ xStart: 1, xEnd: layout.conversationWidth, y: index + 1, ...item.pickerTarget }];
45135
+ });
45136
+ const sidePanelTargets = (sideColumn?.targets ?? []).map((target) => ({
45137
+ xStart: 1,
45138
+ xEnd: layout.sideWidth,
45139
+ y: target.y + 1,
45140
+ ...target.pickerTarget
45141
+ }));
45142
+ const topicPickerTargets = [...overlayTargets, ...sidePanelTargets];
45143
+ const sidePanelTracks = (sideColumn?.tracks ?? []).map((track) => ({
45144
+ ...track,
45145
+ x: 1 + track.x,
45146
+ yStart: track.yStart + 1
45147
+ }));
45148
+ const sideColumnBounds = sideColumn ? (() => {
45149
+ const { topicsHeight, taskHeight } = sidePanelSectionHeights(layout, bodyHeight);
45150
+ const sections = [];
45151
+ let offset = 0;
45152
+ if (layout.showTopicsPanel) {
45153
+ sections.push({ panel: "topics", yStart: offset + 1, yEnd: offset + topicsHeight });
45154
+ offset += topicsHeight;
45155
+ }
45156
+ if (layout.showTaskPanel) {
45157
+ sections.push({ panel: "tasks", yStart: offset + 1, yEnd: offset + taskHeight });
45158
+ }
45159
+ return { xStart: 1, xEnd: layout.sideWidth, sections };
45160
+ })() : null;
45161
+ const sidePanelResizeHandle = sideColumn ? {
45162
+ xStart: Math.max(1, layout.sideWidth),
45163
+ xEnd: layout.sideWidth + SIDE_PANEL_GAP,
45164
+ yStart: 1,
45165
+ yEnd: bodyHeight
45166
+ } : null;
44745
45167
  const cursorY = composer.cursor ? body.length + decision.length + composer.cursor.y : Number.POSITIVE_INFINITY;
44746
45168
  const overlayCursor = topicOverlayCursor(state, layout.conversationWidth, body.length);
44747
- const cursor = composer.cursor && cursorY <= height ? { x: Math.min(composer.cursor.x, width), y: cursorY } : overlayCursor && overlayCursor.y <= height ? overlayCursor : null;
45169
+ const leftRows = [...body, ...decision, ...composer.lines, ...footer].slice(0, height);
45170
+ let terminalPane = null;
45171
+ let frameRows = leftRows;
45172
+ if (layout.terminalWidth > 0) {
45173
+ const leftWidth = width - layout.terminalWidth - TERMINAL_PANE_GAP;
45174
+ const shellRows = shellPaneRows(extras.shell ?? null, layout.terminalWidth, leftRows.length);
45175
+ const divider = paint("\u2502".repeat(TERMINAL_PANE_GAP), {
45176
+ fg: state.terminalPaneFocused ? theme.borderActive : theme.border,
45177
+ bg: theme.canvas
45178
+ });
45179
+ frameRows = leftRows.map((row, index) => `${padToWidth(row, leftWidth)}${divider}${shellRows[index] ?? ""}`);
45180
+ terminalPane = {
45181
+ xStart: leftWidth + TERMINAL_PANE_GAP + 1,
45182
+ xEnd: width,
45183
+ yStart: 1,
45184
+ yEnd: leftRows.length,
45185
+ dividerX: leftWidth + 1,
45186
+ cols: layout.terminalWidth,
45187
+ rows: leftRows.length
45188
+ };
45189
+ }
45190
+ const shellCursor = terminalPane && state.terminalPaneFocused && extras.shell?.cursor ? {
45191
+ x: terminalPane.xStart + extras.shell.cursor.x,
45192
+ y: terminalPane.yStart + extras.shell.cursor.y
45193
+ } : null;
45194
+ const cursor = state.terminalPaneFocused && terminalPane ? shellCursor : composer.cursor && cursorY <= height ? { x: Math.min(composer.cursor.x, width), y: cursorY } : overlayCursor && overlayCursor.y <= height ? overlayCursor : null;
44748
45195
  return {
44749
- frame: linkifyUrls([...body, ...decision, ...composer.lines, ...footer].slice(0, height).join(`
45196
+ frame: linkifyUrls(frameRows.join(`
44750
45197
  `)),
44751
45198
  cursor,
44752
- codeCopyTargets
45199
+ codeCopyTargets,
45200
+ topicPickerTargets,
45201
+ sidePanelTracks,
45202
+ sideColumnBounds,
45203
+ sidePanelResizeHandle,
45204
+ conversationBounds: {
45205
+ xStart: conversationXStart,
45206
+ xEnd: conversationXStart + layout.conversationWidth - 1
45207
+ },
45208
+ terminalPane
44753
45209
  };
44754
45210
  }
45211
+ function padToWidth(row, width) {
45212
+ const visible = displayWidth(stripAnsi(row));
45213
+ return visible >= width ? row : `${row}${paint(" ".repeat(width - visible), { bg: theme.canvas })}`;
45214
+ }
45215
+ function shellColorCode(color, background, fallback) {
45216
+ if (color.kind === "rgb")
45217
+ return background ? bg(color.rgb) : fg(color.rgb);
45218
+ if (color.kind === "palette") {
45219
+ const index = color.index;
45220
+ if (index < 8)
45221
+ return `${ESC}${(background ? 40 : 30) + index}m`;
45222
+ if (index < 16)
45223
+ return `${ESC}${(background ? 100 : 90) + index - 8}m`;
45224
+ return `${ESC}${background ? 48 : 38};5;${index}m`;
45225
+ }
45226
+ return background ? bg(fallback) : fg(fallback);
45227
+ }
45228
+ function paintShellRun(run) {
45229
+ if (colorDepth === "none")
45230
+ return run.text;
45231
+ const fgColor = run.inverse ? run.bg : run.fg;
45232
+ const bgColor = run.inverse ? run.fg : run.bg;
45233
+ const fgFallback = run.inverse ? theme.canvas : theme.text;
45234
+ const bgFallback = run.inverse ? theme.text : theme.canvas;
45235
+ return `${shellColorCode(fgColor, false, fgFallback)}${shellColorCode(bgColor, true, bgFallback)}${run.bold ? `${ESC}1m` : ""}${run.dim ? `${ESC}2m` : ""}${run.italic ? `${ESC}3m` : ""}${run.underline ? `${ESC}4m` : ""}${run.text}${RESET}`;
45236
+ }
45237
+ function shellPaneRows(snapshot, width, height) {
45238
+ const rows = [];
45239
+ for (let index = 0;index < height; index += 1) {
45240
+ const runs = snapshot?.lines[index];
45241
+ if (!runs) {
45242
+ const text3 = !snapshot && index === 0 ? fit(" starting shell\u2026", width) : " ".repeat(width);
45243
+ rows.push(paint(text3, { fg: theme.muted, bg: theme.canvas }));
45244
+ continue;
45245
+ }
45246
+ let used = 0;
45247
+ let row = "";
45248
+ for (const run of runs) {
45249
+ if (used >= width)
45250
+ break;
45251
+ if (used + run.width > width) {
45252
+ row += paintShellRun({
45253
+ ...run,
45254
+ text: sliceWidth(run.text, width - used),
45255
+ width: width - used
45256
+ });
45257
+ used = width;
45258
+ break;
45259
+ }
45260
+ row += paintShellRun(run);
45261
+ used += run.width;
45262
+ }
45263
+ if (used < width)
45264
+ row += paint(" ".repeat(width - used), { bg: theme.canvas });
45265
+ rows.push(row);
45266
+ }
45267
+ return rows;
45268
+ }
44755
45269
  function maxConversationScrollOffset(state, columns, rows) {
44756
45270
  const width = Math.max(1, columns);
44757
45271
  const height = Math.max(14, rows);
@@ -44761,9 +45275,9 @@ function maxConversationScrollOffset(state, columns, rows) {
44761
45275
  }
44762
45276
  if (state.overlay)
44763
45277
  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
45278
  const layout = terminalBodyLayout(state, width);
44766
- const lineCount = conversationContentLines(state, layout.conversationWidth, 0, terminalNowMs(), !layout.showTaskSidebar).length;
45279
+ const bodyHeight = Math.max(3, height - footerLines(state, width).length - decisionPane(state, width).length - vaultRequestPane(state, width).length - composerPane(state, layout.conversationWidth).lines.length);
45280
+ const lineCount = conversationContentLines(state, layout.conversationWidth, 0, terminalNowMs(), !layout.showTaskPanel).length;
44767
45281
  return conversationViewport(lineCount, bodyHeight, state.scrollOffset).maxOffset;
44768
45282
  }
44769
45283
  function preserveConversationScrollAnchor(previous, next, columns, rows) {
@@ -44801,7 +45315,7 @@ var theme, WORKING_FRAMES, CODE_COPY_MARKER = "\u29C9", WORKING_FRAME_INTERVAL_M
44801
45315
  default:
44802
45316
  return `${ESC}48;2;${rgb[0]};${rgb[1]};${rgb[2]}m`;
44803
45317
  }
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;
45318
+ }, 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_GAP = 1, CONVERSATION_MIN_WIDTH = 60, TERMINAL_PANE_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, SHORTCUT_HINTS, NOTICE_STYLE;
44805
45319
  var init_render = __esm(async () => {
44806
45320
  init_color_depth();
44807
45321
  init_commands();
@@ -44842,6 +45356,11 @@ var init_render = __esm(async () => {
44842
45356
  NO_ROW_START = new Set(Array.from(".,;:!?)]}>\xBB\u201D\u2019\u3001\u3002\uFF0C\uFF0E\uFF01\uFF1F\uFF1A\uFF1B\uFF09\uFF3D\uFF5D\u3009\u300B\u300D\u300F\u3011\u3015\u30FB\u2026\u30FC"));
44843
45357
  NO_ROW_END = new Set(Array.from("([{<\xAB\u201C\u2018\uFF08\uFF3B\uFF5B\u3008\u300A\u300C\u300E\u3010\u3014"));
44844
45358
  messageLayoutCache = new WeakMap;
45359
+ SHORTCUT_HINTS = [
45360
+ "Ctrl-O topics \xB7 Ctrl-P panel \xB7 Ctrl-T terminal \xB7 Ctrl-G subagents \xB7 Ctrl-D decisions",
45361
+ "Ctrl-O topics \xB7 Ctrl-P panel \xB7 Ctrl-T term \xB7 Ctrl-G agents \xB7 Ctrl-D decisions",
45362
+ "^O topics \xB7 ^P panel \xB7 ^T term \xB7 ^G agents \xB7 ^D decisions"
45363
+ ];
44845
45364
  NOTICE_STYLE = {
44846
45365
  info: { glyph: "\xB7", fg: theme.muted },
44847
45366
  success: { glyph: "\u2713", fg: theme.green },
@@ -45059,6 +45578,7 @@ function consumeMouseInput(raw) {
45059
45578
  let scrollDelta = 0;
45060
45579
  let horizontalScrollDelta = 0;
45061
45580
  const events = [];
45581
+ const wheelEvents = [];
45062
45582
  const input = raw.replace(SGR_MOUSE_PATTERN, (_sequence, rawButton, rawX, rawY, suffix) => {
45063
45583
  const button = Number.parseInt(rawButton, 10);
45064
45584
  if (Number.isFinite(button) && (button & 64) !== 0) {
@@ -45068,6 +45588,10 @@ function consumeMouseInput(raw) {
45068
45588
  horizontalScrollDelta += delta;
45069
45589
  else
45070
45590
  scrollDelta += delta;
45591
+ const x = Number.parseInt(rawX, 10);
45592
+ const y = Number.parseInt(rawY, 10);
45593
+ if (Number.isFinite(x) && Number.isFinite(y))
45594
+ wheelEvents.push({ x, y, delta, horizontal });
45071
45595
  } else {
45072
45596
  const x = Number.parseInt(rawX, 10);
45073
45597
  const y = Number.parseInt(rawY, 10);
@@ -45082,11 +45606,14 @@ function consumeMouseInput(raw) {
45082
45606
  }
45083
45607
  return "";
45084
45608
  });
45085
- return { input, scrollDelta, horizontalScrollDelta, events };
45609
+ return { input, scrollDelta, horizontalScrollDelta, events, wheelEvents };
45086
45610
  }
45087
45611
  function codeCopyTargetAt(targets, point) {
45088
45612
  return targets.find((target) => target.y === point.y && point.x >= target.xStart && point.x <= target.xEnd);
45089
45613
  }
45614
+ function topicPickerTargetAt(targets, point) {
45615
+ return targets.find((target) => target.y === point.y && point.x >= target.xStart && point.x <= target.xEnd);
45616
+ }
45090
45617
  function escapeStopsActiveTurn(state) {
45091
45618
  if (state.overlay || state.creatingTopic)
45092
45619
  return false;
@@ -46254,12 +46781,16 @@ function columnSlice(value, from2, to) {
46254
46781
  }
46255
46782
  function screenSelectionText(lines, selection) {
46256
46783
  const [start, end] = ordered(selection);
46784
+ const clipFrom = selection.paneClip ? selection.paneClip.xStart - 1 : 0;
46785
+ const clipTo = selection.paneClip ? selection.paneClip.xEnd : Number.POSITIVE_INFINITY;
46257
46786
  const selected = [];
46258
46787
  for (let row = start.y;row <= end.y; row += 1) {
46259
46788
  const value = lines[row - 1] ?? "";
46260
- const from2 = row === start.y ? Math.max(0, start.x - 1) : 0;
46261
- const to = row === end.y ? Math.max(from2, end.x) : Number.POSITIVE_INFINITY;
46262
- selected.push(columnSlice(value, from2, to).trimEnd());
46789
+ const rowFrom = row === start.y ? Math.max(0, start.x - 1) : 0;
46790
+ const rowTo = row === end.y ? Math.max(rowFrom, end.x) : Number.POSITIVE_INFINITY;
46791
+ const from2 = Math.max(rowFrom, clipFrom);
46792
+ const to = Math.min(rowTo, clipTo);
46793
+ selected.push(to > from2 ? columnSlice(value, from2, to).trimEnd() : "");
46263
46794
  }
46264
46795
  return selected.join(`
46265
46796
  `).replace(/\n+$/, "");
@@ -46300,14 +46831,18 @@ function highlightRow(value, from2, to) {
46300
46831
  }
46301
46832
  function highlightScreenSelection(frame, selection) {
46302
46833
  const [start, end] = ordered(selection);
46834
+ const clipFrom = selection.paneClip ? selection.paneClip.xStart - 1 : 0;
46835
+ const clipTo = selection.paneClip ? selection.paneClip.xEnd : Number.POSITIVE_INFINITY;
46303
46836
  return frame.split(`
46304
46837
  `).map((line2, index) => {
46305
46838
  const row = index + 1;
46306
46839
  if (row < start.y || row > end.y)
46307
46840
  return line2;
46308
- const from2 = row === start.y ? Math.max(0, start.x - 1) : 0;
46309
- const to = row === end.y ? Math.max(from2, end.x) : Number.POSITIVE_INFINITY;
46310
- return highlightRow(line2, from2, to);
46841
+ const rowFrom = row === start.y ? Math.max(0, start.x - 1) : 0;
46842
+ const rowTo = row === end.y ? Math.max(rowFrom, end.x) : Number.POSITIVE_INFINITY;
46843
+ const from2 = Math.max(rowFrom, clipFrom);
46844
+ const to = Math.min(rowTo, clipTo);
46845
+ return to > from2 ? highlightRow(line2, from2, to) : line2;
46311
46846
  }).join(`
46312
46847
  `);
46313
46848
  }
@@ -46317,6 +46852,210 @@ var init_selection = __esm(() => {
46317
46852
  ANSI_PATTERN = new RegExp("\\u001b\\]8;[^\\u0007\\u001b]*(?:\\u0007|\\u001b\\\\)|\\u001b\\[[0-?]*[ -/]*[@-~]", "y");
46318
46853
  });
46319
46854
 
46855
+ // ../../adapters/terminal/src/shell-pane.ts
46856
+ import xterm from "@xterm/headless";
46857
+ function ptySupport() {
46858
+ const candidate = globalThis.Bun?.Terminal;
46859
+ return typeof candidate === "function" ? candidate : null;
46860
+ }
46861
+ function cellColor(isDefault, isPalette, isRgb, value) {
46862
+ if (isDefault)
46863
+ return DEFAULT_COLOR;
46864
+ if (isPalette)
46865
+ return { kind: "palette", index: value };
46866
+ if (isRgb) {
46867
+ return {
46868
+ kind: "rgb",
46869
+ rgb: [value >> 16 & 255, value >> 8 & 255, value & 255]
46870
+ };
46871
+ }
46872
+ return DEFAULT_COLOR;
46873
+ }
46874
+ function sameColor(a, b) {
46875
+ if (a.kind !== b.kind)
46876
+ return false;
46877
+ if (a.kind === "palette" && b.kind === "palette")
46878
+ return a.index === b.index;
46879
+ if (a.kind === "rgb" && b.kind === "rgb") {
46880
+ return a.rgb[0] === b.rgb[0] && a.rgb[1] === b.rgb[1] && a.rgb[2] === b.rgb[2];
46881
+ }
46882
+ return true;
46883
+ }
46884
+ function sameStyle(a, b) {
46885
+ return sameColor(a.fg, b.fg) && sameColor(a.bg, b.bg) && a.bold === b.bold && a.dim === b.dim && a.italic === b.italic && a.underline === b.underline && a.inverse === b.inverse;
46886
+ }
46887
+ function blankRun(width) {
46888
+ return {
46889
+ text: " ".repeat(width),
46890
+ width,
46891
+ fg: DEFAULT_COLOR,
46892
+ bg: DEFAULT_COLOR,
46893
+ bold: false,
46894
+ dim: false,
46895
+ italic: false,
46896
+ underline: false,
46897
+ inverse: false
46898
+ };
46899
+ }
46900
+ function shellPastePayload(text3, bracketed) {
46901
+ const safe = text3.replaceAll("\x1B[201~", "");
46902
+ return bracketed ? `\x1B[200~${safe}\x1B[201~` : safe;
46903
+ }
46904
+
46905
+ class ShellPane {
46906
+ #xterm;
46907
+ #pty;
46908
+ #process;
46909
+ #cols;
46910
+ #rows;
46911
+ #disposed = false;
46912
+ constructor(options) {
46913
+ const Pty = ptySupport();
46914
+ if (!Pty)
46915
+ throw new Error("the shell pane needs Bun >= 1.3.5 (Bun.Terminal)");
46916
+ this.#cols = Math.max(2, options.cols);
46917
+ this.#rows = Math.max(1, options.rows);
46918
+ const Terminal = xterm.Terminal;
46919
+ this.#xterm = new Terminal({
46920
+ cols: this.#cols,
46921
+ rows: this.#rows,
46922
+ scrollback: 5000,
46923
+ allowProposedApi: true
46924
+ });
46925
+ this.#pty = new Pty({
46926
+ cols: this.#cols,
46927
+ rows: this.#rows,
46928
+ data: (_terminal, data) => {
46929
+ if (this.#disposed)
46930
+ return;
46931
+ this.#xterm.write(data, options.onChange);
46932
+ }
46933
+ });
46934
+ this.#xterm.onData((data) => {
46935
+ if (!this.#disposed)
46936
+ this.#pty.write(data);
46937
+ });
46938
+ const shell2 = options.shell || process.env.SHELL || "/bin/zsh";
46939
+ const env = { ...options.env ?? process.env };
46940
+ env.TERM = "xterm-256color";
46941
+ env.COLORTERM = "truecolor";
46942
+ delete env.TERM_SESSION_ID;
46943
+ const spawn9 = globalThis.Bun.spawn;
46944
+ this.#process = spawn9([shell2, "-l"], {
46945
+ cwd: options.cwd ?? process.cwd(),
46946
+ env,
46947
+ terminal: this.#pty
46948
+ });
46949
+ this.#process.exited.then((code) => {
46950
+ if (this.#disposed)
46951
+ return;
46952
+ this.dispose();
46953
+ options.onExit(code);
46954
+ });
46955
+ }
46956
+ get cols() {
46957
+ return this.#cols;
46958
+ }
46959
+ get rows() {
46960
+ return this.#rows;
46961
+ }
46962
+ get disposed() {
46963
+ return this.#disposed;
46964
+ }
46965
+ write(data) {
46966
+ if (this.#disposed || !data)
46967
+ return;
46968
+ this.#xterm.scrollToBottom();
46969
+ this.#pty.write(data);
46970
+ }
46971
+ paste(text3) {
46972
+ this.write(shellPastePayload(text3, this.#xterm.modes.bracketedPasteMode));
46973
+ }
46974
+ scroll(delta) {
46975
+ if (this.#disposed || delta === 0)
46976
+ return;
46977
+ this.#xterm.scrollLines(-delta);
46978
+ }
46979
+ resize(cols, rows) {
46980
+ const nextCols = Math.max(2, cols);
46981
+ const nextRows = Math.max(1, rows);
46982
+ if (this.#disposed || nextCols === this.#cols && nextRows === this.#rows)
46983
+ return;
46984
+ this.#cols = nextCols;
46985
+ this.#rows = nextRows;
46986
+ this.#xterm.resize(nextCols, nextRows);
46987
+ this.#pty.resize(nextCols, nextRows);
46988
+ }
46989
+ snapshot() {
46990
+ const buffer = this.#xterm.buffer.active;
46991
+ const lines = [];
46992
+ let reusable;
46993
+ for (let y = 0;y < this.#rows; y += 1) {
46994
+ const line2 = buffer.getLine(buffer.viewportY + y);
46995
+ const runs = [];
46996
+ let used = 0;
46997
+ if (line2) {
46998
+ for (let x = 0;x < this.#cols; x += 1) {
46999
+ const cell = line2.getCell(x, reusable);
47000
+ if (!cell)
47001
+ break;
47002
+ reusable = cell;
47003
+ const width = cell.getWidth();
47004
+ if (width === 0)
47005
+ continue;
47006
+ if (used + width > this.#cols)
47007
+ break;
47008
+ const style = {
47009
+ fg: cellColor(cell.isFgDefault(), cell.isFgPalette(), cell.isFgRGB(), cell.getFgColor()),
47010
+ bg: cellColor(cell.isBgDefault(), cell.isBgPalette(), cell.isBgRGB(), cell.getBgColor()),
47011
+ bold: cell.isBold() !== 0,
47012
+ dim: cell.isDim() !== 0,
47013
+ italic: cell.isItalic() !== 0,
47014
+ underline: cell.isUnderline() !== 0,
47015
+ inverse: cell.isInverse() !== 0
47016
+ };
47017
+ const text3 = cell.getChars() || " ";
47018
+ const last = runs[runs.length - 1];
47019
+ if (last && sameStyle(last, style)) {
47020
+ last.text += text3;
47021
+ last.width += width;
47022
+ } else {
47023
+ runs.push({ text: text3, width, ...style });
47024
+ }
47025
+ used += width;
47026
+ }
47027
+ }
47028
+ if (used < this.#cols)
47029
+ runs.push(blankRun(this.#cols - used));
47030
+ lines.push(runs);
47031
+ }
47032
+ const atLiveScreen = buffer.viewportY === buffer.baseY;
47033
+ const hidden = this.#xterm._core?.coreService?.isCursorHidden;
47034
+ return {
47035
+ cols: this.#cols,
47036
+ rows: this.#rows,
47037
+ lines,
47038
+ cursor: atLiveScreen && !hidden ? { x: Math.min(buffer.cursorX, this.#cols - 1), y: buffer.cursorY } : null
47039
+ };
47040
+ }
47041
+ dispose() {
47042
+ if (this.#disposed)
47043
+ return;
47044
+ this.#disposed = true;
47045
+ try {
47046
+ this.#process.kill();
47047
+ } catch {}
47048
+ try {
47049
+ this.#pty.close();
47050
+ } catch {}
47051
+ this.#xterm.dispose();
47052
+ }
47053
+ }
47054
+ var DEFAULT_COLOR;
47055
+ var init_shell_pane = __esm(() => {
47056
+ DEFAULT_COLOR = { kind: "default" };
47057
+ });
47058
+
46320
47059
  // ../../adapters/terminal/src/terminal-restore.ts
46321
47060
  import { writeSync } from "fs";
46322
47061
  function isRetryable(error2) {
@@ -46790,6 +47529,7 @@ class InputHistory {
46790
47529
  }
46791
47530
 
46792
47531
  // ../../adapters/terminal/src/app.ts
47532
+ import { hostname as hostname2 } from "os";
46793
47533
  function kittyKeyboardEnabled(env = process.env) {
46794
47534
  const value = env.NEGOTIUM_TUI_DISABLE_KITTY_KEYBOARD;
46795
47535
  return value !== "1" && value !== "true" && value !== "yes";
@@ -46808,18 +47548,20 @@ function canvasFill(depth2) {
46808
47548
  return `\x1B[48;2;${CANVAS_RGB[0]};${CANVAS_RGB[1]};${CANVAS_RGB[2]}m`;
46809
47549
  }
46810
47550
  }
46811
- function altScreenSequences(env = process.env, savedDepth) {
47551
+ function workingDirectoryReport(cwd, host2 = hostname2()) {
47552
+ const path = cwd.split("/").map((segment) => encodeURIComponent(segment)).join("/");
47553
+ return `\x1B]7;file://${host2}${path}\x07`;
47554
+ }
47555
+ function altScreenSequences(env = process.env, savedDepth, cwd = process.cwd()) {
46812
47556
  const push = kittyKeyboardEnabled(env) ? KITTY_KEYBOARD_PUSH : "";
46813
47557
  const pop = kittyKeyboardEnabled(env) ? KITTY_KEYBOARD_POP : "";
46814
47558
  const depth2 = detectColorDepth({ env, isTty: true, savedDepth });
46815
- const colored = depth2 !== "none";
46816
- const setBackground = colored ? "\x1B]11;#0a0b0f\x07" : "";
46817
47559
  const fillCanvas = canvasFill(depth2);
46818
- 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";
47560
+ const reportCwd = workingDirectoryReport(cwd);
47561
+ const modeResets = "\x1B[0m\x1B[?7h\x1B[?1l\x1B[?1006l\x1B[?1003l\x1B[?2004l\x1B[?25h\x1B[?1049l";
46820
47562
  return {
46821
- enter: `${setBackground}\x1B[?1049h${fillCanvas}\x1B[2J\x1B[H\x1B[?25l\x1B[?2004h\x1B[?1002h\x1B[?1006h\x1B[?1l${push}`,
46822
- exit: `${END_SYNCHRONIZED_UPDATE}${pop}${modeResets}${resetBackground}`,
47563
+ enter: `${reportCwd}\x1B[?1049h${fillCanvas}\x1B[2J\x1B[H\x1B[?25l\x1B[?2004h\x1B[?1003h\x1B[?1006h\x1B[?1l${push}`,
47564
+ exit: `${END_SYNCHRONIZED_UPDATE}${pop}${modeResets}${reportCwd}`,
46823
47565
  abortEnter: `${END_SYNCHRONIZED_UPDATE}${modeResets}`
46824
47566
  };
46825
47567
  }
@@ -46858,9 +47600,19 @@ class TerminalApp {
46858
47600
  #messageHistory = new Map;
46859
47601
  #queuedRuntimeEvents = new Map;
46860
47602
  #selection = null;
47603
+ #selectionDragging = false;
46861
47604
  #subagentGraphDragPoint = null;
46862
47605
  #plainFrameLines = [];
46863
47606
  #codeCopyTargets = [];
47607
+ #topicPickerTargets = [];
47608
+ #sidePanelTracks = [];
47609
+ #sideColumnBounds = null;
47610
+ #sidePanelResizeHandle = null;
47611
+ #sidePanelDrag = null;
47612
+ #sidePanelResizeDrag = null;
47613
+ #shell = null;
47614
+ #terminalPane = null;
47615
+ #conversationBounds = null;
46864
47616
  #lastInterruptAt = 0;
46865
47617
  #subagentGraphGeneration = 0;
46866
47618
  #subagentGraphAbortController = null;
@@ -46981,8 +47733,9 @@ class TerminalApp {
46981
47733
  }
46982
47734
  }
46983
47735
  #handleRuntimeEvent(event) {
46984
- if (runtimeEventInvalidatesSelection(this.#state, event))
47736
+ if (!this.#selectionDragging && runtimeEventInvalidatesSelection(this.#state, event)) {
46985
47737
  this.#selection = null;
47738
+ }
46986
47739
  if (this.#messageLoadGeneration.has(event.topicId) && runtimeEventWaitsForMessageLoad(event)) {
46987
47740
  const queued = this.#queuedRuntimeEvents.get(event.topicId) ?? [];
46988
47741
  queued.push(event);
@@ -47234,8 +47987,19 @@ class TerminalApp {
47234
47987
  #render() {
47235
47988
  const columns = process.stdout.columns ?? 100;
47236
47989
  const rows = process.stdout.rows ?? 30;
47237
- const rendered = renderAppFrame(this.#state, columns, rows, this.#animationFrame);
47990
+ const rendered = renderAppFrame(this.#state, columns, rows, this.#animationFrame, undefined, {
47991
+ shell: this.#shell?.snapshot() ?? null
47992
+ });
47238
47993
  this.#codeCopyTargets = rendered.codeCopyTargets;
47994
+ this.#topicPickerTargets = rendered.topicPickerTargets;
47995
+ this.#sidePanelTracks = rendered.sidePanelTracks;
47996
+ this.#sideColumnBounds = rendered.sideColumnBounds;
47997
+ this.#sidePanelResizeHandle = rendered.sidePanelResizeHandle;
47998
+ this.#terminalPane = rendered.terminalPane;
47999
+ this.#conversationBounds = rendered.conversationBounds;
48000
+ if (this.#shell && rendered.terminalPane) {
48001
+ this.#shell.resize(rendered.terminalPane.cols, rendered.terminalPane.rows);
48002
+ }
47239
48003
  const baseFrame = rendered.frame;
47240
48004
  this.#plainFrameLines = stripAnsi(baseFrame).split(`
47241
48005
  `);
@@ -47602,6 +48366,10 @@ class TerminalApp {
47602
48366
  }
47603
48367
  #commitPaste(payload) {
47604
48368
  this.#selection = null;
48369
+ if (this.#state.terminalPaneFocused && this.#shell) {
48370
+ this.#shell.paste(payload);
48371
+ return;
48372
+ }
47605
48373
  if (this.#state.overlay === "topics") {
47606
48374
  const filterText = topicFilterPasteText(payload);
47607
48375
  if (filterText) {
@@ -47623,24 +48391,41 @@ class TerminalApp {
47623
48391
  }
47624
48392
  #handleKeySegment(segment) {
47625
48393
  const mouse = consumeMouseInput(segment);
47626
- if (mouse.horizontalScrollDelta !== 0 && (this.#state.overlay === "subagents" || this.#state.overlay === "decisions")) {
48394
+ const { scrollDelta, horizontalScrollDelta } = this.#routeWheelEvents(mouse.wheelEvents);
48395
+ if (horizontalScrollDelta !== 0 && (this.#state.overlay === "subagents" || this.#state.overlay === "decisions")) {
47627
48396
  this.#selection = null;
47628
- this.#panActiveGraph(-mouse.horizontalScrollDelta, 0);
48397
+ this.#panActiveGraph(-horizontalScrollDelta, 0);
47629
48398
  }
47630
- if (mouse.scrollDelta !== 0) {
48399
+ if (scrollDelta !== 0) {
47631
48400
  this.#selection = null;
47632
- this.#scroll(mouse.scrollDelta);
48401
+ this.#scroll(scrollDelta);
47633
48402
  }
47634
48403
  for (const event of mouse.events) {
47635
48404
  if (this.#state.overlay === "subagents" || this.#state.overlay === "decisions") {
47636
48405
  this.#handleGraphMouse(event);
47637
- } else
47638
- this.#handleMouseSelection(event);
48406
+ continue;
48407
+ }
48408
+ if (this.#handleSidePanelResizeDrag(event))
48409
+ continue;
48410
+ this.#focusPaneUnderPress(event);
48411
+ if (this.#handleSidePanelScrollbarDrag(event))
48412
+ continue;
48413
+ if (this.#handlePickableRowMouse(event))
48414
+ continue;
48415
+ this.#handleMouseSelection(event);
47639
48416
  }
47640
48417
  const chunk = normalizeKeySequences(mouse.input);
47641
48418
  if (!chunk)
47642
48419
  return;
47643
48420
  this.#selection = null;
48421
+ if (chunk === TERMINAL_PANE_KEY) {
48422
+ this.#toggleTerminalPane();
48423
+ return;
48424
+ }
48425
+ if (this.#state.terminalPaneFocused && this.#shell) {
48426
+ this.#shell.write(chunk);
48427
+ return;
48428
+ }
47644
48429
  if (chunk === "\x03") {
47645
48430
  this.#handleInterrupt();
47646
48431
  return;
@@ -47674,10 +48459,17 @@ class TerminalApp {
47674
48459
  this.#toggleDecisionGraph();
47675
48460
  return;
47676
48461
  }
47677
- if (chunk === "\x14") {
48462
+ if (chunk === "\x10") {
48463
+ if (editingVaultSecret)
48464
+ return;
48465
+ this.#state = toggleTopicsPanel(this.#state);
48466
+ this.#queueRender();
48467
+ return;
48468
+ }
48469
+ if (chunk === "\x12") {
47678
48470
  if (editingVaultSecret)
47679
48471
  return;
47680
- this.#state = toggleTaskSidebar(this.#state);
48472
+ this.#state = cycleTopicsPanelSort(this.#state);
47681
48473
  this.#queueRender();
47682
48474
  return;
47683
48475
  }
@@ -48099,7 +48891,7 @@ class TerminalApp {
48099
48891
  notice: undefined,
48100
48892
  noticeLevel: undefined
48101
48893
  };
48102
- if (text3.startsWith("/")) {
48894
+ if (isKnownTerminalCommand(text3)) {
48103
48895
  await this.#runCommand(text3);
48104
48896
  return;
48105
48897
  }
@@ -48815,24 +49607,283 @@ class TerminalApp {
48815
49607
  }
48816
49608
  this.#queueRender();
48817
49609
  }
49610
+ #routeWheelEvents(events) {
49611
+ let scrollDelta = 0;
49612
+ let horizontalScrollDelta = 0;
49613
+ if (events.length === 0)
49614
+ return { scrollDelta, horizontalScrollDelta };
49615
+ const bounds = this.#sideColumnBounds;
49616
+ const pane = this.#terminalPane;
49617
+ let touchedPanel = false;
49618
+ for (const event of events) {
49619
+ if (pane && this.#shell && event.x >= pane.xStart && event.x <= pane.xEnd && !event.horizontal) {
49620
+ this.#shell.scroll(event.delta);
49621
+ touchedPanel = true;
49622
+ continue;
49623
+ }
49624
+ const section = bounds && event.x >= bounds.xStart && event.x <= bounds.xEnd ? bounds.sections.find((s) => event.y >= s.yStart && event.y <= s.yEnd) : undefined;
49625
+ if (!section) {
49626
+ if (event.horizontal)
49627
+ horizontalScrollDelta += event.delta;
49628
+ else
49629
+ scrollDelta += event.delta;
49630
+ continue;
49631
+ }
49632
+ touchedPanel = true;
49633
+ const columns = process.stdout.columns ?? 100;
49634
+ const rows = process.stdout.rows ?? 30;
49635
+ 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));
49636
+ }
49637
+ if (touchedPanel)
49638
+ this.#queueRender();
49639
+ return { scrollDelta, horizontalScrollDelta };
49640
+ }
49641
+ #toggleTerminalPane() {
49642
+ if (this.#state.terminalPaneOpen) {
49643
+ const shell2 = this.#shell;
49644
+ this.#shell = null;
49645
+ shell2?.dispose();
49646
+ this.#state = closeTerminalPane(this.#state);
49647
+ this.#queueRender();
49648
+ return;
49649
+ }
49650
+ if (!this.#shell) {
49651
+ if (!ptySupport()) {
49652
+ this.#state = {
49653
+ ...this.#state,
49654
+ notice: "Shell pane needs Bun >= 1.3.5 (bun upgrade)",
49655
+ noticeLevel: "warn"
49656
+ };
49657
+ this.#queueRender();
49658
+ return;
49659
+ }
49660
+ try {
49661
+ const columns2 = process.stdout.columns ?? 100;
49662
+ const rows = process.stdout.rows ?? 30;
49663
+ const shell2 = new ShellPane({
49664
+ cols: Math.max(30, Math.floor(columns2 * 0.3)),
49665
+ rows: Math.max(1, rows),
49666
+ cwd: process.cwd(),
49667
+ onChange: () => this.#queueRender(),
49668
+ onExit: () => {
49669
+ if (this.#shell !== shell2)
49670
+ return;
49671
+ this.#shell = null;
49672
+ this.#state = {
49673
+ ...closeTerminalPane(this.#state),
49674
+ notice: "Shell exited",
49675
+ noticeLevel: "info"
49676
+ };
49677
+ this.#queueRender();
49678
+ }
49679
+ });
49680
+ this.#shell = shell2;
49681
+ } catch (error2) {
49682
+ this.#state = {
49683
+ ...this.#state,
49684
+ notice: `Shell pane failed: ${error2 instanceof Error ? error2.message : String(error2)}`,
49685
+ noticeLevel: "error"
49686
+ };
49687
+ this.#queueRender();
49688
+ return;
49689
+ }
49690
+ }
49691
+ this.#state = openTerminalPane(this.#state);
49692
+ const columns = process.stdout.columns ?? 100;
49693
+ if (maxTerminalPaneWidth(this.#state, columns) < 30 || columns < 92) {
49694
+ this.#state = {
49695
+ ...this.#state,
49696
+ notice: "Window too narrow for the shell pane \u2014 widen it to see the pane",
49697
+ noticeLevel: "warn"
49698
+ };
49699
+ }
49700
+ this.#queueRender();
49701
+ }
49702
+ #focusPaneUnderPress(event) {
49703
+ if (event.kind !== "press" || (event.button & 3) !== 0)
49704
+ return;
49705
+ const pane = this.#terminalPane;
49706
+ const inside = !!pane && event.x >= pane.xStart && event.x <= pane.xEnd;
49707
+ const next = focusTerminalPane(this.#state, inside && !!this.#shell);
49708
+ if (next !== this.#state) {
49709
+ this.#state = next;
49710
+ this.#queueRender();
49711
+ }
49712
+ }
49713
+ #handleSidePanelResizeDrag(event) {
49714
+ if (event.kind === "press" && (event.button & 3) === 0) {
49715
+ const pane = this.#terminalPane;
49716
+ if (pane && event.x === pane.dividerX && event.y >= pane.yStart && event.y <= pane.yEnd) {
49717
+ this.#sidePanelResizeDrag = { target: "terminal", startX: event.x, startWidth: pane.cols };
49718
+ return true;
49719
+ }
49720
+ const handle = this.#sidePanelResizeHandle;
49721
+ if (!handle || event.x < handle.xStart || event.x > handle.xEnd || event.y < handle.yStart || event.y > handle.yEnd) {
49722
+ this.#sidePanelResizeDrag = null;
49723
+ return false;
49724
+ }
49725
+ this.#sidePanelResizeDrag = {
49726
+ target: "side",
49727
+ startX: event.x,
49728
+ startWidth: this.#state.sidePanelWidth
49729
+ };
49730
+ return true;
49731
+ }
49732
+ const drag = this.#sidePanelResizeDrag;
49733
+ if (!drag)
49734
+ return false;
49735
+ if (event.kind === "release") {
49736
+ this.#sidePanelResizeDrag = null;
49737
+ return true;
49738
+ }
49739
+ const columns = process.stdout.columns ?? 100;
49740
+ if (drag.target === "terminal") {
49741
+ const nextWidth2 = drag.startWidth - (event.x - drag.startX);
49742
+ this.#state = setTerminalPaneWidth(this.#state, nextWidth2, maxTerminalPaneWidth(this.#state, columns));
49743
+ this.#queueRender();
49744
+ return true;
49745
+ }
49746
+ const nextWidth = drag.startWidth + (event.x - drag.startX);
49747
+ this.#state = setSidePanelWidth(this.#state, nextWidth, maxSidePanelWidth(columns));
49748
+ this.#queueRender();
49749
+ return true;
49750
+ }
49751
+ #handleSidePanelScrollbarDrag(event) {
49752
+ if (event.kind === "press" && (event.button & 3) === 0) {
49753
+ const track2 = this.#sidePanelTracks.find((t) => event.x === t.x && event.y >= t.yStart && event.y < t.yStart + t.viewportRows);
49754
+ if (!track2) {
49755
+ this.#sidePanelDrag = null;
49756
+ return false;
49757
+ }
49758
+ const current3 = track2.panel === "topics" ? this.#state.topicsPanelScrollOffset : this.#state.taskPanelScrollOffset;
49759
+ const thumb = scrollbarThumb(track2.rowCount, track2.viewportRows, current3);
49760
+ this.#sidePanelDrag = {
49761
+ panel: track2.panel,
49762
+ track: track2,
49763
+ grabRowOffset: event.y - (track2.yStart + thumb.top)
49764
+ };
49765
+ return true;
49766
+ }
49767
+ const drag = this.#sidePanelDrag;
49768
+ if (!drag)
49769
+ return false;
49770
+ if (event.kind === "release") {
49771
+ this.#sidePanelDrag = null;
49772
+ return true;
49773
+ }
49774
+ const { track, grabRowOffset, panel } = drag;
49775
+ const offset = scrollOffsetFromDragRow(track.rowCount, track.viewportRows, event.y - track.yStart, grabRowOffset);
49776
+ const columns = process.stdout.columns ?? 100;
49777
+ const rows = process.stdout.rows ?? 30;
49778
+ this.#state = panel === "topics" ? setTopicsPanelScrollOffset(this.#state, offset, maxTopicsPanelScrollOffset(this.#state, columns, rows)) : setTaskPanelScrollOffset(this.#state, offset, maxTaskPanelScrollOffset(this.#state, columns, rows));
49779
+ this.#queueRender();
49780
+ return true;
49781
+ }
49782
+ #handlePickableRowMouse(event) {
49783
+ const inOverlay = this.#state.overlay === "topics";
49784
+ const hovering = event.kind === "drag" && (event.button & 3) === 3;
49785
+ const isClick = event.kind === "press" && (event.button & 3) === 0;
49786
+ if (!inOverlay && !hovering && !isClick)
49787
+ return false;
49788
+ const target = topicPickerTargetAt(this.#topicPickerTargets, { x: event.x, y: event.y });
49789
+ if (inOverlay) {
49790
+ if (!target)
49791
+ return true;
49792
+ const highlighted = this.#highlightPickedTarget(target);
49793
+ if (!hovering && highlighted)
49794
+ this.#selectPickedTopic();
49795
+ return true;
49796
+ }
49797
+ if (target?.backgroundSessionId !== undefined) {
49798
+ const sessionId = target.backgroundSessionId;
49799
+ if (hovering) {
49800
+ this.#setSidePanelHover(undefined, sessionId);
49801
+ return true;
49802
+ }
49803
+ this.#setSidePanelHover(undefined, undefined);
49804
+ this.#state = {
49805
+ ...this.#state,
49806
+ overlay: "background-session",
49807
+ topicPickerBackgroundId: sessionId,
49808
+ backgroundScrollOffset: 0,
49809
+ notice: undefined,
49810
+ noticeLevel: undefined
49811
+ };
49812
+ this.#queueRender();
49813
+ return true;
49814
+ }
49815
+ if (target?.topicIndex === undefined) {
49816
+ this.#setSidePanelHover(undefined, undefined);
49817
+ return false;
49818
+ }
49819
+ const topic = this.#state.topics[target.topicIndex];
49820
+ if (!topic)
49821
+ return false;
49822
+ if (hovering) {
49823
+ this.#setSidePanelHover(topic.id, undefined);
49824
+ return true;
49825
+ }
49826
+ this.#setSidePanelHover(undefined, undefined);
49827
+ if (topic.id !== this.#state.activeTopicId)
49828
+ this.#activateTopic(topic.id);
49829
+ return true;
49830
+ }
49831
+ #setSidePanelHover(topicId, backgroundSessionId) {
49832
+ if (this.#state.sidePanelHoverTopicId === topicId && this.#state.sidePanelHoverBackgroundSessionId === backgroundSessionId) {
49833
+ return;
49834
+ }
49835
+ this.#state = {
49836
+ ...this.#state,
49837
+ sidePanelHoverTopicId: topicId,
49838
+ sidePanelHoverBackgroundSessionId: backgroundSessionId
49839
+ };
49840
+ this.#queueRender();
49841
+ }
49842
+ #highlightPickedTarget(target) {
49843
+ if (target.backgroundSessionId !== undefined) {
49844
+ if (this.#state.topicPickerBackgroundId === target.backgroundSessionId)
49845
+ return true;
49846
+ this.#state = { ...this.#state, topicPickerBackgroundId: target.backgroundSessionId };
49847
+ } else if (target.topicIndex !== undefined) {
49848
+ if (!this.#state.topicPickerBackgroundId && this.#state.topicPickerIndex === target.topicIndex) {
49849
+ return true;
49850
+ }
49851
+ this.#state = {
49852
+ ...this.#state,
49853
+ topicPickerIndex: target.topicIndex,
49854
+ topicPickerBackgroundId: undefined
49855
+ };
49856
+ } else {
49857
+ return false;
49858
+ }
49859
+ this.#queueRender();
49860
+ return true;
49861
+ }
48818
49862
  #handleMouseSelection(event) {
48819
49863
  if ((event.button & 3) !== 0 && event.kind !== "release")
48820
49864
  return;
48821
- const point = { x: event.x, y: event.y };
48822
49865
  if (event.kind === "press") {
49866
+ const point = { x: event.x, y: event.y };
48823
49867
  const codeTarget = codeCopyTargetAt(this.#codeCopyTargets, point);
48824
49868
  if (codeTarget) {
48825
49869
  this.#selection = null;
48826
49870
  this.#copyCodeBlock(codeTarget.text);
48827
49871
  return;
48828
49872
  }
48829
- this.#selection = { anchor: point, focus: point };
49873
+ const bounds = this.#sideColumnBounds;
49874
+ const pane = this.#terminalPane;
49875
+ const conversation = this.#conversationBounds;
49876
+ const paneClip = bounds && point.x >= bounds.xStart && point.x <= bounds.xEnd ? { xStart: bounds.xStart, xEnd: bounds.xEnd } : pane && point.x >= pane.xStart && point.x <= pane.xEnd ? { xStart: pane.xStart, xEnd: pane.xEnd } : (bounds || pane) && conversation ? { xStart: conversation.xStart, xEnd: conversation.xEnd } : undefined;
49877
+ this.#selection = { anchor: point, focus: point, paneClip };
49878
+ this.#selectionDragging = true;
48830
49879
  this.#queueRender();
48831
49880
  return;
48832
49881
  }
49882
+ if (event.kind === "release")
49883
+ this.#selectionDragging = false;
48833
49884
  if (!this.#selection)
48834
49885
  return;
48835
- this.#selection = { ...this.#selection, focus: point };
49886
+ this.#selection = { ...this.#selection, focus: { x: event.x, y: event.y } };
48836
49887
  if (event.kind === "drag") {
48837
49888
  this.#queueRender();
48838
49889
  return;
@@ -49073,6 +50124,8 @@ class TerminalApp {
49073
50124
  this.#finishRun = null;
49074
50125
  }
49075
50126
  async#cleanup(clientStartAttempted, uiActive) {
50127
+ this.#shell?.dispose();
50128
+ this.#shell = null;
49076
50129
  this.#subagentGraphGeneration += 1;
49077
50130
  this.#subagentGraphAbortController?.abort();
49078
50131
  this.#subagentGraphAbortController = null;
@@ -49114,7 +50167,7 @@ class TerminalApp {
49114
50167
  await this.#client.stop();
49115
50168
  }
49116
50169
  }
49117
- var KITTY_KEYBOARD_PUSH = "\x1B[>1u", KITTY_KEYBOARD_POP = "\x1B[<u", CANVAS_RGB, ENTER_ALT_SCREEN, EXIT_ALT_SCREEN, ABORT_ENTER_ALT_SCREEN, NEW_TOPIC_KEY = "\x0E", DELETE_TOPIC_KEY = "\x04", FILTER_BACKSPACE_KEYS, CONFIRM_KEYS, CANCEL_KEYS, INPUT_CARRY_FLUSH_MS = 15;
50170
+ var KITTY_KEYBOARD_PUSH = "\x1B[>1u", KITTY_KEYBOARD_POP = "\x1B[<u", CANVAS_RGB, ENTER_ALT_SCREEN, EXIT_ALT_SCREEN, ABORT_ENTER_ALT_SCREEN, NEW_TOPIC_KEY = "\x0E", DELETE_TOPIC_KEY = "\x04", TERMINAL_PANE_KEY = "\x14", FILTER_BACKSPACE_KEYS, CONFIRM_KEYS, CANCEL_KEYS, INPUT_CARRY_FLUSH_MS = 15;
49118
50171
  var init_app = __esm(async () => {
49119
50172
  init_color_depth();
49120
50173
  init_commands();
@@ -49122,6 +50175,7 @@ var init_app = __esm(async () => {
49122
50175
  init_path_suggest();
49123
50176
  init_screen_renderer();
49124
50177
  init_selection();
50178
+ init_shell_pane();
49125
50179
  init_subagent_graph();
49126
50180
  init_terminal_restore();
49127
50181
  await __promiseAll([
@@ -52518,8 +53572,8 @@ var init_canonical_mcp_bridge = __esm(async () => {
52518
53572
  });
52519
53573
 
52520
53574
  // ../../adapters/otium/src/secure-transport.ts
52521
- function isLoopbackHostname(hostname2) {
52522
- const normalized = hostname2.toLowerCase();
53575
+ function isLoopbackHostname(hostname3) {
53576
+ const normalized = hostname3.toLowerCase();
52523
53577
  if (normalized === "localhost" || normalized === "::1" || normalized === "[::1]")
52524
53578
  return true;
52525
53579
  const octets = normalized.split(".");
@@ -55727,7 +56781,7 @@ __export(exports_join_cli, {
55727
56781
  localNodeNameDefault: () => localNodeNameDefault,
55728
56782
  joinCommand: () => joinCommand
55729
56783
  });
55730
- import { hostname as hostname2 } from "os";
56784
+ import { hostname as hostname3 } from "os";
55731
56785
  function option2(args, name) {
55732
56786
  const index = args.indexOf(`--${name}`);
55733
56787
  return index >= 0 ? args[index + 1]?.trim() : undefined;
@@ -55749,7 +56803,7 @@ function localNodeNameDefault() {
55749
56803
  if (normalized)
55750
56804
  return normalized;
55751
56805
  }
55752
- return normalizeNodeNameCandidate(hostname2());
56806
+ return normalizeNodeNameCandidate(hostname3());
55753
56807
  }
55754
56808
  async function confirmEnrollment(message2) {
55755
56809
  if (!process.stdin.isTTY)
@@ -59178,4 +60232,4 @@ switch (command) {
59178
60232
  }
59179
60233
  }
59180
60234
 
59181
- //# debugId=CF7D25563A652B9264756E2164756E21
60235
+ //# debugId=3A2E1C01E70CD8A864756E2164756E21