@ashx-j/lunr 0.2.7 → 0.2.8

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 (59) hide show
  1. package/dist/builtin-extensions/pi-subagents/src/extension/control-notices.d.ts.map +1 -1
  2. package/dist/builtin-extensions/pi-subagents/src/extension/control-notices.js +2 -2
  3. package/dist/builtin-extensions/pi-subagents/src/extension/control-notices.js.map +1 -1
  4. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  5. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +1 -7
  6. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  7. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  8. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +21 -0
  9. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  10. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +1 -0
  11. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
  12. package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
  13. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +3 -0
  14. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  15. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +9 -11
  16. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  17. package/dist/catalog/models.json +1 -1
  18. package/dist/catalog/providers/github-copilot.json +1 -1
  19. package/dist/catalog/providers/google-vertex.json +1 -1
  20. package/dist/catalog/providers/google.json +1 -1
  21. package/dist/catalog/providers/nvidia.json +1 -1
  22. package/dist/catalog/providers/opencode-go.json +1 -1
  23. package/dist/catalog/providers/opencode.json +1 -1
  24. package/dist/catalog/providers/openrouter.json +1 -1
  25. package/dist/catalog/providers/vercel-ai-gateway.json +1 -1
  26. package/dist/catalog/publication.json +3 -3
  27. package/dist/core/rollback.d.ts +1 -1
  28. package/dist/core/rollback.d.ts.map +1 -1
  29. package/dist/core/rollback.js +1 -1
  30. package/dist/core/rollback.js.map +1 -1
  31. package/dist/core/slash-commands.d.ts.map +1 -1
  32. package/dist/core/slash-commands.js +2 -1
  33. package/dist/core/slash-commands.js.map +1 -1
  34. package/dist/modes/interactive/components/assistant-message.d.ts +6 -1
  35. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  36. package/dist/modes/interactive/components/assistant-message.js +92 -64
  37. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  38. package/dist/modes/interactive/components/thinking-summary.d.ts +4 -4
  39. package/dist/modes/interactive/components/thinking-summary.d.ts.map +1 -1
  40. package/dist/modes/interactive/components/thinking-summary.js +7 -5
  41. package/dist/modes/interactive/components/thinking-summary.js.map +1 -1
  42. package/dist/modes/interactive/components/thinking-tail.d.ts.map +1 -1
  43. package/dist/modes/interactive/components/thinking-tail.js +5 -3
  44. package/dist/modes/interactive/components/thinking-tail.js.map +1 -1
  45. package/dist/modes/interactive/interactive-mode.d.ts +2 -0
  46. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  47. package/dist/modes/interactive/interactive-mode.js +42 -40
  48. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  49. package/docs/providers.md +2 -0
  50. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  51. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  52. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  53. package/examples/extensions/gondolin/package-lock.json +2 -2
  54. package/examples/extensions/gondolin/package.json +1 -1
  55. package/examples/extensions/sandbox/package-lock.json +2 -2
  56. package/examples/extensions/sandbox/package.json +1 -1
  57. package/examples/extensions/with-deps/package-lock.json +2 -2
  58. package/examples/extensions/with-deps/package.json +1 -1
  59. package/package.json +4 -4
