@deepseek-harness-tui/dsh-tui 0.11.2 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/README.md +21 -5
  2. package/bin/dsh-tui.js +30 -3
  3. package/cordis.patch.yml +24 -5
  4. package/guide/dsh-tui-guide/SKILL.md +37 -0
  5. package/guide/dsh-tui-guide/configuration.en.md +410 -0
  6. package/guide/dsh-tui-guide/configuration.md +349 -0
  7. package/guide/dsh-tui-guide/getting-started.en.md +398 -0
  8. package/guide/dsh-tui-guide/getting-started.md +397 -0
  9. package/guide/dsh-tui-guide/interaction.en.md +674 -0
  10. package/guide/dsh-tui-guide/interaction.md +747 -0
  11. package/guide/dsh-tui-guide/migrate.en.md +166 -0
  12. package/guide/dsh-tui-guide/migrate.md +132 -0
  13. package/guide/dsh-tui-guide/plugins.en.md +68 -0
  14. package/guide/dsh-tui-guide/plugins.md +56 -0
  15. package/guide/dsh-tui-guide/themes.en.md +192 -0
  16. package/guide/dsh-tui-guide/themes.md +185 -0
  17. package/guide/dsh-tui-guide/user-guide.en.md +608 -0
  18. package/guide/dsh-tui-guide/user-guide.md +586 -0
  19. package/guide/dsh-tui-guide/vscode.en.md +301 -0
  20. package/guide/dsh-tui-guide/vscode.md +272 -0
  21. package/lib/settings.json +5 -5
  22. package/lib/types/adapter/channel/ui-policy.d.ts +3 -2
  23. package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
  24. package/lib/types/adapter/channel/ui-policy.js +4 -0
  25. package/lib/types/adapter/ports/channel-settings.d.ts +2 -1
  26. package/lib/types/adapter/ports/channel-settings.d.ts.map +1 -1
  27. package/lib/types/adapter/ports/channel-ui.d.ts +29 -2
  28. package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
  29. package/lib/types/adapter/ports/channel-view.d.ts +43 -9
  30. package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
  31. package/lib/types/components/Chat/JobCard.d.ts +9 -0
  32. package/lib/types/components/Chat/JobCard.d.ts.map +1 -1
  33. package/lib/types/components/Chat/JobCard.js +42 -15
  34. package/lib/types/components/Chat/SubagentMessage.js +6 -6
  35. package/lib/types/components/JobsPanel.d.ts.map +1 -1
  36. package/lib/types/components/JobsPanel.js +80 -33
  37. package/lib/types/components/MessageList.js +3 -3
  38. package/lib/types/components/PresetPicker.d.ts +4 -0
  39. package/lib/types/components/PresetPicker.d.ts.map +1 -1
  40. package/lib/types/components/PresetPicker.js +5 -1
  41. package/lib/types/components/PromptInput.d.ts.map +1 -1
  42. package/lib/types/components/PromptInput.js +12 -1
  43. package/lib/types/components/StarPrompt.d.ts +6 -0
  44. package/lib/types/components/StarPrompt.d.ts.map +1 -1
  45. package/lib/types/components/StarPrompt.js +45 -0
  46. package/lib/types/components/SubagentCard.d.ts.map +1 -1
  47. package/lib/types/components/SubagentCard.js +8 -8
  48. package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
  49. package/lib/types/components/SubagentDetailScene.js +108 -10
  50. package/lib/types/components/WhaleGirl.d.ts +6 -0
  51. package/lib/types/components/WhaleGirl.d.ts.map +1 -1
  52. package/lib/types/components/WhaleGirl.js +18 -0
  53. package/lib/types/components/messages/AssistantThinkingMessage.js +4 -4
  54. package/lib/types/components/trajectory/MiniWake.d.ts +11 -1
  55. package/lib/types/components/trajectory/MiniWake.d.ts.map +1 -1
  56. package/lib/types/components/trajectory/MiniWake.js +9 -3
  57. package/lib/types/dsh-adapter/channel/external-commands.d.ts.map +1 -1
  58. package/lib/types/dsh-adapter/channel/external-commands.js +8 -2
  59. package/lib/types/dsh-adapter/channel/job-projection.d.ts +1 -0
  60. package/lib/types/dsh-adapter/channel/job-projection.d.ts.map +1 -1
  61. package/lib/types/dsh-adapter/channel/job-projection.js +88 -3
  62. package/lib/types/dsh-adapter/channel/mode-actions.d.ts +13 -0
  63. package/lib/types/dsh-adapter/channel/mode-actions.d.ts.map +1 -1
  64. package/lib/types/dsh-adapter/channel/mode-actions.js +48 -7
  65. package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts.map +1 -1
  66. package/lib/types/dsh-adapter/channel/mode-permission-actions.js +25 -9
  67. package/lib/types/dsh-adapter/channel/preferences.d.ts +1 -1
  68. package/lib/types/dsh-adapter/channel/preferences.d.ts.map +1 -1
  69. package/lib/types/dsh-adapter/channel/preferences.js +17 -7
  70. package/lib/types/dsh-adapter/channel/state.d.ts +4 -2
  71. package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
  72. package/lib/types/dsh-adapter/channel/state.js +1 -1
  73. package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -1
  74. package/lib/types/dsh-adapter/channel/subagent-projection.js +5 -0
  75. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  76. package/lib/types/dsh-adapter/channel.js +16 -1
  77. package/lib/types/dsh-adapter/contract.d.ts +5 -4
  78. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  79. package/lib/types/dsh-adapter/contract.js +9 -3
  80. package/lib/types/dsh-adapter/jobs.d.ts +82 -16
  81. package/lib/types/dsh-adapter/jobs.d.ts.map +1 -1
  82. package/lib/types/dsh-adapter/jobs.js +104 -9
  83. package/lib/types/dsh-adapter/oauth/bonus.d.ts +27 -0
  84. package/lib/types/dsh-adapter/oauth/bonus.d.ts.map +1 -0
  85. package/lib/types/dsh-adapter/oauth/bonus.js +69 -0
  86. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/command.d.ts +2 -1
  87. package/lib/types/dsh-adapter/oauth/command.d.ts.map +1 -0
  88. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/command.js +7 -4
  89. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/credentials.d.ts +13 -10
  90. package/lib/types/dsh-adapter/oauth/credentials.d.ts.map +1 -0
  91. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/credentials.js +61 -23
  92. package/lib/types/dsh-adapter/oauth/deepseek.d.ts +39 -0
  93. package/lib/types/dsh-adapter/oauth/deepseek.d.ts.map +1 -0
  94. package/lib/types/dsh-adapter/oauth/deepseek.js +189 -0
  95. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/index.d.ts +24 -16
  96. package/lib/types/dsh-adapter/oauth/index.d.ts.map +1 -0
  97. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/index.js +34 -20
  98. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/interaction.d.ts +14 -12
  99. package/lib/types/dsh-adapter/oauth/interaction.d.ts.map +1 -0
  100. package/lib/types/dsh-adapter/oauth/interaction.js +283 -0
  101. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/opener.d.ts +2 -1
  102. package/lib/types/dsh-adapter/oauth/opener.d.ts.map +1 -0
  103. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/opener.js +1 -1
  104. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/pi-ai.d.ts +10 -3
  105. package/lib/types/dsh-adapter/oauth/pi-ai.d.ts.map +1 -0
  106. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/pi-ai.js +8 -3
  107. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/profiles.d.ts +6 -3
  108. package/lib/types/dsh-adapter/oauth/profiles.d.ts.map +1 -0
  109. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/profiles.js +11 -3
  110. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/service.d.ts +14 -7
  111. package/lib/types/dsh-adapter/oauth/service.d.ts.map +1 -0
  112. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/service.js +82 -17
  113. package/lib/types/dsh-adapter/plugin.d.ts +8 -1
  114. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  115. package/lib/types/dsh-adapter/plugin.js +72 -12
  116. package/lib/types/dsh-adapter/presets.d.ts +6 -4
  117. package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
  118. package/lib/types/dsh-adapter/presets.js +6 -4
  119. package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -1
  120. package/lib/types/dsh-adapter/providerWizard.js +6 -2
  121. package/lib/types/dsh-adapter/subagents.d.ts +1 -0
  122. package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
  123. package/lib/types/dsh-adapter/subagents.js +10 -1
  124. package/lib/types/i18n.d.ts +174 -18
  125. package/lib/types/i18n.d.ts.map +1 -1
  126. package/lib/types/i18n.js +53 -11
  127. package/lib/types/ink/components/App.d.ts.map +1 -1
  128. package/lib/types/ink/components/App.js +10 -1
  129. package/lib/types/ink/parse-keypress.d.ts +35 -0
  130. package/lib/types/ink/parse-keypress.d.ts.map +1 -1
  131. package/lib/types/ink/parse-keypress.js +611 -83
  132. package/lib/types/ink/terminal-querier.d.ts +25 -0
  133. package/lib/types/ink/terminal-querier.d.ts.map +1 -1
  134. package/lib/types/ink/terminal-querier.js +42 -0
  135. package/lib/types/ink/update-overflow-guard.d.ts +24 -0
  136. package/lib/types/ink/update-overflow-guard.d.ts.map +1 -1
  137. package/lib/types/ink/update-overflow-guard.js +25 -3
  138. package/lib/types/minimalUiMode.d.ts +19 -0
  139. package/lib/types/minimalUiMode.d.ts.map +1 -0
  140. package/lib/types/minimalUiMode.js +23 -0
  141. package/lib/types/oauth.d.ts +5 -10
  142. package/lib/types/oauth.d.ts.map +1 -1
  143. package/lib/types/oauth.js +5 -10
  144. package/lib/types/screens/Chat.d.ts +4 -1
  145. package/lib/types/screens/Chat.d.ts.map +1 -1
  146. package/lib/types/screens/Chat.js +27 -7
  147. package/lib/types/screens/StatusLine.d.ts +4 -0
  148. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  149. package/lib/types/screens/StatusLine.js +12 -12
  150. package/lib/types/settings/definitions.js +4 -4
  151. package/lib/types/tips.d.ts.map +1 -1
  152. package/lib/types/tips.js +10 -4
  153. package/lib/types/update.js +2 -2
  154. package/lib/types/utils/pastedImagePath.d.ts +8 -0
  155. package/lib/types/utils/pastedImagePath.d.ts.map +1 -1
  156. package/lib/types/utils/pastedImagePath.js +29 -5
  157. package/package.json +111 -100
  158. package/presets/liangshen/agent.cordis.yml +4 -1
  159. package/presets/liangshen/preset.yml +1 -1
  160. package/lib/types/minimalMode.d.ts +0 -13
  161. package/lib/types/minimalMode.d.ts.map +0 -1
  162. package/lib/types/minimalMode.js +0 -17
  163. package/node_modules/@deepseek-harness-tui/dsh-auth/LICENSE +0 -21
  164. package/node_modules/@deepseek-harness-tui/dsh-auth/README.md +0 -143
  165. package/node_modules/@deepseek-harness-tui/dsh-auth/cordis.patch.yml +0 -11
  166. package/node_modules/@deepseek-harness-tui/dsh-auth/dsh-plugin.json +0 -44
  167. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/interaction.js +0 -193
  168. package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +0 -69
@@ -6,8 +6,10 @@ import { Divider } from './design-system/Divider.js';
6
6
  import { ExitButton } from './SubagentDashboard.js';
7
7
  import { isPlainReturnInput } from '../utils/modifiers.js';
8
8
  import { toolNameColor } from './messages/AssistantToolUseMessage.js';
9
+ import { Markdown } from './Markdown.js';
9
10
  import { getCliHighlightPromise } from '../terminal-utils/cliHighlight.js';
10
- import { isMinimalMode } from '../minimalMode.js';
11
+ import { isMinimalUiMode } from '../minimalUiMode.js';
12
+ import { THINKING_SETTLED_MARKER } from '../terminal-utils/figures.js';
11
13
  function formatDuration(ms) {
12
14
  if (ms < 1000)
13
15
  return `${ms}ms`;
@@ -21,16 +23,16 @@ function formatTimestamp(ts) {
21
23
  return new Date(ts).toLocaleTimeString();
22
24
  }
23
25
  function statusGlyph(status) {
24
- const minimal = isMinimalMode();
26
+ const minimalUi = isMinimalUiMode();
25
27
  if (status === 'completed')
26
- return { glyph: minimal ? '✓' : '🟢', color: minimal ? undefined : 'success', label: 'done' };
28
+ return { glyph: minimalUi ? '✓' : '🟢', color: minimalUi ? undefined : 'success', label: 'done' };
27
29
  if (status === 'failed')
28
- return { glyph: minimal ? '×' : '🔴', color: minimal ? undefined : 'error', label: 'failed' };
30
+ return { glyph: minimalUi ? '×' : '🔴', color: minimalUi ? undefined : 'error', label: 'failed' };
29
31
  if (status === 'cancelled')
30
- return { glyph: minimal ? '×' : '🔴', color: minimal ? undefined : 'error', label: 'cancelled' };
32
+ return { glyph: minimalUi ? '×' : '🔴', color: minimalUi ? undefined : 'error', label: 'cancelled' };
31
33
  if (status === 'unknown')
32
- return { glyph: minimal ? '·' : '⚪', color: minimal ? undefined : 'subtle', label: 'history' };
33
- return { glyph: minimal ? '·' : '🟡', color: minimal ? undefined : 'warning', label: 'running' };
34
+ return { glyph: minimalUi ? '·' : '⚪', color: minimalUi ? undefined : 'subtle', label: 'history' };
35
+ return { glyph: minimalUi ? '·' : '🟡', color: minimalUi ? undefined : 'warning', label: 'running' };
34
36
  }
35
37
  const PAGES = ['summary', 'output', 'tools'];
36
38
  /** One label/value row of the summary stats card. */
@@ -67,6 +69,39 @@ function JsonArgsText({ raw }) {
67
69
  return _jsx(Text, { wrap: "wrap", children: highlighted });
68
70
  return _jsx(Text, { dimColor: true, wrap: "wrap", children: flat });
69
71
  }
72
+ function groupOutputEvents(events) {
73
+ const blocks = [];
74
+ for (const line of events) {
75
+ if (line.kind === 'thinking') {
76
+ const last = blocks[blocks.length - 1];
77
+ if (last !== undefined && last.kind === 'thinking')
78
+ last.lines.push(line);
79
+ else
80
+ blocks.push({ kind: 'thinking', lines: [line] });
81
+ continue;
82
+ }
83
+ // Consecutive prose lines are ONE markdown document: without the fold a
84
+ // `**bold**` line, its list items and its paragraph break would render as
85
+ // raw syntax separated by blank rows. Activity pointers and tool/error
86
+ // rows stay independent single lines.
87
+ if (line.kind === 'text' && !isActivityLine(line.text)) {
88
+ const last = blocks[blocks.length - 1];
89
+ if (last !== undefined && last.kind === 'prose')
90
+ last.lines.push(line);
91
+ else
92
+ blocks.push({ kind: 'prose', lines: [line] });
93
+ continue;
94
+ }
95
+ blocks.push({ kind: 'line', line });
96
+ }
97
+ return blocks;
98
+ }
99
+ /** The child's own status line reaches us as a text delta (\`⏵ reading …\`);
100
+ * it is activity, not prose, so it renders as a dim pointer row. */
101
+ const ACTIVITY_GLYPHS = ['⏵', '▶', '▸', '»'];
102
+ function isActivityLine(text) {
103
+ return ACTIVITY_GLYPHS.includes(text.trimStart().slice(0, 1));
104
+ }
70
105
  /**
71
106
  * SubagentDetailScene — full-screen paged detail view for one subagent.
72
107
  * Header block (identity + stats) stays fixed; the body pages through
@@ -86,6 +121,27 @@ export function SubagentDetailScene({ subagent, onBack, onInterrupt, }) {
86
121
  const info = statusGlyph(subagent.status);
87
122
  const totalTokens = subagent.tokens?.total ?? ((subagent.tokens?.input ?? 0) + (subagent.tokens?.output ?? 0) || 0);
88
123
  const pageIndex = PAGES.indexOf(page);
124
+ /** Folded reasoning runs (the transcript's thinking grammar). Enter flips
125
+ * every run at once so one key stays predictable across thought steps. */
126
+ const [thinkingOpen, setThinkingOpen] = React.useState(false);
127
+ const blocks = groupOutputEvents(subagent.outputEvents);
128
+ const hasThinking = blocks.some(block => block.kind === 'thinking');
129
+ const settled = !isRunning;
130
+ // The deliverable is the LAST prose block: a `── Conclusion ──` rule goes in
131
+ // front of it once the run settles, so the answer is never the last line of
132
+ // a wall of reasoning.
133
+ const answerBlock = (() => {
134
+ if (!settled)
135
+ return -1;
136
+ for (let i = blocks.length - 1; i >= 0; i--) {
137
+ const block = blocks[i];
138
+ if (block.kind === 'prose' && block.lines.some(p => p.text.trim() !== ''))
139
+ return i;
140
+ if (block.kind === 'line' && block.line.kind === 'text' && block.line.text.trim() !== '')
141
+ return i;
142
+ }
143
+ return -1;
144
+ })();
89
145
  const turnPage = (delta) => {
90
146
  const next = (pageIndex + delta + PAGES.length) % PAGES.length;
91
147
  setPage(PAGES[next]);
@@ -94,6 +150,17 @@ export function SubagentDetailScene({ subagent, onBack, onInterrupt, }) {
94
150
  // tail -f: while the subagent runs and the output page is showing, follow
95
151
  // the newest streamed line. Page switches or settlement stop the follow so
96
152
  // manual ↑ scrolling wins.
153
+ // Expanding the reasoning grows the box past its viewport, and the renderer
154
+ // then treats the growth as "was at bottom" (maxScroll was 0 while the folded
155
+ // body fit) and re-pins the view to the bottom — pushing the fold header out
156
+ // of sight. Re-anchor AFTER that frame: the first immediate lands behind the
157
+ // renderer's own scheduling, and the second behind the re-pin frame it caused.
158
+ React.useEffect(() => {
159
+ if (page !== 'output')
160
+ return;
161
+ const first = setImmediate(() => scrollRef.current?.scrollTo?.(0));
162
+ return () => clearImmediate(first);
163
+ }, [thinkingOpen, page]);
97
164
  const outputLength = subagent.outputEvents.length;
98
165
  React.useEffect(() => {
99
166
  if (page !== 'output' || !isRunning)
@@ -133,7 +200,13 @@ export function SubagentDetailScene({ subagent, onBack, onInterrupt, }) {
133
200
  }
134
201
  if (isPlainReturnInput(input, key)) {
135
202
  event.stopImmediatePropagation();
136
- onBack();
203
+ // Enter folds the reasoning while the output page is showing (the
204
+ // transcript's ctrl+o equivalent). Elsewhere it keeps its "leave the
205
+ // detail" meaning, which Esc and the ✕ button still provide.
206
+ if (page === 'output' && hasThinking)
207
+ setThinkingOpen(open => !open);
208
+ else
209
+ onBack();
137
210
  return;
138
211
  }
139
212
  event.stopImmediatePropagation();
@@ -142,6 +215,31 @@ export function SubagentDetailScene({ subagent, onBack, onInterrupt, }) {
142
215
  const active = page === name;
143
216
  return (_jsxs(React.Fragment, { children: [_jsx(Box, { onClick: () => setPage(name), backgroundColor: !active ? 'userMessageBackgroundHover' : undefined, children: _jsx(Text, { color: active ? 'accent' : undefined, bold: active, inverse: active, children: ` ${label} ` }) }), _jsx(Text, { dimColor: true, children: name === PAGES[PAGES.length - 1] ? '' : '│' })] }, name));
144
217
  };
145
- return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: info.color, bold: true, children: info.glyph }), _jsx(Text, { bold: true, children: `${t('subagent-card-prefix')}${subagent.description}` }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { color: info.color, children: info.label }), _jsx(Box, { flexGrow: 1 }), _jsx(ExitButton, { onClick: onBack })] }), _jsxs(Text, { children: [_jsx(Text, { children: subagent.model ?? subagent.provider ?? 'default' }), _jsxs(Text, { dimColor: true, children: [elapsed !== undefined ? ` · ${formatDuration(elapsed)} · ` : ' · ', totalTokens || '—', " tok \u00B7 ", subagent.toolCalls.length, " tools"] })] }), _jsxs(Text, { dimColor: true, children: [`${t('subagent-started')} ${formatTimestamp(subagent.startedAt)}`
146
- + (subagent.completedAt ? ` · ${t('subagent-completed')} ${formatTimestamp(subagent.completedAt)}` : ''), ` · id ${subagent.agentId}`] }), subagent.error && (_jsx(Box, { marginTop: 0, children: _jsx(Text, { color: "error", wrap: "wrap", children: `${t('subagent-error-label')}: ${subagent.error}` }) })), _jsxs(Box, { flexDirection: "row", gap: 0, marginTop: 1, children: [tab('summary', t('subagent-tab-summary')), tab('output', t('subagent-output-label')), tab('tools', t('subagent-tools')), _jsx(Text, { dimColor: true, children: ` ${pageIndex + 1}/${PAGES.length}` })] }), _jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(20, Math.min(72, columns - 6))) }), _jsx(Box, { flexDirection: "column", paddingX: 1, maxHeight: Math.max(10, rows - 14), children: _jsxs(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, children: [page === 'summary' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatGrid, { subagent: subagent, totalTokens: totalTokens, elapsed: elapsed, statusLabel: info.label, statusColor: info.color }), subagent.summary && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, bold: true, children: '─ summary ' }), _jsx(Text, { wrap: "wrap", children: subagent.summary })] })), !subagent.summary && (_jsx(Text, { dimColor: true, children: isRunning ? t('subagent-no-output') : t('subagent-no-summary') }))] })), page === 'output' && (subagent.outputEvents.length === 0 && subagent.output.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-output') })) : (subagent.outputEvents.map((line, index) => (_jsxs(Text, { wrap: "wrap", dimColor: line.kind === 'thinking' || line.kind === 'system', color: line.kind === 'error' ? 'error' : undefined, children: [line.kind === 'thinking' ? ' ⌁ ' : ' ', line.text, !line.settled && isRunning ? ' ▍' : ''] }, index))))), page === 'tools' && (subagent.toolCalls.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-tools') })) : (subagent.toolCalls.map((tool, index) => (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: tool.status === 'failed' ? 'error' : tool.status === 'running' ? 'warning' : 'success', children: tool.status === 'running' ? '·' : tool.status === 'failed' ? '×' : '✓' }), _jsx(Text, { color: toolNameColor(tool.name), children: tool.name }), tool.endedAt && _jsx(Text, { dimColor: true, children: formatDuration(tool.endedAt - tool.startedAt) })] }), tool.argsPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(JsonArgsText, { raw: tool.argsPreview }) })), tool.resultPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: `⎿ ${tool.resultPreview}` }) })), tool.error && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { color: "error", wrap: "wrap", children: tool.error }) }))] }, tool.id ?? index)))))] }) }), _jsx(Divider, { color: "subtle", title: "" }), _jsxs(Box, { marginTop: 0, flexDirection: "row", children: [_jsx(Text, { dimColor: true, children: `←/→ ${t('subagent-hint-page')} · ↑/↓ ${t('subagent-hint-scroll')}` }), isRunning && onInterrupt && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Box, { onClick: () => onInterrupt(subagent.agentId), children: _jsx(Text, { dimColor: true, bold: true, color: "warning", children: "X interrupt" }) })] })), _jsx(Text, { dimColor: true, children: ` · Esc ${t('subagent-hint-back')}` })] })] }));
218
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: info.color, bold: true, children: info.glyph }), _jsx(Text, { bold: true, children: `${t('subagent-card-prefix')}${subagent.description}` }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { color: info.color, children: info.label }), subagent.mode === 'continuable' && _jsx(Text, { color: "warning", children: t('subagent-mode-continuable') }), subagent.mode === 'one-shot' && _jsx(Text, { dimColor: true, children: t('subagent-mode-one-shot') }), _jsx(Box, { flexGrow: 1 }), _jsx(ExitButton, { onClick: onBack })] }), _jsxs(Text, { children: [_jsx(Text, { children: subagent.model ?? subagent.provider ?? 'default' }), _jsxs(Text, { dimColor: true, children: [elapsed !== undefined ? ` · ${formatDuration(elapsed)} · ` : ' · ', totalTokens || '—', " tok \u00B7 ", subagent.toolCalls.length, " tools"] })] }), _jsxs(Text, { dimColor: true, children: [`${t('subagent-started')} ${formatTimestamp(subagent.startedAt)}`
219
+ + (subagent.completedAt ? ` · ${t('subagent-completed')} ${formatTimestamp(subagent.completedAt)}` : ''), ` · id ${subagent.agentId.slice(0, 8)}`] }), subagent.error && (_jsx(Box, { marginTop: 0, children: _jsx(Text, { color: "error", wrap: "wrap", children: `${t('subagent-error-label')}: ${subagent.error}` }) })), _jsxs(Box, { flexDirection: "row", gap: 0, marginTop: 1, children: [tab('summary', t('subagent-tab-summary')), tab('output', subagent.outputEvents.length > 0 ? `${t('subagent-output-label')} ${subagent.outputEvents.length}` : t('subagent-output-label')), tab('tools', subagent.toolCalls.length > 0 ? `${t('subagent-tools')} ${subagent.toolCalls.length}` : t('subagent-tools')), _jsx(Text, { dimColor: true, children: ` ${pageIndex + 1}/${PAGES.length}` })] }), _jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(20, Math.min(72, columns - 6))) }), _jsx(Box, { flexDirection: "column", paddingX: 1, maxHeight: Math.max(10, rows - 14), children: _jsxs(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, children: [page === 'summary' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatGrid, { subagent: subagent, totalTokens: totalTokens, elapsed: elapsed, statusLabel: info.label, statusColor: info.color }), subagent.summary && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, bold: true, children: '─ summary ' }), _jsx(Text, { wrap: "wrap", children: subagent.summary })] })), !subagent.summary && (_jsx(Text, { dimColor: true, children: isRunning ? t('subagent-no-output') : t('subagent-no-summary') }))] })), page === 'output' && (subagent.outputEvents.length === 0 && subagent.output.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-output') })) : (blocks.map((block, index) => {
220
+ if (block.kind === 'thinking') {
221
+ const text = block.lines.map(line => line.text).join('\n');
222
+ const chars = text.replace(/\s+/g, '').length;
223
+ const label = `${THINKING_SETTLED_MARKER} ${t('subagent-thinking-fold', { count: block.lines.length, chars })}`;
224
+ const hint = thinkingOpen ? t('subagent-thinking-collapse') : t('subagent-thinking-expand');
225
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { italic: true, dimColor: true, children: `${label} · ${hint}` }), thinkingOpen ? (_jsx(Box, { flexDirection: "column", paddingLeft: 2, children: block.lines.map((line, i) => (_jsx(Text, { dimColor: true, italic: true, wrap: "wrap", children: line.text }, i))) })) : (_jsx(Text, { dimColor: true, italic: true, wrap: "truncate-end", children: ` ${block.lines[0]?.text.replace(/\s+/g, ' ').trim() ?? ''}` }))] }, `think-${index}`));
226
+ }
227
+ if (block.kind === 'prose') {
228
+ const proseText = block.lines.map(p => p.text).join('\n');
229
+ const proseUnsettled = isRunning && block.lines.some(p => p.settled === false);
230
+ const isProseAnswer = index === answerBlock;
231
+ return (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [isProseAnswer && (_jsx(Text, { dimColor: true, children: `── ${t('subagent-conclusion')} ${'─'.repeat(Math.max(8, Math.min(60, columns - 16)))}` })), _jsx(Markdown, { dimColor: false, cacheTokens: true, children: proseText }), proseUnsettled && _jsx(Text, { dimColor: true, children: '▌' })] }, `prose-${index}`));
232
+ }
233
+ const line = block.line;
234
+ const unsettled = !line.settled && isRunning ? ' ▍' : '';
235
+ if (line.kind === 'text' && isActivityLine(line.text)) {
236
+ return (_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: "accent", children: '⏵' }), _jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [line.text.replace(/^[\s⏵▶▸»]+/, ''), unsettled] })] }, `act-${index}`));
237
+ }
238
+ const isAnswer = index === answerBlock;
239
+ // Tool / error / system rows get a leading row gap so a wall
240
+ // of streamed rows stops reading as one cramped paragraph.
241
+ const rowGap = line.kind === 'tool' || line.kind === 'error' || line.kind === 'system';
242
+ return (_jsxs(Box, { flexDirection: "column", marginTop: rowGap && index !== 0 ? 1 : 0, children: [isAnswer && (_jsx(Text, { dimColor: true, children: `── ${t('subagent-conclusion')} ${'─'.repeat(Math.max(8, Math.min(60, columns - 16)))}` })), _jsxs(Text, { wrap: "wrap", bold: isAnswer, dimColor: line.kind === 'system', color: line.kind === 'error' ? 'error' : line.kind === 'tool' ? 'accent' : undefined, children: [line.kind === 'tool' ? `● ${line.text}` : line.text, unsettled] })] }, `line-${index}`));
243
+ }))), page === 'tools' && (subagent.toolCalls.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-tools') })) : (subagent.toolCalls.map((tool, index) => (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: tool.status === 'failed' ? 'error' : tool.status === 'running' ? 'warning' : 'success', children: tool.status === 'running' ? '·' : tool.status === 'failed' ? '×' : '✓' }), _jsx(Text, { color: toolNameColor(tool.name), children: tool.name }), tool.endedAt && _jsx(Text, { dimColor: true, children: formatDuration(tool.endedAt - tool.startedAt) })] }), tool.argsPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(JsonArgsText, { raw: tool.argsPreview }) })), tool.resultPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: `⎿ ${tool.resultPreview}` }) })), tool.error && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { color: "error", wrap: "wrap", children: tool.error }) }))] }, tool.id ?? index)))))] }) }), _jsx(Divider, { color: "subtle", title: "" }), _jsxs(Box, { marginTop: 0, flexDirection: "row", children: [_jsx(Text, { dimColor: true, children: `←/→ ${t('subagent-hint-page')} · ↑/↓ ${t('subagent-hint-scroll')}`
244
+ + (page === 'output' && hasThinking ? ` · ${t('subagent-hint-fold')}` : '') }), isRunning && onInterrupt && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Box, { onClick: () => onInterrupt(subagent.agentId), children: _jsx(Text, { dimColor: true, bold: true, color: "warning", children: "X interrupt" }) })] })), _jsx(Text, { dimColor: true, children: ` · Esc ${t('subagent-hint-back')}` })] })] }));
147
245
  }
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import type { TerminalImageSource } from '../ink/terminal-image.js';
2
3
  /**
3
4
  * The maid portrait (鲸鱼娘) that can stand in for the header's pixel whale
4
5
  * (settings `dsh-tui.whaleGirl`): the author-designed 30×30 true-color
@@ -26,4 +27,9 @@ export declare const WHALE_GIRL_CENTER = 19.5;
26
27
  export declare function WhaleGirlArt({ width }: {
27
28
  width?: number;
28
29
  }): React.ReactNode;
30
+ /** The same heart-pose portrait used by the Star celebration, rendered as cells when image protocols are absent. */
31
+ export declare function WhaleGirlHappyArt({ source, width }: {
32
+ source?: TerminalImageSource;
33
+ width?: number;
34
+ }): React.ReactNode;
29
35
  //# sourceMappingURL=WhaleGirl.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WhaleGirl.d.ts","sourceRoot":"","sources":["../../../src/components/WhaleGirl.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB;;;;;;;;GAQG;AAEH,4DAA4D;AAC5D,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAI5C,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAA;AAErC;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAW3E"}