@@ -27,6 +27,7 @@ export class AssistantMessageComponent extends Container {
27
27
  hiddenThinkingLabel;
28
28
  outputPad;
29
29
  lastMessage;
30
+ thinkingSource;
30
31
  hasToolCalls = false;
31
32
  // lunr: gutter rail — prefix rendered lines with a dim │; the last line uses ╰
32
33
  // when this component closes the turn (no tool calls follow).
@@ -49,20 +50,20 @@ export class AssistantMessageComponent extends Container {
49
50
  invalidate() {
50
51
  super.invalidate();
51
52
  if (this.lastMessage) {
52
- this.updateContent(this.lastMessage);
53
+ this.updateContent(this.lastMessage, this.thinkingSourceOptions());
53
54
  }
54
55
  }
55
56
  setHideThinkingBlock(hide) {
56
57
  this.hideThinkingBlock = hide;
57
58
  if (this.lastMessage) {
58
- this.updateContent(this.lastMessage);
59
+ this.updateContent(this.lastMessage, this.thinkingSourceOptions());
59
60
  }
60
61
  }
61
62
  // lunr: collapsible reasoning toggles (live; re-render like setHideThinkingBlock).
62
63
  setThinkingCollapse(collapse) {
63
64
  this.thinkingCollapse = collapse;
64
65
  if (this.lastMessage) {
65
- this.updateContent(this.lastMessage);
66
+ this.updateContent(this.lastMessage, this.thinkingSourceOptions());
66
67
  }
67
68
  }
68
69
  // lunr: ctrl+o expansion — Expandable interface hook; expanded collapsed thinking
@@ -70,7 +71,7 @@ export class AssistantMessageComponent extends Container {
70
71
  setExpanded(expanded) {
71
72
  this.expanded = expanded;
72
73
  if (this.lastMessage) {
73
- this.updateContent(this.lastMessage);
74
+ this.updateContent(this.lastMessage, this.thinkingSourceOptions());
74
75
  }
75
76
  }
76
77
  setThinkingTimings(timings) {
@@ -83,13 +84,13 @@ export class AssistantMessageComponent extends Container {
83
84
  setHiddenThinkingLabel(label) {
84
85
  this.hiddenThinkingLabel = label;
85
86
  if (this.lastMessage) {
86
- this.updateContent(this.lastMessage);
87
+ this.updateContent(this.lastMessage, this.thinkingSourceOptions());
87
88
  }
88
89
  }
89
90
  setOutputPad(padding) {
90
91
  this.outputPad = padding;
91
92
  if (this.lastMessage) {
92
- this.updateContent(this.lastMessage);
93
+ this.updateContent(this.lastMessage, this.thinkingSourceOptions());
93
94
  }
94
95
  }
95
96
  render(width) {
@@ -120,17 +121,25 @@ export class AssistantMessageComponent extends Container {
120
121
  }
121
122
  return lines;
122
123
  }
123
- updateContent(message) {
124
+ thinkingSourceOptions() {
125
+ return this.thinkingSource ? { thinkingSource: this.thinkingSource } : undefined;
126
+ }
127
+ updateContent(message, options) {
124
128
  this.lastMessage = message;
129
+ this.thinkingSource = options?.thinkingSource;
125
130
  // Clear content container
126
131
  this.contentContainer.clear();
127
- const hasVisibleContent = message.content.some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()));
132
+ const sourceMessage = this.thinkingSource ?? message;
133
+ const sourceThinkingRuns = collectThinkingRuns(sourceMessage.content);
134
+ const hasVisibleContent = message.content.some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim())) ||
135
+ (!this.hideThinkingBlock && sourceThinkingRuns.some((run) => run.length > 0));
128
136
  if (hasVisibleContent) {
129
137
  this.contentContainer.addChild(new Spacer(1));
130
138
  }
131
139
  // Render content in order
132
140
  let isFirstTextBlock = true;
133
141
  let thinkingRunIndex = -1;
142
+ let renderedThinking = false;
134
143
  for (let i = 0; i < message.content.length; i++) {
135
144
  const content = message.content[i];
136
145
  if (content.type === "text" && content.text.trim()) {
@@ -149,7 +158,7 @@ export class AssistantMessageComponent extends Container {
149
158
  }
150
159
  else if (content.type === "thinking") {
151
160
  thinkingRunIndex++;
152
- const thinkingBlocks = [];
161
+ const displayBlocks = [];
153
162
  for (; i < message.content.length; i++) {
154
163
  const thinkingContent = message.content[i];
155
164
  if (thinkingContent.type !== "thinking") {
@@ -157,66 +166,23 @@ export class AssistantMessageComponent extends Container {
157
166
  }
158
167
  const thinking = thinkingContent.thinking.trim();
159
168
  if (thinking) {
160
- thinkingBlocks.push(thinking);
169
+ displayBlocks.push(thinking);
161
170
  }
162
171
  }
163
172
  i--;
164
- if (thinkingBlocks.length === 0) {
173
+ const thinkingBlocks = sourceThinkingRuns[thinkingRunIndex] ?? displayBlocks;
174
+ if (thinkingBlocks.length === 0 || this.hideThinkingBlock) {
165
175
  continue;
166
176
  }
167
- // Add spacing only when another visible assistant content block follows.
168
- // This avoids a superfluous blank line before separately-rendered tool execution blocks.
169
- const hasVisibleContentAfter = message.content
170
- .slice(i + 1)
171
- .some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()));
172
- if (this.hideThinkingBlock) {
173
- // lunr: thinking blocks are hidden — render nothing (the "Thinking..." label is
174
- // removed; an alternative hidden indicator will be designed separately).
175
- }
176
- else {
177
- // lunr: a run is complete when a block follows it, its timing closed, or no
178
- // timings are attached (history). Incomplete = still streaming.
179
- const runComplete = isThinkingRunComplete(i < message.content.length - 1, this.thinkingTimings?.[thinkingRunIndex], this.thinkingTimings !== undefined);
180
- if (this.thinkingCollapse && !this.expanded && runComplete) {
181
- // lunr: collapsible reasoning — a completed run collapses to
182
- // "✻ Thought for Xs" (duration only from live timings) + its first sentence.
183
- const timing = this.thinkingTimings?.[thinkingRunIndex];
184
- const label = timing?.end !== undefined
185
- ? `✻ Thought for ${formatThoughtDuration(timing.end - timing.start)}`
186
- : "✻ Thought";
187
- this.contentContainer.addChild(new Text(theme.fg("thinkingText", theme.italic(label)), this.outputPad, 0));
188
- const snippet = thinkingSnippet(thinkingBlocks.join("\n\n"));
189
- if (snippet) {
190
- this.contentContainer.addChild(new Text(theme.fg("thinkingText", theme.italic(snippet)), this.outputPad + 2, 0));
191
- }
192
- if (hasVisibleContentAfter) {
193
- this.contentContainer.addChild(new Spacer(1));
194
- }
195
- }
196
- else if (!runComplete && !this.expanded) {
197
- // lunr: rolling window — a still-streaming run shows only its last
198
- // THINKING_TAIL_LINES rendered lines; older lines disappear as new
199
- // ones stream in. ctrl+o expand (or completion) renders in full.
200
- this.contentContainer.addChild(new ThinkingTailComponent(thinkingBlocks.join("\n\n"), this.outputPad, 0, this.markdownTheme, {
201
- color: (text) => theme.fg("thinkingText", text),
202
- italic: true,
203
- }));
204
- if (hasVisibleContentAfter) {
205
- this.contentContainer.addChild(new Spacer(1));
206
- }
207
- }
208
- else {
209
- // Render each run of thinking blocks as one Markdown section.
210
- this.contentContainer.addChild(new Markdown(thinkingBlocks.join("\n\n"), this.outputPad, 0, this.markdownTheme, {
211
- color: (text) => theme.fg("thinkingText", text),
212
- italic: true,
213
- }));
214
- // lunr: only add the spacer when thinking is visible (avoids stray blank line when hidden).
215
- if (hasVisibleContentAfter) {
216
- this.contentContainer.addChild(new Spacer(1));
217
- }
218
- }
219
- }
177
+ renderedThinking = true;
178
+ this.renderThinkingRun(thinkingRunIndex, thinkingBlocks, message, i);
179
+ }
180
+ }
181
+ if (!this.hideThinkingBlock && !renderedThinking && sourceThinkingRuns.length > 0) {
182
+ thinkingRunIndex = 0;
183
+ const thinkingBlocks = sourceThinkingRuns[0] ?? [];
184
+ if (thinkingBlocks.length > 0) {
185
+ this.renderThinkingRun(thinkingRunIndex, thinkingBlocks, message, -1);
220
186
  }
221
187
  }
222
188
  // Check if incomplete/failed - show after partial content.
@@ -243,5 +209,67 @@ export class AssistantMessageComponent extends Container {
243
209
  }
244
210
  }
245
211
  }
212
+ renderThinkingRun(thinkingRunIndex, thinkingBlocks, displayMessage, displayIndex) {
213
+ const hasVisibleContentAfter = displayIndex >= 0
214
+ ? displayMessage.content
215
+ .slice(displayIndex + 1)
216
+ .some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()))
217
+ : displayMessage.content.some((c) => c.type === "text" && c.text.trim());
218
+ const runComplete = isThinkingRunComplete(false, this.thinkingTimings?.[thinkingRunIndex], this.thinkingTimings !== undefined);
219
+ if (this.thinkingCollapse && !this.expanded && runComplete) {
220
+ const timing = this.thinkingTimings?.[thinkingRunIndex];
221
+ const label = timing?.end !== undefined
222
+ ? `✻ Thought for ${formatThoughtDuration(timing.end - timing.start)}`
223
+ : "✻ Thought";
224
+ this.contentContainer.addChild(new Text(theme.fg("thinkingText", theme.italic(label)), this.outputPad, 0));
225
+ const snippet = thinkingSnippet(thinkingBlocks.join("\n\n"));
226
+ if (snippet) {
227
+ this.contentContainer.addChild(new Text(theme.fg("thinkingText", theme.italic(snippet)), this.outputPad + 2, 0));
228
+ }
229
+ if (hasVisibleContentAfter) {
230
+ this.contentContainer.addChild(new Spacer(1));
231
+ }
232
+ return;
233
+ }
234
+ if (!runComplete && !this.expanded) {
235
+ this.contentContainer.addChild(new ThinkingTailComponent(thinkingBlocks.join("\n\n"), this.outputPad, 0, this.markdownTheme, {
236
+ color: (text) => theme.fg("thinkingText", text),
237
+ italic: true,
238
+ }));
239
+ if (hasVisibleContentAfter) {
240
+ this.contentContainer.addChild(new Spacer(1));
241
+ }
242
+ return;
243
+ }
244
+ this.contentContainer.addChild(new Markdown(thinkingBlocks.join("\n\n"), this.outputPad, 0, this.markdownTheme, {
245
+ color: (text) => theme.fg("thinkingText", text),
246
+ italic: true,
247
+ }));
248
+ if (hasVisibleContentAfter) {
249
+ this.contentContainer.addChild(new Spacer(1));
250
+ }
251
+ }
252
+ }
253
+ function collectThinkingRuns(content) {
254
+ const runs = [];
255
+ for (let i = 0; i < content.length; i++) {
256
+ const block = content[i];
257
+ if (block.type !== "thinking")
258
+ continue;
259
+ const blocks = [];
260
+ for (; i < content.length; i++) {
261
+ const thinkingContent = content[i];
262
+ if (thinkingContent.type !== "thinking") {
263
+ i--;
264
+ break;
265
+ }
266
+ const thinking = thinkingContent.thinking.trim();
267
+ if (thinking)
268
+ blocks.push(thinking);
269
+ }
270
+ if (blocks.length > 0)
271
+ runs.push(blocks);
272
+ }
273
+ return runs;
246
274
  }
247
275
  //# sourceMappingURL=assistant-message.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"assistant-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@ashx-j/lunr-ai\";\nimport { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@ashx-j/lunr-tui\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport {\n\tformatThoughtDuration,\n\tisThinkingRunComplete,\n\ttype ThinkingRunTiming,\n\tthinkingSnippet,\n} from \"./thinking-summary.ts\";\nimport { ThinkingTailComponent } from \"./thinking-tail.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tselectable = true;\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\t// lunr: collapsible reasoning — when true, a completed thinking run renders as\n\t// \"✻ Thought for Xs\" + its first sentence instead of the full block.\n\tprivate thinkingCollapse: boolean;\n\t// lunr: ctrl+o expansion — when true, collapsed thinking runs render in full\n\t// (wired into the global app.tools.expand toggle via the Expandable interface).\n\tprivate expanded = false;\n\t// lunr: live per-run timings from interactive-mode; undefined = history message\n\t// (always treated as final, rendered without durations).\n\tprivate thinkingTimings?: ThinkingRunTiming[];\n\tprivate markdownTheme: MarkdownTheme;\n\t// lunr: kept for the deferred alternative hidden-thinking indicator (Phase 8 removed the label render).\n\t// biome-ignore lint/correctness/noUnusedPrivateClassMembers: retained for future use\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate lastMessage?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\t// lunr: gutter rail — prefix rendered lines with a dim │; the last line uses ╰\n\t// when this component closes the turn (no tool calls follow).\n\tprivate gutterRail: boolean;\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = \"Thinking...\",\n\t\toutputPad = 1,\n\t\tgutterRail = false,\n\t\tthinkingCollapse = false,\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.thinkingCollapse = thinkingCollapse;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\t\tthis.gutterRail = gutterRail;\n\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\t// lunr: collapsible reasoning toggles (live; re-render like setHideThinkingBlock).\n\tsetThinkingCollapse(collapse: boolean): void {\n\t\tthis.thinkingCollapse = collapse;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\t// lunr: ctrl+o expansion — Expandable interface hook; expanded collapsed thinking\n\t// runs render as full Markdown blocks again.\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetThinkingTimings(timings: ThinkingRunTiming[] | undefined): void {\n\t\tthis.thinkingTimings = timings;\n\t}\n\n\t// lunr: gutter rail toggle (live; applied on next render).\n\tsetGutterRail(enabled: boolean): void {\n\t\tthis.gutterRail = enabled;\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\t// lunr: gutter rail — render content at width-2 and prefix each line with a\n\t\t// dim │; the last line uses ╰ when this assistant message closes the turn\n\t\t// (no tool calls follow). When tool calls are present the rail stays open (│)\n\t\t// because tool-execution components render their own borders below.\n\t\tconst railEnabled = this.gutterRail;\n\t\tconst contentWidth = railEnabled ? Math.max(1, width - 2) : width;\n\t\tconst lines = super.render(contentWidth);\n\t\tif (this.hasToolCalls || lines.length === 0) {\n\t\t\tif (railEnabled && lines.length > 0) {\n\t\t\t\tconst rail = theme.fg(\"dim\", \"│ \");\n\t\t\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t\t\tlines[i] = rail + lines[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\n\t\tif (railEnabled) {\n\t\t\tconst rail = theme.fg(\"dim\", \"│ \");\n\t\t\tconst close = theme.fg(\"dim\", \"╰ \");\n\t\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t\tlines[i] = (i === lines.length - 1 ? close : rail) + lines[i];\n\t\t\t}\n\t\t}\n\t\treturn lines;\n\t}\n\n\tupdateContent(message: AssistantMessage): void {\n\t\tthis.lastMessage = message;\n\n\t\t// Clear content container\n\t\tthis.contentContainer.clear();\n\n\t\tconst hasVisibleContent = message.content.some(\n\t\t\t(c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()),\n\t\t);\n\n\t\tif (hasVisibleContent) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\t// Render content in order\n\t\tlet isFirstTextBlock = true;\n\t\tlet thinkingRunIndex = -1;\n\t\tfor (let i = 0; i < message.content.length; i++) {\n\t\t\tconst content = message.content[i];\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\t// Assistant text messages with no background - trim the text\n\t\t\t\t// Set paddingY=0 to avoid extra spacing before tool executions\n\t\t\t\t// lunr: leading ● marks the first text block (never thinking blocks). It rides\n\t\t\t\t// the defaultTextStyle below, so it renders bright white like the body.\n\t\t\t\tconst trimmed = content.text.trim();\n\t\t\t\tconst text = isFirstTextBlock ? `● ${trimmed}` : trimmed;\n\t\t\t\tisFirstTextBlock = false;\n\t\t\t\t// lunr: theme-polish — assistant message body renders bright white via the\n\t\t\t\t// shared userMessageText token (same knob as user messages).\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Markdown(text, this.outputPad, 0, this.markdownTheme, {\n\t\t\t\t\t\tcolor: (body: string) => theme.fg(\"userMessageText\", body),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else if (content.type === \"thinking\") {\n\t\t\t\tthinkingRunIndex++;\n\t\t\t\tconst thinkingBlocks: string[] = [];\n\t\t\t\tfor (; i < message.content.length; i++) {\n\t\t\t\t\tconst thinkingContent = message.content[i];\n\t\t\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\t\t\tif (thinking) {\n\t\t\t\t\t\tthinkingBlocks.push(thinking);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti--;\n\n\t\t\t\tif (thinkingBlocks.length === 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Add spacing only when another visible assistant content block follows.\n\t\t\t\t// This avoids a superfluous blank line before separately-rendered tool execution blocks.\n\t\t\t\tconst hasVisibleContentAfter = message.content\n\t\t\t\t\t.slice(i + 1)\n\t\t\t\t\t.some((c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()));\n\n\t\t\t\tif (this.hideThinkingBlock) {\n\t\t\t\t\t// lunr: thinking blocks are hidden — render nothing (the \"Thinking...\" label is\n\t\t\t\t\t// removed; an alternative hidden indicator will be designed separately).\n\t\t\t\t} else {\n\t\t\t\t\t// lunr: a run is complete when a block follows it, its timing closed, or no\n\t\t\t\t\t// timings are attached (history). Incomplete = still streaming.\n\t\t\t\t\tconst runComplete = isThinkingRunComplete(\n\t\t\t\t\t\ti < message.content.length - 1,\n\t\t\t\t\t\tthis.thinkingTimings?.[thinkingRunIndex],\n\t\t\t\t\t\tthis.thinkingTimings !== undefined,\n\t\t\t\t\t);\n\t\t\t\t\tif (this.thinkingCollapse && !this.expanded && runComplete) {\n\t\t\t\t\t\t// lunr: collapsible reasoning — a completed run collapses to\n\t\t\t\t\t\t// \"✻ Thought for Xs\" (duration only from live timings) + its first sentence.\n\t\t\t\t\t\tconst timing = this.thinkingTimings?.[thinkingRunIndex];\n\t\t\t\t\t\tconst label =\n\t\t\t\t\t\t\ttiming?.end !== undefined\n\t\t\t\t\t\t\t\t? `✻ Thought for ${formatThoughtDuration(timing.end - timing.start)}`\n\t\t\t\t\t\t\t\t: \"✻ Thought\";\n\t\t\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\t\t\tnew Text(theme.fg(\"thinkingText\", theme.italic(label)), this.outputPad, 0),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst snippet = thinkingSnippet(thinkingBlocks.join(\"\\n\\n\"));\n\t\t\t\t\t\tif (snippet) {\n\t\t\t\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\t\t\t\tnew Text(theme.fg(\"thinkingText\", theme.italic(snippet)), this.outputPad + 2, 0),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (!runComplete && !this.expanded) {\n\t\t\t\t\t\t// lunr: rolling window — a still-streaming run shows only its last\n\t\t\t\t\t\t// THINKING_TAIL_LINES rendered lines; older lines disappear as new\n\t\t\t\t\t\t// ones stream in. ctrl+o expand (or completion) renders in full.\n\t\t\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\t\t\tnew ThinkingTailComponent(thinkingBlocks.join(\"\\n\\n\"), this.outputPad, 0, this.markdownTheme, {\n\t\t\t\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\t\t\t\titalic: true,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Render each run of thinking blocks as one Markdown section.\n\t\t\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\t\t\tnew Markdown(thinkingBlocks.join(\"\\n\\n\"), this.outputPad, 0, this.markdownTheme, {\n\t\t\t\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\t\t\t\titalic: true,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// lunr: only add the spacer when thinking is visible (avoids stray blank line when hidden).\n\t\t\t\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check if incomplete/failed - show after partial content.\n\t\t// For aborted/error tool calls, tool execution components show the error.\n\t\t// Length stops can happen before a tool call is complete, so surface them here too.\n\t\tconst hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t\tthis.hasToolCalls = hasToolCalls;\n\t\tif (message.stopReason === \"length\") {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\"Error: Model stopped because it reached the maximum output token limit. The response may be incomplete.\",\n\t\t\t\t\t),\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (!hasToolCalls) {\n\t\t\tif (message.stopReason === \"aborted\") {\n\t\t\t\tconst abortMessage =\n\t\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t\t: \"Operation aborted\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", abortMessage), this.outputPad, 0));\n\t\t\t} else if (message.stopReason === \"error\") {\n\t\t\t\tconst errorMsg = message.errorMessage || \"Unknown error\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", `Error: ${errorMsg}`), this.outputPad, 0));\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"assistant-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@ashx-j/lunr-ai\";\nimport { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@ashx-j/lunr-tui\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport {\n\tformatThoughtDuration,\n\tisThinkingRunComplete,\n\ttype ThinkingRunTiming,\n\tthinkingSnippet,\n} from \"./thinking-summary.ts\";\nimport { ThinkingTailComponent } from \"./thinking-tail.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tselectable = true;\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\t// lunr: collapsible reasoning — when true, a completed thinking run renders as\n\t// \"✻ Thought for Xs\" + its first sentence instead of the full block.\n\tprivate thinkingCollapse: boolean;\n\t// lunr: ctrl+o expansion — when true, collapsed thinking runs render in full\n\t// (wired into the global app.tools.expand toggle via the Expandable interface).\n\tprivate expanded = false;\n\t// lunr: live per-run timings from interactive-mode; undefined = history message\n\t// (always treated as final, rendered without durations).\n\tprivate thinkingTimings?: ThinkingRunTiming[];\n\tprivate markdownTheme: MarkdownTheme;\n\t// lunr: kept for the deferred alternative hidden-thinking indicator (Phase 8 removed the label render).\n\t// biome-ignore lint/correctness/noUnusedPrivateClassMembers: retained for future use\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate lastMessage?: AssistantMessage;\n\tprivate thinkingSource?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\t// lunr: gutter rail — prefix rendered lines with a dim │; the last line uses ╰\n\t// when this component closes the turn (no tool calls follow).\n\tprivate gutterRail: boolean;\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = \"Thinking...\",\n\t\toutputPad = 1,\n\t\tgutterRail = false,\n\t\tthinkingCollapse = false,\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.thinkingCollapse = thinkingCollapse;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\t\tthis.gutterRail = gutterRail;\n\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\t// lunr: collapsible reasoning toggles (live; re-render like setHideThinkingBlock).\n\tsetThinkingCollapse(collapse: boolean): void {\n\t\tthis.thinkingCollapse = collapse;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\t// lunr: ctrl+o expansion — Expandable interface hook; expanded collapsed thinking\n\t// runs render as full Markdown blocks again.\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tsetThinkingTimings(timings: ThinkingRunTiming[] | undefined): void {\n\t\tthis.thinkingTimings = timings;\n\t}\n\n\t// lunr: gutter rail toggle (live; applied on next render).\n\tsetGutterRail(enabled: boolean): void {\n\t\tthis.gutterRail = enabled;\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage, this.thinkingSourceOptions());\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\t// lunr: gutter rail — render content at width-2 and prefix each line with a\n\t\t// dim │; the last line uses ╰ when this assistant message closes the turn\n\t\t// (no tool calls follow). When tool calls are present the rail stays open (│)\n\t\t// because tool-execution components render their own borders below.\n\t\tconst railEnabled = this.gutterRail;\n\t\tconst contentWidth = railEnabled ? Math.max(1, width - 2) : width;\n\t\tconst lines = super.render(contentWidth);\n\t\tif (this.hasToolCalls || lines.length === 0) {\n\t\t\tif (railEnabled && lines.length > 0) {\n\t\t\t\tconst rail = theme.fg(\"dim\", \"│ \");\n\t\t\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t\t\tlines[i] = rail + lines[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\n\t\tif (railEnabled) {\n\t\t\tconst rail = theme.fg(\"dim\", \"│ \");\n\t\t\tconst close = theme.fg(\"dim\", \"╰ \");\n\t\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t\tlines[i] = (i === lines.length - 1 ? close : rail) + lines[i];\n\t\t\t}\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate thinkingSourceOptions(): { thinkingSource?: AssistantMessage } | undefined {\n\t\treturn this.thinkingSource ? { thinkingSource: this.thinkingSource } : undefined;\n\t}\n\n\tupdateContent(message: AssistantMessage, options?: { thinkingSource?: AssistantMessage }): void {\n\t\tthis.lastMessage = message;\n\t\tthis.thinkingSource = options?.thinkingSource;\n\n\t\t// Clear content container\n\t\tthis.contentContainer.clear();\n\n\t\tconst sourceMessage = this.thinkingSource ?? message;\n\t\tconst sourceThinkingRuns = collectThinkingRuns(sourceMessage.content);\n\t\tconst hasVisibleContent =\n\t\t\tmessage.content.some(\n\t\t\t\t(c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()),\n\t\t\t) ||\n\t\t\t(!this.hideThinkingBlock && sourceThinkingRuns.some((run) => run.length > 0));\n\n\t\tif (hasVisibleContent) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\t// Render content in order\n\t\tlet isFirstTextBlock = true;\n\t\tlet thinkingRunIndex = -1;\n\t\tlet renderedThinking = false;\n\t\tfor (let i = 0; i < message.content.length; i++) {\n\t\t\tconst content = message.content[i];\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\t// Assistant text messages with no background - trim the text\n\t\t\t\t// Set paddingY=0 to avoid extra spacing before tool executions\n\t\t\t\t// lunr: leading ● marks the first text block (never thinking blocks). It rides\n\t\t\t\t// the defaultTextStyle below, so it renders bright white like the body.\n\t\t\t\tconst trimmed = content.text.trim();\n\t\t\t\tconst text = isFirstTextBlock ? `● ${trimmed}` : trimmed;\n\t\t\t\tisFirstTextBlock = false;\n\t\t\t\t// lunr: theme-polish — assistant message body renders bright white via the\n\t\t\t\t// shared userMessageText token (same knob as user messages).\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Markdown(text, this.outputPad, 0, this.markdownTheme, {\n\t\t\t\t\t\tcolor: (body: string) => theme.fg(\"userMessageText\", body),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else if (content.type === \"thinking\") {\n\t\t\t\tthinkingRunIndex++;\n\t\t\t\tconst displayBlocks: string[] = [];\n\t\t\t\tfor (; i < message.content.length; i++) {\n\t\t\t\t\tconst thinkingContent = message.content[i];\n\t\t\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\t\t\tif (thinking) {\n\t\t\t\t\t\tdisplayBlocks.push(thinking);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti--;\n\n\t\t\t\tconst thinkingBlocks = sourceThinkingRuns[thinkingRunIndex] ?? displayBlocks;\n\t\t\t\tif (thinkingBlocks.length === 0 || this.hideThinkingBlock) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\trenderedThinking = true;\n\t\t\t\tthis.renderThinkingRun(thinkingRunIndex, thinkingBlocks, message, i);\n\t\t\t}\n\t\t}\n\n\t\tif (!this.hideThinkingBlock && !renderedThinking && sourceThinkingRuns.length > 0) {\n\t\t\tthinkingRunIndex = 0;\n\t\t\tconst thinkingBlocks = sourceThinkingRuns[0] ?? [];\n\t\t\tif (thinkingBlocks.length > 0) {\n\t\t\t\tthis.renderThinkingRun(thinkingRunIndex, thinkingBlocks, message, -1);\n\t\t\t}\n\t\t}\n\n\t\t// Check if incomplete/failed - show after partial content.\n\t\t// For aborted/error tool calls, tool execution components show the error.\n\t\t// Length stops can happen before a tool call is complete, so surface them here too.\n\t\tconst hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t\tthis.hasToolCalls = hasToolCalls;\n\t\tif (message.stopReason === \"length\") {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\"Error: Model stopped because it reached the maximum output token limit. The response may be incomplete.\",\n\t\t\t\t\t),\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (!hasToolCalls) {\n\t\t\tif (message.stopReason === \"aborted\") {\n\t\t\t\tconst abortMessage =\n\t\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t\t: \"Operation aborted\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", abortMessage), this.outputPad, 0));\n\t\t\t} else if (message.stopReason === \"error\") {\n\t\t\t\tconst errorMsg = message.errorMessage || \"Unknown error\";\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", `Error: ${errorMsg}`), this.outputPad, 0));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate renderThinkingRun(\n\t\tthinkingRunIndex: number,\n\t\tthinkingBlocks: string[],\n\t\tdisplayMessage: AssistantMessage,\n\t\tdisplayIndex: number,\n\t): void {\n\t\tconst hasVisibleContentAfter =\n\t\t\tdisplayIndex >= 0\n\t\t\t\t? displayMessage.content\n\t\t\t\t\t\t.slice(displayIndex + 1)\n\t\t\t\t\t\t.some((c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()))\n\t\t\t\t: displayMessage.content.some((c) => c.type === \"text\" && c.text.trim());\n\n\t\tconst runComplete = isThinkingRunComplete(\n\t\t\tfalse,\n\t\t\tthis.thinkingTimings?.[thinkingRunIndex],\n\t\t\tthis.thinkingTimings !== undefined,\n\t\t);\n\t\tif (this.thinkingCollapse && !this.expanded && runComplete) {\n\t\t\tconst timing = this.thinkingTimings?.[thinkingRunIndex];\n\t\t\tconst label =\n\t\t\t\ttiming?.end !== undefined\n\t\t\t\t\t? `✻ Thought for ${formatThoughtDuration(timing.end - timing.start)}`\n\t\t\t\t\t: \"✻ Thought\";\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"thinkingText\", theme.italic(label)), this.outputPad, 0));\n\t\t\tconst snippet = thinkingSnippet(thinkingBlocks.join(\"\\n\\n\"));\n\t\t\tif (snippet) {\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Text(theme.fg(\"thinkingText\", theme.italic(snippet)), this.outputPad + 2, 0),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!runComplete && !this.expanded) {\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew ThinkingTailComponent(thinkingBlocks.join(\"\\n\\n\"), this.outputPad, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\titalic: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Markdown(thinkingBlocks.join(\"\\n\\n\"), this.outputPad, 0, this.markdownTheme, {\n\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\titalic: true,\n\t\t\t}),\n\t\t);\n\t\tif (hasVisibleContentAfter) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t}\n\t}\n}\n\nfunction collectThinkingRuns(content: AssistantMessage[\"content\"]): string[][] {\n\tconst runs: string[][] = [];\n\tfor (let i = 0; i < content.length; i++) {\n\t\tconst block = content[i];\n\t\tif (block.type !== \"thinking\") continue;\n\t\tconst blocks: string[] = [];\n\t\tfor (; i < content.length; i++) {\n\t\t\tconst thinkingContent = content[i];\n\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\ti--;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\tif (thinking) blocks.push(thinking);\n\t\t}\n\t\tif (blocks.length > 0) runs.push(blocks);\n\t}\n\treturn runs;\n}\n"]}