1
+ {"version":3,"file":"WhaleGirl.d.ts","sourceRoot":"","sources":["../../../src/components/WhaleGirl.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAEnE;;;;;;;;GAQG;AAEH,4DAA4D;AAC5D,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAI5C,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAA;AAErC;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAW3E;AAED,oHAAoH;AACpH,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,KAAU,EAAE,EAAE;IACxD,MAAM,CAAC,EAAE,mBAAmB,CAAA;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,GAAG,KAAK,CAAC,SAAS,CAoBlB"}
@@ -1,4 +1,5 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
2
3
  import { Box, Text } from '../ui.js';
3
4
  import { renderCellRows } from './Whale.js';
4
5
  import { WHALE_GIRL_COLS, WHALE_GIRL_SPRITE, WHALE_GIRL_SPRITE_ROWS } from './whaleGirlSprite.js';
@@ -30,3 +31,20 @@ export function WhaleGirlArt({ width }) {
30
31
  const pad = width === undefined ? 0 : Math.max(0, Math.floor((width - WHALE_GIRL_COLS) / 2));
31
32
  return (_jsx(Box, { flexDirection: "column", flexShrink: 0, width: width, children: WHALE_GIRL_ROWS.map((row, index) => (_jsxs(Text, { wrap: "truncate-end", children: [pad > 0 ? ' '.repeat(pad) : '', row] }, index))) }));
32
33
  }
34
+ /** The same heart-pose portrait used by the Star celebration, rendered as cells when image protocols are absent. */
35
+ export function WhaleGirlHappyArt({ source, width = 40 }) {
36
+ const rows = React.useMemo(() => {
37
+ if (source === undefined)
38
+ return WHALE_GIRL_ROWS;
39
+ return renderCellRows(WHALE_GIRL_COLS, 32, (x, y) => {
40
+ const sx = Math.min(source.width - 1, Math.floor((x + 0.5) * source.width / WHALE_GIRL_COLS));
41
+ const sy = Math.min(source.height - 1, Math.floor((y + 0.5) * source.height / 32));
42
+ const at = (sy * source.width + sx) * 4;
43
+ if ((source.data[at + 3] ?? 0) < 80)
44
+ return undefined;
45
+ return [source.data[at] ?? 0, source.data[at + 1] ?? 0, source.data[at + 2] ?? 0];
46
+ });
47
+ }, [source]);
48
+ const pad = Math.max(0, Math.floor((width - WHALE_GIRL_COLS) / 2));
49
+ return (_jsxs(Box, { flexDirection: "column", flexShrink: 0, width: width, children: [source === undefined && _jsx(Text, { color: "#f477a7", wrap: "truncate-end", children: ' ♥ ♥' }), rows.map((row, index) => (_jsxs(Text, { wrap: "truncate-end", children: [' '.repeat(pad), row] }, index)))] }));
50
+ }
@@ -8,7 +8,7 @@ import { formatDuration } from '../../terminal-utils/format.js';
8
8
  import { THINKING_SPINNER_FRAMES, THINKING_SPINNER_INTERVAL_MS, THINKING_SETTLED_MARKER, } from '../../terminal-utils/figures.js';
9
9
  import { BRAND, ICE } from '../shimmer.js';
10
10
  import { interpolateColor } from '../Spinner/spinnerUtils.js';
11
- import { isMinimalMode } from '../../minimalMode.js';
11
+ import { isMinimalUiMode } from '../../minimalUiMode.js';
12
12
  import { primaryComboString } from '../../utils/keymap.js';
13
13
  /** Preview body rows — a FIXED row count (kimicode-style constant-height
14
14
  * ticker). Ink's truncate slices the whole string across newlines as one
@@ -48,10 +48,10 @@ export function AssistantThinkingMessage({ thinking, textFull, marginTopOnTurn,
48
48
  : '';
49
49
  // Kimi Code style blue pulse: the streaming glyph breathes along the
50
50
  // header's brand→ice ladder, one sine period per ~7 frames (≈0.56s) —
51
- // lively without strobing. Minimal mode drops the color (plain glyph);
51
+ // lively without strobing. The minimal UI drops the color (plain glyph);
52
52
  // settled always keeps the plain dim anchor.
53
53
  const label = `${t('thinking-label')}${duration}${streaming ? '…' : ` ${t('hint-expand-ctrl-o', { key: primaryComboString('transcript') })}`}`;
54
- const minimal = isMinimalMode();
54
+ const minimalUi = isMinimalUiMode();
55
55
  const pulse = (Math.sin(frame * 0.9) + 1) / 2;
56
56
  const pulseColor = interpolateColor(BRAND, ICE, pulse);
57
57
  const frameText = THINKING_SPINNER_FRAMES[frame % THINKING_SPINNER_FRAMES.length];
@@ -61,7 +61,7 @@ export function AssistantThinkingMessage({ thinking, textFull, marginTopOnTurn,
61
61
  const hoverProps = onClick !== undefined
62
62
  ? { onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false) }
63
63
  : {};
64
- const header = streaming ? (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { children: minimal ? frameText : chalk.rgb(pulseColor.r, pulseColor.g, pulseColor.b).bold(frameText) }), _jsx(Text, { dimColor: !hovered, color: hovered ? 'text' : undefined, italic: true, children: ` ${label}` })] })) : (_jsx(Text, { italic: true, dimColor: !hovered, color: hovered ? 'text' : undefined, children: `${minimal ? '*' : THINKING_SETTLED_MARKER} ${label}` }));
64
+ const header = streaming ? (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { children: minimalUi ? frameText : chalk.rgb(pulseColor.r, pulseColor.g, pulseColor.b).bold(frameText) }), _jsx(Text, { dimColor: !hovered, color: hovered ? 'text' : undefined, italic: true, children: ` ${label}` })] })) : (_jsx(Text, { italic: true, dimColor: !hovered, color: hovered ? 'text' : undefined, children: `${minimalUi ? '*' : THINKING_SETTLED_MARKER} ${label}` }));
65
65
  if (preview) {
66
66
  // Live ticker: the model's last few reasoning lines, dimmed, one Text
67
67
  // per row so each truncates to the width independently, padded to a
@@ -11,7 +11,7 @@ import type { WaveBand } from '../../dsh-adapter/types.js';
11
11
  * @returns Glyph count, or 0 when the strip should not render at all.
12
12
  */
13
13
  export declare function miniWakeWidth(columns: number): number;
14
- export declare function MiniWake({ band, hint, tick, }: {
14
+ export declare function MiniWake({ band, hint, tick, onOpen, hoverHint, }: {
15
15
  /** The session projected onto {@link miniWakeWidth} columns. */
16
16
  band: WaveBand;
17
17
  /**
@@ -20,5 +20,15 @@ export declare function MiniWake({ band, hint, tick, }: {
20
20
  */
21
21
  hint?: string;
22
22
  tick: number;
23
+ /**
24
+ * Open the trajectory scene. The strip is the conversation's permanent
25
+ * pointer at that second view, so pointing AT it and clicking is the
26
+ * gesture the affordance implies — the key chord stays as the alternative,
27
+ * not the only way in.
28
+ */
29
+ onOpen?: () => void;
30
+ /** Key chord revealed while the pointer rests on the strip (the first-run
31
+ * hint has already retired by then). */
32
+ hoverHint?: string;
23
33
  }): React.ReactNode;
24
34
  //# sourceMappingURL=MiniWake.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MiniWake.d.ts","sourceRoot":"","sources":["../../../../src/components/trajectory/MiniWake.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,4BAA4B,CAAA;AAwCvE;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,IAAI,EACJ,IAAI,GACL,EAAE;IACD,gEAAgE;IAChE,IAAI,EAAE,QAAQ,CAAA;IACd;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACb,GAAG,KAAK,CAAC,SAAS,CAqClB"}
1
+ {"version":3,"file":"MiniWake.d.ts","sourceRoot":"","sources":["../../../../src/components/trajectory/MiniWake.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,4BAA4B,CAAA;AAwCvE;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,SAAS,GACV,EAAE;IACD,gEAAgE;IAChE,IAAI,EAAE,QAAQ,CAAA;IACd;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB;6CACyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GAAG,KAAK,CAAC,SAAS,CAmDlB"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
2
3
  import chalk from 'chalk';
3
- import { Box, Text, useTheme } from '../../ui.js';
4
+ import { Box, NoSelect, Text, useTheme } from '../../ui.js';
4
5
  import { getTheme } from '../../theme.js';
5
6
  import { alive, mix } from '../../trajectory/motion.js';
6
7
  import { parseRGB } from '../Spinner/spinnerUtils.js';
@@ -59,9 +60,10 @@ export function miniWakeWidth(columns) {
59
60
  return 8;
60
61
  return 0;
61
62
  }
62
- export function MiniWake({ band, hint, tick, }) {
63
+ export function MiniWake({ band, hint, tick, onOpen, hoverHint, }) {
63
64
  const [themeName] = useTheme();
64
65
  const theme = getTheme(themeName);
66
+ const [hovered, setHovered] = React.useState(false);
65
67
  if (band.buckets.length === 0)
66
68
  return null;
67
69
  const logFloor = Math.log1p(Math.max(0, band.floor));
@@ -85,7 +87,11 @@ export function MiniWake({ band, hint, tick, }) {
85
87
  const colour = failed ? theme.error : channelColor(dominantChannel(bucket), theme);
86
88
  strip += chalk.hex(toHex(colour))(shown >= BLOCKS.length ? FULL : BLOCKS[shown - 1]);
87
89
  }
88
- return (_jsxs(Box, { flexShrink: 0, flexDirection: "row", gap: 1, children: [_jsx(Text, { children: strip }), hint !== undefined ? _jsx(Text, { color: "subtle", children: hint }) : null] }));
90
+ // The hint slot narrates the affordance: first-run chord, then — while the
91
+ // pointer is on the strip — the chord again, because that is the moment the
92
+ // user is asking "what does this thing do".
93
+ const shownHint = hint ?? (hovered && onOpen !== undefined ? hoverHint : undefined);
94
+ return (_jsx(NoSelect, { children: _jsxs(Box, { flexShrink: 0, flexDirection: "row", gap: 1, onClick: onOpen, onMouseEnter: onOpen !== undefined ? () => setHovered(true) : undefined, onMouseLeave: onOpen !== undefined ? () => setHovered(false) : undefined, children: [_jsx(Text, { children: strip }), shownHint !== undefined ? _jsx(Text, { color: hovered && hint === undefined ? 'accent' : 'subtle', children: shownHint }) : null] }) }));
89
95
  }
90
96
  /** Theme colour → `#rrggbb`, the form chalk needs. */
91
97
  function toHex(colour) {
@@ -1 +1 @@
1
- {"version":3,"file":"external-commands.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/external-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,KAAK,EAAoB,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAKjF,OAAO,EAAkD,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1G,OAAO,KAAK,EAAqB,gBAAgB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAcjH;0DAC0D;AAC1D,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE;IACJ,cAAc,EAAE,cAAc,GAAG,SAAS,CAAA;IAC1C,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAA;IACpD,KAAK,IAAI,KAAK,CAAA;IACd,OAAO,IAAI,OAAO,CAAA;IAClB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAA;IACzC,MAAM,CAAC,OAAO,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3G,QAAQ,EAAE,cAAc,CAAA;IACxB,WAAW,IAAI,kBAAkB,GAAG,SAAS,CAAA;IAC7C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CACtG;mBA+HO,MAAM,YACF,MAAM,cACL,SAAS,gBAAgB,EAAE,KACrC,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC;uBA2EtC,MAAM,YACF,MAAM,cACL,SAAS,gBAAgB,EAAE,KACrC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;EAG/B"}
1
+ {"version":3,"file":"external-commands.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/external-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,KAAK,EAAoB,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAKjF,OAAO,EAAkD,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1G,OAAO,KAAK,EAAqB,gBAAgB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAcjH;0DAC0D;AAC1D,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE;IACJ,cAAc,EAAE,cAAc,GAAG,SAAS,CAAA;IAC1C,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAA;IACpD,KAAK,IAAI,KAAK,CAAA;IACd,OAAO,IAAI,OAAO,CAAA;IAClB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAA;IACzC,MAAM,CAAC,OAAO,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3G,QAAQ,EAAE,cAAc,CAAA;IACxB,WAAW,IAAI,kBAAkB,GAAG,SAAS,CAAA;IAC7C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CACtG;mBA+HO,MAAM,YACF,MAAM,cACL,SAAS,gBAAgB,EAAE,KACrC,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC;uBAiFtC,MAAM,YACF,MAAM,cACL,SAAS,gBAAgB,EAAE,KACrC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;EAG/B"}
@@ -111,8 +111,6 @@ export function createExternalCommandInvoker(ctx, deps) {
111
111
  /** Run one DSH registry command (`/plan`, …) on the live agent while
112
112
  * preserving its settled result kind for composer admission. */
113
113
  const invoke = async (name, rawInput, imageRefs = []) => {
114
- const capture = deps.capture();
115
- assertCapabilityShadowPolicy('host.commands.invoke', deps.runtime.mode, deps.runtime.slices);
116
114
  const service = deps.commandService;
117
115
  if (service === undefined)
118
116
  return undefined;
@@ -123,6 +121,14 @@ export function createExternalCommandInvoker(ctx, deps) {
123
121
  // apply another scope's owner policy.
124
122
  const definition = service.find(commandAgent, name);
125
123
  try {
124
+ // The channel-lifetime capture and the shadow/capability policy check
125
+ // live INSIDE the try: both can throw (a lease that ended between
126
+ // keystrokes, a shadow-mode denial), and `invoke` is an async action
127
+ // whose callers treat a rejection as unhandled — the same refusal must
128
+ // degrade to the settled `{ kind: 'error' }` outcome every other
129
+ // failure below uses.
130
+ const capture = deps.capture();
131
+ assertCapabilityShadowPolicy('host.commands.invoke', deps.runtime.mode, deps.runtime.slices);
126
132
  const signal = new AbortController().signal;
127
133
  const line = `/${name}${rawInput}`;
128
134
  const batch = await registryCommandImages(service, line, deps.composer.includeLegacyImageRefs(line, imageRefs), stagedSnapshot, signal);
@@ -24,5 +24,6 @@ export declare function createJobProjection(getState: () => Pick<ChannelState, '
24
24
  attach: (jobs: JobsRuntime | undefined, ownService?: (dispose: () => void) => void) => void;
25
25
  dropRows: () => void;
26
26
  reset: () => void;
27
+ reanchor: () => void;
27
28
  };
28
29
  //# sourceMappingURL=job-projection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"job-projection.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/job-projection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAGnD,OAAO,EAAE,kBAAkB,EAAqB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAA;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAW,UAAU,EAAE,MAAM,YAAY,CAAA;AAEnE;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,IAAI,CAAC,YAAY,EAAE,gBAAgB,GAAG,MAAM,GAAG,MAAM,CAAC,EACtE,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,CAAC,CAAA;IAC5C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAA;IACxG,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IACzB,KAAK,IAAI,KAAK,CAAA;IACd,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;;;mBA4EqB,WAAW,GAAG,SAAS,eAAe,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,IAAI,KAAG,IAAI;oBAwD3E,IAAI;iBACP,IAAI;EAGvB"}
1
+ {"version":3,"file":"job-projection.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/job-projection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAGnD,OAAO,EAAE,kBAAkB,EAAqB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAA;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAW,UAAU,EAAE,MAAM,YAAY,CAAA;AAEnE;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,IAAI,CAAC,YAAY,EAAE,gBAAgB,GAAG,MAAM,GAAG,MAAM,CAAC,EACtE,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,CAAC,CAAA;IAC5C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAA;IACxG,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IACzB,KAAK,IAAI,KAAK,CAAA;IACd,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;;;mBA4FqB,WAAW,GAAG,SAAS,eAAe,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,IAAI,KAAG,IAAI;oBA4G3E,IAAI;iBACP,IAAI;oBAQD,IAAI;EAG1B"}
@@ -9,6 +9,8 @@ import { BackgroundJobStore, formatJobDuration } from '../jobs.js';
9
9
  export function createJobProjection(getState, deps) {
10
10
  const jobRowsByJobId = new Map();
11
11
  let jobsRuntime;
12
+ /** The live attachment's conditional roster re-read (see `reanchor`). */
13
+ let reanchorActive;
12
14
  let detachActive;
13
15
  let attachmentToken;
14
16
  let attachmentCurrent = () => false;
@@ -30,6 +32,7 @@ export function createJobProjection(getState, deps) {
30
32
  label: job.label,
31
33
  status: job.status,
32
34
  ...(job.detail === undefined ? {} : { detail: job.detail }),
35
+ ...(job.progress === undefined ? {} : { progress: job.progress }),
33
36
  startedAt: job.startedAt,
34
37
  ...(job.finishedAt === undefined ? {} : { finishedAt: job.finishedAt }),
35
38
  outputLines: job.outputLines,
@@ -63,7 +66,10 @@ export function createJobProjection(getState, deps) {
63
66
  return false;
64
67
  const job = store.get(id);
65
68
  try {
66
- jobs.kill(id, deps.agent(), 'dsh-tui /jobs panel');
69
+ // The kernel fence compares job.owner.id === caller, so the caller
70
+ // MUST be the session id string — the Agent object matches nothing
71
+ // and every owned-job kill would throw "another session".
72
+ jobs.kill(id, sessionCaller(), 'dsh-tui /jobs panel');
67
73
  }
68
74
  catch {
69
75
  return false;
@@ -74,6 +80,15 @@ export function createJobProjection(getState, deps) {
74
80
  return true;
75
81
  },
76
82
  };
83
+ /**
84
+ * Caller identity for the kernel fence: the owning session id string
85
+ * (`Agent.id`). The registry compares `job.owner.id === caller`, so the
86
+ * Agent object the channel holds is only good for extracting the id.
87
+ */
88
+ const sessionCaller = () => {
89
+ const agent = deps.agent();
90
+ return agent?.id;
91
+ };
77
92
  /**
78
93
  * Each service attachment has one idempotent disposer, dual-owned by the
79
94
  * Channel and (when injected) the service context. Reattachment revokes the
@@ -88,19 +103,53 @@ export function createJobProjection(getState, deps) {
88
103
  let detached = false;
89
104
  let detach;
90
105
  const current = () => !detached && attachmentToken === token && detachActive === detach && jobsRuntime === jobs && deps.owner.current();
106
+ // The caller the roster was last read with. `reanchor` compares against it
107
+ // so a bind that did NOT change the session stays a no-op — mounting binds
108
+ // the channel's own agent immediately after the service attaches, and
109
+ // re-reading there would be a second, redundant list().
110
+ let lastCaller;
111
+ let callerKnown = false;
91
112
  const refresh = () => {
92
113
  // Check before list(): retained callbacks must not touch a revoked or
93
114
  // replaced service, nor invoke any store/row work after owner disposal.
94
115
  if (!current())
95
116
  return;
96
117
  try {
97
- const snapshot = jobs.list(deps.agent());
118
+ const caller = sessionCaller();
119
+ lastCaller = caller;
120
+ callerKnown = true;
121
+ const snapshot = jobs.list(caller);
98
122
  if (!current())
99
123
  return;
100
124
  store.replace(snapshot);
101
125
  }
102
126
  catch { /* optional service is disposing */ }
103
127
  };
128
+ /** Re-read only when the bound session actually changed. */
129
+ const reanchorThis = () => {
130
+ if (callerKnown && sessionCaller() === lastCaller)
131
+ return;
132
+ dropRows();
133
+ store.reset();
134
+ refresh();
135
+ };
136
+ /** One kernel `output` event: pull the ring increment past our cursor. */
137
+ const pullOutput = (id) => {
138
+ if (!current())
139
+ return;
140
+ if (jobs.readAt === undefined)
141
+ return;
142
+ const cursor = store.kernelCursorOf(id);
143
+ if (cursor === undefined)
144
+ return;
145
+ try {
146
+ const read = jobs.readAt(id, cursor, sessionCaller());
147
+ if (!current())
148
+ return;
149
+ store.onKernelOutput(id, read);
150
+ }
151
+ catch { /* job gone or fenced mid-pull; roster refresh follows */ }
152
+ };
104
153
  // Publish the attachment identity before subscription: registries are
105
154
  // allowed to synchronously deliver their current snapshot from on*().
106
155
  // Every registration below is transactional because a second on*() can
@@ -118,6 +167,8 @@ export function createJobProjection(getState, deps) {
118
167
  }
119
168
  if (jobsRuntime === jobs)
120
169
  jobsRuntime = undefined;
170
+ if (reanchorActive === reanchorThis)
171
+ reanchorActive = undefined;
121
172
  for (const dispose of disposers.splice(0))
122
173
  dispose?.();
123
174
  // A service-context detach happens before channel teardown on remount;
@@ -130,7 +181,34 @@ export function createJobProjection(getState, deps) {
130
181
  detachActive = detach;
131
182
  attachmentToken = token;
132
183
  attachmentCurrent = current;
184
+ reanchorActive = reanchorThis;
133
185
  try {
186
+ const bus = jobs.events;
187
+ if (bus !== undefined && typeof bus.subscribe === 'function') {
188
+ // Kernel bus: lifecycle commits re-read the roster; output events
189
+ // pull non-consuming readAt increments with the store's own cursor.
190
+ // Subscribe to EVERY owner on purpose: this filter is captured at
191
+ // attach, but the channel still rebinds afterwards (dsh-tui opens a
192
+ // fresh session at boot and only then resumes the user's), and a filter
193
+ // frozen to the boot session starves the panel for good — the kernel
194
+ // drops every foreign-owner event, refresh() never runs again and the
195
+ // roster stays at the empty list read during attach. Nothing extra is
196
+ // exposed by widening it: the roster read is list(caller) and the ring
197
+ // pull is readAt(id, cursor, caller), both fenced with the caller AT
198
+ // CALL TIME, so another session's jobs fall out (and a foreign ring
199
+ // read throws into the contained catch).
200
+ disposers.push(bus.subscribe({ owners: 'all' }, event => {
201
+ if (!current())
202
+ return;
203
+ try {
204
+ if (event.type === 'output')
205
+ pullOutput(event.id);
206
+ else
207
+ refresh();
208
+ }
209
+ catch { /* contained per-event; the next event re-syncs */ }
210
+ }));
211
+ }
134
212
  if (typeof jobs.onJobsChanged === 'function')
135
213
  disposers.push(jobs.onJobsChanged(refresh));
136
214
  if (typeof jobs.onJobDone === 'function')
@@ -146,5 +224,12 @@ export function createJobProjection(getState, deps) {
146
224
  };
147
225
  const dropRows = () => { jobRowsByJobId.clear(); };
148
226
  const reset = () => { dropRows(); store.reset(); };
149
- return { store, control, attach, dropRows, reset };
227
+ /**
228
+ * Re-anchor after the live agent changed. `reset()` runs while the OLD
229
+ * binding is still installed (resetSessionProjection precedes bindAgent), so
230
+ * it can only clear; the re-read has to happen here, once the new agent is
231
+ * bound, or the next session's jobs would only appear on its first event.
232
+ */
233
+ const reanchor = () => { reanchorActive?.(); };
234
+ return { store, control, attach, dropRows, reset, reanchor };
150
235
  }
@@ -7,6 +7,19 @@ import type { ChannelState } from './types.js';
7
7
  import type { createChannelBinding } from './binding.js';
8
8
  type Binding = ReturnType<typeof createChannelBinding>;
9
9
  type ModeState = Pick<ChannelState, 'mode' | 'modeIndex' | 'emit'>;
10
+ /**
11
+ * Fail-soft reporting for a session-mode write. The mutation path reaches
12
+ * kernel-owned state (`session.append`, `approval.setPolicy` → the agent
13
+ * inbox) that can legitimately refuse while a turn is publishing, and the
14
+ * keyboard entry point (`Shift+Tab`) cannot await it — a rejection there
15
+ * becomes an unhandledRejection, which the process guard treats as fatal and
16
+ * which kills the TUI with the session still open. A refused switch is a
17
+ * user-visible outcome, not a crash: log it, say so in the status line, and
18
+ * re-derive the indicator from the durable log so the UI cannot keep showing
19
+ * a mode the session never entered. Shared by the base actions and the
20
+ * permission-aware composition so both report the same way.
21
+ */
22
+ export declare function reportModeSwitchFailure(ctx: Context, notify: ChannelState['notify'], refreshMode: () => void, error: unknown): void;
10
23
  /** Durable session-mode folds and transitions. Construction is inert; root wires it after state construction. */
11
24
  export declare function createModeActions(ctx: Context, state: ModeState, deps: {
12
25
  owner: {
@@ -1 +1 @@
1
- {"version":3,"file":"mode-actions.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/mode-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAE5D,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC7E,OAAO,EAAsB,KAAK,qBAAqB,EAAE,MAAM,iCAAiC,CAAA;AAChG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAExD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,SAAS,GAAG,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC,CAAA;AAElE,iHAAiH;AACjH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE;IACJ,KAAK,EAAE;QAAE,OAAO,IAAI,OAAO,CAAA;KAAE,CAAA;IAC7B,OAAO,EAAE,qBAAqB,CAAA;IAC9B,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,CAAC,CAAA;IACzD,YAAY,EAAE,SAAS,eAAe,EAAE,CAAA;IACxC,cAAc,CAAC,EAAE;QAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;IAC9D,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IAChF,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;CAC/B;uBAyDqB,IAAI;qBAkJE,OAAO,CAAC,IAAI,CAAC;sBAlEV,eAAe,sDAAgC,OAAO,CAAC,IAAI,CAAC;8BAyE1D,KAAK,CAAC,SAAS,CAAC,SAAS,YAAY,KAAG,IAAI;EA4B9E"}
1
+ {"version":3,"file":"mode-actions.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/mode-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAE5D,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC7E,OAAO,EAAsB,KAAK,qBAAqB,EAAE,MAAM,iCAAiC,CAAA;AAChG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAExD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,SAAS,GAAG,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC,CAAA;AAElE;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,EAC9B,WAAW,EAAE,MAAM,IAAI,EACvB,KAAK,EAAE,OAAO,GACb,IAAI,CAUN;AAED,iHAAiH;AACjH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE;IACJ,KAAK,EAAE;QAAE,OAAO,IAAI,OAAO,CAAA;KAAE,CAAA;IAC7B,OAAO,EAAE,qBAAqB,CAAA;IAC9B,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,CAAC,CAAA;IACzD,YAAY,EAAE,SAAS,eAAe,EAAE,CAAA;IACxC,cAAc,CAAC,EAAE;QAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;IAC9D,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IAChF,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;CAC/B;uBAyDqB,IAAI;qBA4JE,OAAO,CAAC,IAAI,CAAC;sBA1EV,eAAe,sDAA0B,OAAO,CAAC,IAAI,CAAC;8BAuFpD,KAAK,CAAC,SAAS,CAAC,SAAS,YAAY,KAAG,IAAI;EA4B9E"}