@@ -28,9 +28,9 @@ export declare function updateThinkingRunTimings(timings: ThinkingRunTiming[], c
28
28
  /**
29
29
  * Whether a thinking run is complete and may collapse. Note: partial streaming
30
30
  * messages carry `stopReason: "stop"` from the start, so stopReason cannot be
31
- * used. Instead: a run is complete when a block follows it, when its live
32
- * timing has an `end`, or when no timings are attached at all (history /
33
- * resumed messages are always final).
31
+ * used. History / resumed messages have no timings and are always final. A
32
+ * live run stays open until its timing records `end` a following empty
33
+ * block must not collapse it early.
34
34
  */
35
- export declare function isThinkingRunComplete(hasFollowingBlock: boolean, timing: ThinkingRunTiming | undefined, timingsAttached: boolean): boolean;
35
+ export declare function isThinkingRunComplete(_hasFollowingBlock: boolean, timing: ThinkingRunTiming | undefined, timingsAttached: boolean): boolean;
36
36
  //# sourceMappingURL=thinking-summary.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"thinking-summary.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-summary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D;;;;;GAKG;AAEH,uFAAuF;AACvF,MAAM,WAAW,iBAAiB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAKD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,MAAM,CAyB5E;AAED,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAWxD;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,iBAAiB,EAAE,EAC5B,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,EACpC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,GACd,IAAI,CA8BN;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACpC,iBAAiB,EAAE,OAAO,EAC1B,MAAM,EAAE,iBAAiB,GAAG,SAAS,EACrC,eAAe,EAAE,OAAO,GACtB,OAAO,CAET","sourcesContent":["import type { AssistantMessage } from \"@ashx-j/lunr-ai\";\n\n/**\n * lunr: pure helpers for the collapsible-reasoning feature. A completed\n * thinking run renders as \"✻ Thought for Xs\" + the run's first sentence\n * instead of the full block. Kept component-free so vitest can cover the\n * extraction/timing logic directly.\n */\n\n/** Live timing for one thinking run (a maximal run of consecutive thinking blocks). */\nexport interface ThinkingRunTiming {\n\tstart: number;\n\tend?: number;\n}\n\n/** Leading markdown punctuation stripped before sentence extraction. */\nconst LEADING_MARKDOWN_RE = /^[#*`>\\-\\s]+/;\n\n/**\n * Extract a one-line summary from a thinking run: the first sentence of the\n * first substantive line (blank and markdown-only lines skipped). If no\n * sentence terminator appears within `maxLen` chars, hard-truncate at the\n * last word boundary and append an ellipsis.\n */\nexport function thinkingSnippet(joinedThinking: string, maxLen = 120): string {\n\tconst text = joinedThinking\n\t\t.split(\"\\n\")\n\t\t// Markdown headers are structural labels, not sentences — skip them.\n\t\t.filter((line) => !line.trimStart().startsWith(\"#\"))\n\t\t.map((line) => line.replace(LEADING_MARKDOWN_RE, \"\").trim())\n\t\t.filter((line) => line.length > 0)\n\t\t.join(\" \")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n\tif (!text) {\n\t\treturn \"\";\n\t}\n\t// First sentence: up to a terminator followed by whitespace or end-of-text.\n\tconst sentenceMatch = text.match(/^.*?[.!?](?=\\s|$)/);\n\tif (sentenceMatch && sentenceMatch[0].length <= maxLen) {\n\t\treturn sentenceMatch[0];\n\t}\n\tif (text.length <= maxLen) {\n\t\treturn text;\n\t}\n\tconst window = text.slice(0, maxLen);\n\tconst lastSpace = window.lastIndexOf(\" \");\n\tconst cut = lastSpace > 0 ? window.slice(0, lastSpace) : window;\n\treturn `${cut}…`;\n}\n\n/** Format a thinking duration: `<1s`, `Ns`, or `Mm Ss`. */\nexport function formatThoughtDuration(ms: number): string {\n\tconst seconds = Math.round(ms / 1000);\n\tif (seconds < 1) {\n\t\treturn \"<1s\";\n\t}\n\tif (seconds < 60) {\n\t\treturn `${seconds}s`;\n\t}\n\tconst minutes = Math.floor(seconds / 60);\n\tconst rest = seconds % 60;\n\treturn rest > 0 ? `${minutes}m ${rest}s` : `${minutes}m`;\n}\n\n/**\n * Update per-run timings from the full (un-sliced) streaming message content.\n * A run gets `start` the first time it is seen and `end` (once) when any\n * content block follows it or the message is final.\n */\nexport function updateThinkingRunTimings(\n\ttimings: ThinkingRunTiming[],\n\tcontent: AssistantMessage[\"content\"],\n\tnow: number,\n\tisFinal: boolean,\n): void {\n\tlet runIndex = -1;\n\tlet inThinkingRun = false;\n\tfor (let i = 0; i < content.length; i++) {\n\t\tconst block = content[i];\n\t\tif (block.type === \"thinking\") {\n\t\t\tif (!inThinkingRun) {\n\t\t\t\trunIndex++;\n\t\t\t\tinThinkingRun = true;\n\t\t\t\tif (!timings[runIndex]) {\n\t\t\t\t\ttimings[runIndex] = { start: now };\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (inThinkingRun) {\n\t\t\t// A non-thinking block follows the run: it is complete.\n\t\t\tconst timing = timings[runIndex];\n\t\t\tif (timing && timing.end === undefined) {\n\t\t\t\ttiming.end = now;\n\t\t\t}\n\t\t\tinThinkingRun = false;\n\t\t}\n\t}\n\tif (isFinal && inThinkingRun) {\n\t\tconst timing = timings[runIndex];\n\t\tif (timing && timing.end === undefined) {\n\t\t\ttiming.end = now;\n\t\t}\n\t}\n}\n\n/**\n * Whether a thinking run is complete and may collapse. Note: partial streaming\n * messages carry `stopReason: \"stop\"` from the start, so stopReason cannot be\n * used. Instead: a run is complete when a block follows it, when its live\n * timing has an `end`, or when no timings are attached at all (history /\n * resumed messages are always final).\n */\nexport function isThinkingRunComplete(\n\thasFollowingBlock: boolean,\n\ttiming: ThinkingRunTiming | undefined,\n\ttimingsAttached: boolean,\n): boolean {\n\treturn hasFollowingBlock || timing?.end !== undefined || !timingsAttached;\n}\n"]}
1
+ {"version":3,"file":"thinking-summary.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-summary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D;;;;;GAKG;AAEH,uFAAuF;AACvF,MAAM,WAAW,iBAAiB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAKD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,MAAM,CAyB5E;AAED,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAWxD;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,iBAAiB,EAAE,EAC5B,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,EACpC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,GACd,IAAI,CA8BN;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACpC,kBAAkB,EAAE,OAAO,EAC3B,MAAM,EAAE,iBAAiB,GAAG,SAAS,EACrC,eAAe,EAAE,OAAO,GACtB,OAAO,CAGT","sourcesContent":["import type { AssistantMessage } from \"@ashx-j/lunr-ai\";\n\n/**\n * lunr: pure helpers for the collapsible-reasoning feature. A completed\n * thinking run renders as \"✻ Thought for Xs\" + the run's first sentence\n * instead of the full block. Kept component-free so vitest can cover the\n * extraction/timing logic directly.\n */\n\n/** Live timing for one thinking run (a maximal run of consecutive thinking blocks). */\nexport interface ThinkingRunTiming {\n\tstart: number;\n\tend?: number;\n}\n\n/** Leading markdown punctuation stripped before sentence extraction. */\nconst LEADING_MARKDOWN_RE = /^[#*`>\\-\\s]+/;\n\n/**\n * Extract a one-line summary from a thinking run: the first sentence of the\n * first substantive line (blank and markdown-only lines skipped). If no\n * sentence terminator appears within `maxLen` chars, hard-truncate at the\n * last word boundary and append an ellipsis.\n */\nexport function thinkingSnippet(joinedThinking: string, maxLen = 120): string {\n\tconst text = joinedThinking\n\t\t.split(\"\\n\")\n\t\t// Markdown headers are structural labels, not sentences — skip them.\n\t\t.filter((line) => !line.trimStart().startsWith(\"#\"))\n\t\t.map((line) => line.replace(LEADING_MARKDOWN_RE, \"\").trim())\n\t\t.filter((line) => line.length > 0)\n\t\t.join(\" \")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n\tif (!text) {\n\t\treturn \"\";\n\t}\n\t// First sentence: up to a terminator followed by whitespace or end-of-text.\n\tconst sentenceMatch = text.match(/^.*?[.!?](?=\\s|$)/);\n\tif (sentenceMatch && sentenceMatch[0].length <= maxLen) {\n\t\treturn sentenceMatch[0];\n\t}\n\tif (text.length <= maxLen) {\n\t\treturn text;\n\t}\n\tconst window = text.slice(0, maxLen);\n\tconst lastSpace = window.lastIndexOf(\" \");\n\tconst cut = lastSpace > 0 ? window.slice(0, lastSpace) : window;\n\treturn `${cut}…`;\n}\n\n/** Format a thinking duration: `<1s`, `Ns`, or `Mm Ss`. */\nexport function formatThoughtDuration(ms: number): string {\n\tconst seconds = Math.round(ms / 1000);\n\tif (seconds < 1) {\n\t\treturn \"<1s\";\n\t}\n\tif (seconds < 60) {\n\t\treturn `${seconds}s`;\n\t}\n\tconst minutes = Math.floor(seconds / 60);\n\tconst rest = seconds % 60;\n\treturn rest > 0 ? `${minutes}m ${rest}s` : `${minutes}m`;\n}\n\n/**\n * Update per-run timings from the full (un-sliced) streaming message content.\n * A run gets `start` the first time it is seen and `end` (once) when any\n * content block follows it or the message is final.\n */\nexport function updateThinkingRunTimings(\n\ttimings: ThinkingRunTiming[],\n\tcontent: AssistantMessage[\"content\"],\n\tnow: number,\n\tisFinal: boolean,\n): void {\n\tlet runIndex = -1;\n\tlet inThinkingRun = false;\n\tfor (let i = 0; i < content.length; i++) {\n\t\tconst block = content[i];\n\t\tif (block.type === \"thinking\") {\n\t\t\tif (!inThinkingRun) {\n\t\t\t\trunIndex++;\n\t\t\t\tinThinkingRun = true;\n\t\t\t\tif (!timings[runIndex]) {\n\t\t\t\t\ttimings[runIndex] = { start: now };\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (inThinkingRun) {\n\t\t\t// A non-thinking block follows the run: it is complete.\n\t\t\tconst timing = timings[runIndex];\n\t\t\tif (timing && timing.end === undefined) {\n\t\t\t\ttiming.end = now;\n\t\t\t}\n\t\t\tinThinkingRun = false;\n\t\t}\n\t}\n\tif (isFinal && inThinkingRun) {\n\t\tconst timing = timings[runIndex];\n\t\tif (timing && timing.end === undefined) {\n\t\t\ttiming.end = now;\n\t\t}\n\t}\n}\n\n/**\n * Whether a thinking run is complete and may collapse. Note: partial streaming\n * messages carry `stopReason: \"stop\"` from the start, so stopReason cannot be\n * used. History / resumed messages have no timings and are always final. A\n * live run stays open until its timing records `end` a following empty\n * block must not collapse it early.\n */\nexport function isThinkingRunComplete(\n\t_hasFollowingBlock: boolean,\n\ttiming: ThinkingRunTiming | undefined,\n\ttimingsAttached: boolean,\n): boolean {\n\tif (!timingsAttached) return true;\n\treturn timing?.end !== undefined;\n}\n"]}
@@ -84,11 +84,13 @@ export function updateThinkingRunTimings(timings, content, now, isFinal) {
84
84
  /**
85
85
  * Whether a thinking run is complete and may collapse. Note: partial streaming
86
86
  * messages carry `stopReason: "stop"` from the start, so stopReason cannot be
87
- * used. Instead: a run is complete when a block follows it, when its live
88
- * timing has an `end`, or when no timings are attached at all (history /
89
- * resumed messages are always final).
87
+ * used. History / resumed messages have no timings and are always final. A
88
+ * live run stays open until its timing records `end` a following empty
89
+ * block must not collapse it early.
90
90
  */
91
- export function isThinkingRunComplete(hasFollowingBlock, timing, timingsAttached) {
92
- return hasFollowingBlock || timing?.end !== undefined || !timingsAttached;
91
+ export function isThinkingRunComplete(_hasFollowingBlock, timing, timingsAttached) {
92
+ if (!timingsAttached)
93
+ return true;
94
+ return timing?.end !== undefined;
93
95
  }
94
96
  //# sourceMappingURL=thinking-summary.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"thinking-summary.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-summary.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@ashx-j/lunr-ai\";\n\n/**\n * lunr: pure helpers for the collapsible-reasoning feature. A completed\n * thinking run renders as \"✻ Thought for Xs\" + the run's first sentence\n * instead of the full block. Kept component-free so vitest can cover the\n * extraction/timing logic directly.\n */\n\n/** Live timing for one thinking run (a maximal run of consecutive thinking blocks). */\nexport interface ThinkingRunTiming {\n\tstart: number;\n\tend?: number;\n}\n\n/** Leading markdown punctuation stripped before sentence extraction. */\nconst LEADING_MARKDOWN_RE = /^[#*`>\\-\\s]+/;\n\n/**\n * Extract a one-line summary from a thinking run: the first sentence of the\n * first substantive line (blank and markdown-only lines skipped). If no\n * sentence terminator appears within `maxLen` chars, hard-truncate at the\n * last word boundary and append an ellipsis.\n */\nexport function thinkingSnippet(joinedThinking: string, maxLen = 120): string {\n\tconst text = joinedThinking\n\t\t.split(\"\\n\")\n\t\t// Markdown headers are structural labels, not sentences — skip them.\n\t\t.filter((line) => !line.trimStart().startsWith(\"#\"))\n\t\t.map((line) => line.replace(LEADING_MARKDOWN_RE, \"\").trim())\n\t\t.filter((line) => line.length > 0)\n\t\t.join(\" \")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n\tif (!text) {\n\t\treturn \"\";\n\t}\n\t// First sentence: up to a terminator followed by whitespace or end-of-text.\n\tconst sentenceMatch = text.match(/^.*?[.!?](?=\\s|$)/);\n\tif (sentenceMatch && sentenceMatch[0].length <= maxLen) {\n\t\treturn sentenceMatch[0];\n\t}\n\tif (text.length <= maxLen) {\n\t\treturn text;\n\t}\n\tconst window = text.slice(0, maxLen);\n\tconst lastSpace = window.lastIndexOf(\" \");\n\tconst cut = lastSpace > 0 ? window.slice(0, lastSpace) : window;\n\treturn `${cut}…`;\n}\n\n/** Format a thinking duration: `<1s`, `Ns`, or `Mm Ss`. */\nexport function formatThoughtDuration(ms: number): string {\n\tconst seconds = Math.round(ms / 1000);\n\tif (seconds < 1) {\n\t\treturn \"<1s\";\n\t}\n\tif (seconds < 60) {\n\t\treturn `${seconds}s`;\n\t}\n\tconst minutes = Math.floor(seconds / 60);\n\tconst rest = seconds % 60;\n\treturn rest > 0 ? `${minutes}m ${rest}s` : `${minutes}m`;\n}\n\n/**\n * Update per-run timings from the full (un-sliced) streaming message content.\n * A run gets `start` the first time it is seen and `end` (once) when any\n * content block follows it or the message is final.\n */\nexport function updateThinkingRunTimings(\n\ttimings: ThinkingRunTiming[],\n\tcontent: AssistantMessage[\"content\"],\n\tnow: number,\n\tisFinal: boolean,\n): void {\n\tlet runIndex = -1;\n\tlet inThinkingRun = false;\n\tfor (let i = 0; i < content.length; i++) {\n\t\tconst block = content[i];\n\t\tif (block.type === \"thinking\") {\n\t\t\tif (!inThinkingRun) {\n\t\t\t\trunIndex++;\n\t\t\t\tinThinkingRun = true;\n\t\t\t\tif (!timings[runIndex]) {\n\t\t\t\t\ttimings[runIndex] = { start: now };\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (inThinkingRun) {\n\t\t\t// A non-thinking block follows the run: it is complete.\n\t\t\tconst timing = timings[runIndex];\n\t\t\tif (timing && timing.end === undefined) {\n\t\t\t\ttiming.end = now;\n\t\t\t}\n\t\t\tinThinkingRun = false;\n\t\t}\n\t}\n\tif (isFinal && inThinkingRun) {\n\t\tconst timing = timings[runIndex];\n\t\tif (timing && timing.end === undefined) {\n\t\t\ttiming.end = now;\n\t\t}\n\t}\n}\n\n/**\n * Whether a thinking run is complete and may collapse. Note: partial streaming\n * messages carry `stopReason: \"stop\"` from the start, so stopReason cannot be\n * used. Instead: a run is complete when a block follows it, when its live\n * timing has an `end`, or when no timings are attached at all (history /\n * resumed messages are always final).\n */\nexport function isThinkingRunComplete(\n\thasFollowingBlock: boolean,\n\ttiming: ThinkingRunTiming | undefined,\n\ttimingsAttached: boolean,\n): boolean {\n\treturn hasFollowingBlock || timing?.end !== undefined || !timingsAttached;\n}\n"]}
1
+ {"version":3,"file":"thinking-summary.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-summary.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@ashx-j/lunr-ai\";\n\n/**\n * lunr: pure helpers for the collapsible-reasoning feature. A completed\n * thinking run renders as \"✻ Thought for Xs\" + the run's first sentence\n * instead of the full block. Kept component-free so vitest can cover the\n * extraction/timing logic directly.\n */\n\n/** Live timing for one thinking run (a maximal run of consecutive thinking blocks). */\nexport interface ThinkingRunTiming {\n\tstart: number;\n\tend?: number;\n}\n\n/** Leading markdown punctuation stripped before sentence extraction. */\nconst LEADING_MARKDOWN_RE = /^[#*`>\\-\\s]+/;\n\n/**\n * Extract a one-line summary from a thinking run: the first sentence of the\n * first substantive line (blank and markdown-only lines skipped). If no\n * sentence terminator appears within `maxLen` chars, hard-truncate at the\n * last word boundary and append an ellipsis.\n */\nexport function thinkingSnippet(joinedThinking: string, maxLen = 120): string {\n\tconst text = joinedThinking\n\t\t.split(\"\\n\")\n\t\t// Markdown headers are structural labels, not sentences — skip them.\n\t\t.filter((line) => !line.trimStart().startsWith(\"#\"))\n\t\t.map((line) => line.replace(LEADING_MARKDOWN_RE, \"\").trim())\n\t\t.filter((line) => line.length > 0)\n\t\t.join(\" \")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n\tif (!text) {\n\t\treturn \"\";\n\t}\n\t// First sentence: up to a terminator followed by whitespace or end-of-text.\n\tconst sentenceMatch = text.match(/^.*?[.!?](?=\\s|$)/);\n\tif (sentenceMatch && sentenceMatch[0].length <= maxLen) {\n\t\treturn sentenceMatch[0];\n\t}\n\tif (text.length <= maxLen) {\n\t\treturn text;\n\t}\n\tconst window = text.slice(0, maxLen);\n\tconst lastSpace = window.lastIndexOf(\" \");\n\tconst cut = lastSpace > 0 ? window.slice(0, lastSpace) : window;\n\treturn `${cut}…`;\n}\n\n/** Format a thinking duration: `<1s`, `Ns`, or `Mm Ss`. */\nexport function formatThoughtDuration(ms: number): string {\n\tconst seconds = Math.round(ms / 1000);\n\tif (seconds < 1) {\n\t\treturn \"<1s\";\n\t}\n\tif (seconds < 60) {\n\t\treturn `${seconds}s`;\n\t}\n\tconst minutes = Math.floor(seconds / 60);\n\tconst rest = seconds % 60;\n\treturn rest > 0 ? `${minutes}m ${rest}s` : `${minutes}m`;\n}\n\n/**\n * Update per-run timings from the full (un-sliced) streaming message content.\n * A run gets `start` the first time it is seen and `end` (once) when any\n * content block follows it or the message is final.\n */\nexport function updateThinkingRunTimings(\n\ttimings: ThinkingRunTiming[],\n\tcontent: AssistantMessage[\"content\"],\n\tnow: number,\n\tisFinal: boolean,\n): void {\n\tlet runIndex = -1;\n\tlet inThinkingRun = false;\n\tfor (let i = 0; i < content.length; i++) {\n\t\tconst block = content[i];\n\t\tif (block.type === \"thinking\") {\n\t\t\tif (!inThinkingRun) {\n\t\t\t\trunIndex++;\n\t\t\t\tinThinkingRun = true;\n\t\t\t\tif (!timings[runIndex]) {\n\t\t\t\t\ttimings[runIndex] = { start: now };\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (inThinkingRun) {\n\t\t\t// A non-thinking block follows the run: it is complete.\n\t\t\tconst timing = timings[runIndex];\n\t\t\tif (timing && timing.end === undefined) {\n\t\t\t\ttiming.end = now;\n\t\t\t}\n\t\t\tinThinkingRun = false;\n\t\t}\n\t}\n\tif (isFinal && inThinkingRun) {\n\t\tconst timing = timings[runIndex];\n\t\tif (timing && timing.end === undefined) {\n\t\t\ttiming.end = now;\n\t\t}\n\t}\n}\n\n/**\n * Whether a thinking run is complete and may collapse. Note: partial streaming\n * messages carry `stopReason: \"stop\"` from the start, so stopReason cannot be\n * used. History / resumed messages have no timings and are always final. A\n * live run stays open until its timing records `end` a following empty\n * block must not collapse it early.\n */\nexport function isThinkingRunComplete(\n\t_hasFollowingBlock: boolean,\n\ttiming: ThinkingRunTiming | undefined,\n\ttimingsAttached: boolean,\n): boolean {\n\tif (!timingsAttached) return true;\n\treturn timing?.end !== undefined;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"thinking-tail.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-tail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAY,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEtF;;;;;;GAMG;AAEH,iEAAiE;AACjE,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,qBAAa,qBAAsB,YAAW,SAAS;IACtD,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,QAAQ,CAAS;IAEzB,YACC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,aAAa,EAC5B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAChE,QAAQ,GAAE,MAA4B,EAItC;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAM9B;CACD","sourcesContent":["import { type Component, Markdown, type MarkdownTheme } from \"@ashx-j/lunr-tui\";\n\n/**\n * lunr: rolling window for a still-streaming thinking run. Renders the full\n * Markdown block (same styling as the complete-run branch) but returns only\n * the last `maxLines` rendered lines, so at most N visual lines of reasoning\n * are on screen at a time and older lines disappear as new ones stream in.\n * Applied render-time only; the transcript is untouched.\n */\n\n/** Maximum rendered lines shown for a streaming thinking run. */\nexport const THINKING_TAIL_LINES = 4;\n\nexport class ThinkingTailComponent implements Component {\n\tprivate markdown: Markdown;\n\tprivate maxLines: number;\n\n\tconstructor(\n\t\ttext: string,\n\t\tpaddingX: number,\n\t\tpaddingY: number,\n\t\tmarkdownTheme: MarkdownTheme,\n\t\toptions?: { color?: (text: string) => string; italic?: boolean },\n\t\tmaxLines: number = THINKING_TAIL_LINES,\n\t) {\n\t\tthis.maxLines = maxLines;\n\t\tthis.markdown = new Markdown(text, paddingX, paddingY, markdownTheme, options);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.markdown.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines = this.markdown.render(width);\n\t\tif (lines.length <= this.maxLines) {\n\t\t\treturn lines;\n\t\t}\n\t\treturn lines.slice(-this.maxLines);\n\t}\n}\n"]}
1
+ {"version":3,"file":"thinking-tail.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-tail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAY,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEtF;;;;;;GAMG;AAEH,iEAAiE;AACjE,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,qBAAa,qBAAsB,YAAW,SAAS;IACtD,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,QAAQ,CAAS;IAEzB,YACC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,aAAa,EAC5B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAChE,QAAQ,GAAE,MAA4B,EAItC;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ9B;CACD","sourcesContent":["import { type Component, Markdown, type MarkdownTheme } from \"@ashx-j/lunr-tui\";\n\n/**\n * lunr: rolling window for a still-streaming thinking run. Renders the full\n * Markdown block (same styling as the complete-run branch) but returns only\n * the last `maxLines` rendered lines, so at most N visual lines of reasoning\n * are on screen at a time and older lines disappear as new ones stream in.\n * Applied render-time only; the transcript is untouched.\n */\n\n/** Maximum rendered lines shown for a streaming thinking run. */\nexport const THINKING_TAIL_LINES = 4;\n\nexport class ThinkingTailComponent implements Component {\n\tprivate markdown: Markdown;\n\tprivate maxLines: number;\n\n\tconstructor(\n\t\ttext: string,\n\t\tpaddingX: number,\n\t\tpaddingY: number,\n\t\tmarkdownTheme: MarkdownTheme,\n\t\toptions?: { color?: (text: string) => string; italic?: boolean },\n\t\tmaxLines: number = THINKING_TAIL_LINES,\n\t) {\n\t\tthis.maxLines = maxLines;\n\t\tthis.markdown = new Markdown(text, paddingX, paddingY, markdownTheme, options);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.markdown.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines = this.markdown.render(width);\n\t\tconst tail = lines.length <= this.maxLines ? lines : lines.slice(-this.maxLines);\n\t\tif (tail.length >= this.maxLines) {\n\t\t\treturn tail;\n\t\t}\n\t\tconst pad = this.maxLines - tail.length;\n\t\treturn [...Array.from({ length: pad }, () => \"\"), ...tail];\n\t}\n}\n"]}
@@ -20,10 +20,12 @@ export class ThinkingTailComponent {
20
20
  }
21
21
  render(width) {
22
22
  const lines = this.markdown.render(width);
23
- if (lines.length <= this.maxLines) {
24
- return lines;
23
+ const tail = lines.length <= this.maxLines ? lines : lines.slice(-this.maxLines);
24
+ if (tail.length >= this.maxLines) {
25
+ return tail;
25
26
  }
26
- return lines.slice(-this.maxLines);
27
+ const pad = this.maxLines - tail.length;
28
+ return [...Array.from({ length: pad }, () => ""), ...tail];
27
29
  }
28
30
  }
29
31
  //# sourceMappingURL=thinking-tail.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"thinking-tail.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-tail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,QAAQ,EAAsB,MAAM,wBAAwB,CAAC;AAEtF;;;;;;GAMG;AAEH,iEAAiE;AACjE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,MAAM,OAAO,qBAAqB;IACzB,QAAQ,CAAW;IACnB,QAAQ,CAAS;IAEzB,YACC,IAAY,EACZ,QAAgB,EAChB,QAAgB,EAChB,aAA4B,EAC5B,OAAgE,EAChE,QAAQ,GAAW,mBAAmB,EACrC;QACD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,IAAI,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;IAAA,CAC/E;IAED,UAAU,GAAS;QAClB,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IAAA,CAC3B;IAED,MAAM,CAAC,KAAa,EAAY;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnC,OAAO,KAAK,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAAA,CACnC;CACD","sourcesContent":["import { type Component, Markdown, type MarkdownTheme } from \"@ashx-j/lunr-tui\";\n\n/**\n * lunr: rolling window for a still-streaming thinking run. Renders the full\n * Markdown block (same styling as the complete-run branch) but returns only\n * the last `maxLines` rendered lines, so at most N visual lines of reasoning\n * are on screen at a time and older lines disappear as new ones stream in.\n * Applied render-time only; the transcript is untouched.\n */\n\n/** Maximum rendered lines shown for a streaming thinking run. */\nexport const THINKING_TAIL_LINES = 4;\n\nexport class ThinkingTailComponent implements Component {\n\tprivate markdown: Markdown;\n\tprivate maxLines: number;\n\n\tconstructor(\n\t\ttext: string,\n\t\tpaddingX: number,\n\t\tpaddingY: number,\n\t\tmarkdownTheme: MarkdownTheme,\n\t\toptions?: { color?: (text: string) => string; italic?: boolean },\n\t\tmaxLines: number = THINKING_TAIL_LINES,\n\t) {\n\t\tthis.maxLines = maxLines;\n\t\tthis.markdown = new Markdown(text, paddingX, paddingY, markdownTheme, options);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.markdown.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines = this.markdown.render(width);\n\t\tif (lines.length <= this.maxLines) {\n\t\t\treturn lines;\n\t\t}\n\t\treturn lines.slice(-this.maxLines);\n\t}\n}\n"]}
1
+ {"version":3,"file":"thinking-tail.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-tail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,QAAQ,EAAsB,MAAM,wBAAwB,CAAC;AAEtF;;;;;;GAMG;AAEH,iEAAiE;AACjE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,MAAM,OAAO,qBAAqB;IACzB,QAAQ,CAAW;IACnB,QAAQ,CAAS;IAEzB,YACC,IAAY,EACZ,QAAgB,EAChB,QAAgB,EAChB,aAA4B,EAC5B,OAAgE,EAChE,QAAQ,GAAW,mBAAmB,EACrC;QACD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,IAAI,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;IAAA,CAC/E;IAED,UAAU,GAAS;QAClB,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IAAA,CAC3B;IAED,MAAM,CAAC,KAAa,EAAY;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjF,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC;QACb,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC;QACxC,OAAO,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;IAAA,CAC3D;CACD","sourcesContent":["import { type Component, Markdown, type MarkdownTheme } from \"@ashx-j/lunr-tui\";\n\n/**\n * lunr: rolling window for a still-streaming thinking run. Renders the full\n * Markdown block (same styling as the complete-run branch) but returns only\n * the last `maxLines` rendered lines, so at most N visual lines of reasoning\n * are on screen at a time and older lines disappear as new ones stream in.\n * Applied render-time only; the transcript is untouched.\n */\n\n/** Maximum rendered lines shown for a streaming thinking run. */\nexport const THINKING_TAIL_LINES = 4;\n\nexport class ThinkingTailComponent implements Component {\n\tprivate markdown: Markdown;\n\tprivate maxLines: number;\n\n\tconstructor(\n\t\ttext: string,\n\t\tpaddingX: number,\n\t\tpaddingY: number,\n\t\tmarkdownTheme: MarkdownTheme,\n\t\toptions?: { color?: (text: string) => string; italic?: boolean },\n\t\tmaxLines: number = THINKING_TAIL_LINES,\n\t) {\n\t\tthis.maxLines = maxLines;\n\t\tthis.markdown = new Markdown(text, paddingX, paddingY, markdownTheme, options);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.markdown.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines = this.markdown.render(width);\n\t\tconst tail = lines.length <= this.maxLines ? lines : lines.slice(-this.maxLines);\n\t\tif (tail.length >= this.maxLines) {\n\t\t\treturn tail;\n\t\t}\n\t\tconst pad = this.maxLines - tail.length;\n\t\treturn [...Array.from({ length: pad }, () => \"\"), ...tail];\n\t}\n}\n"]}
@@ -414,7 +414,9 @@ export declare class InteractiveMode {
414
414
  private handleInitCommand;
415
415
  private handleSwarmCommand;
416
416
  private handleResearchCommand;
417
+ private rewindLastTurn;
417
418
  private handleUndoCommand;
419
+ private handleEditCommand;
418
420
  private handleRedoCommand;
419
421
  private handleReloadCommand;
420
422
  private handleExportCommand;