@markusylisiurunen/tau 0.3.40 → 0.3.41

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 (125) hide show
  1. package/README.md +29 -33
  2. package/dist/core/cli.js +2 -2
  3. package/dist/core/cli.js.map +1 -1
  4. package/dist/core/commands/registry.js +22 -20
  5. package/dist/core/commands/registry.js.map +1 -1
  6. package/dist/core/config/builtin_themes.js +936 -998
  7. package/dist/core/config/builtin_themes.js.map +1 -1
  8. package/dist/core/personas.js +8 -53
  9. package/dist/core/personas.js.map +1 -1
  10. package/dist/core/session/direct_bash.js +8 -8
  11. package/dist/core/session/direct_bash.js.map +1 -1
  12. package/dist/core/session/runner.js +6 -1
  13. package/dist/core/session/runner.js.map +1 -1
  14. package/dist/core/tools/bash.js +80 -63
  15. package/dist/core/tools/bash.js.map +1 -1
  16. package/dist/core/tools/code_mode.js +33 -12
  17. package/dist/core/tools/code_mode.js.map +1 -1
  18. package/dist/core/tools/edit.js +72 -84
  19. package/dist/core/tools/edit.js.map +1 -1
  20. package/dist/core/tools/goal.js +97 -16
  21. package/dist/core/tools/goal.js.map +1 -1
  22. package/dist/core/tools/history.js +3 -3
  23. package/dist/core/tools/history.js.map +1 -1
  24. package/dist/core/tools/interrupt_agent.js +56 -37
  25. package/dist/core/tools/interrupt_agent.js.map +1 -1
  26. package/dist/core/tools/list_agents.js +28 -13
  27. package/dist/core/tools/list_agents.js.map +1 -1
  28. package/dist/core/tools/nook.js +3 -3
  29. package/dist/core/tools/nook.js.map +1 -1
  30. package/dist/core/tools/presentation.js +226 -0
  31. package/dist/core/tools/presentation.js.map +1 -0
  32. package/dist/core/tools/registry.js.map +1 -1
  33. package/dist/core/tools/send_input_to_agent.js +57 -47
  34. package/dist/core/tools/send_input_to_agent.js.map +1 -1
  35. package/dist/core/tools/spawn_agent.js +81 -52
  36. package/dist/core/tools/spawn_agent.js.map +1 -1
  37. package/dist/core/tools/subagent_ui.js +14 -38
  38. package/dist/core/tools/subagent_ui.js.map +1 -1
  39. package/dist/core/tools/view_image.js +45 -45
  40. package/dist/core/tools/view_image.js.map +1 -1
  41. package/dist/core/tools/wait_for_agents.js +94 -56
  42. package/dist/core/tools/wait_for_agents.js.map +1 -1
  43. package/dist/core/tools/web.js +3 -3
  44. package/dist/core/tools/web.js.map +1 -1
  45. package/dist/core/tools/write.js +41 -39
  46. package/dist/core/tools/write.js.map +1 -1
  47. package/dist/core/utils/gemini_speech.js +1 -1
  48. package/dist/core/utils/gemini_speech.js.map +1 -1
  49. package/dist/core/utils/subagent_utils.js +3 -3
  50. package/dist/core/utils/subagent_utils.js.map +1 -1
  51. package/dist/core/version.js +1 -1
  52. package/dist/execution/cloudflare_sandbox_execution_environment.js +1 -1
  53. package/dist/execution/cloudflare_sandbox_execution_environment.js.map +1 -1
  54. package/dist/host/client_tool_broker.js +29 -31
  55. package/dist/host/client_tool_broker.js.map +1 -1
  56. package/dist/host/local_session_host.js +10 -4
  57. package/dist/host/local_session_host.js.map +1 -1
  58. package/dist/main.js +6 -6
  59. package/dist/main.js.map +1 -1
  60. package/dist/tui/chat_controller/diff_review_service.js +1 -1
  61. package/dist/tui/chat_controller/diff_review_service.js.map +1 -1
  62. package/dist/tui/chat_controller/history_labels.js +13 -0
  63. package/dist/tui/chat_controller/history_labels.js.map +1 -1
  64. package/dist/tui/chat_view.js +24 -50
  65. package/dist/tui/chat_view.js.map +1 -1
  66. package/dist/tui/session_chat_app.js +3 -4
  67. package/dist/tui/session_chat_app.js.map +1 -1
  68. package/dist/tui/session_chat_controller.js +80 -111
  69. package/dist/tui/session_chat_controller.js.map +1 -1
  70. package/dist/tui/terminal_appearance.js +44 -25
  71. package/dist/tui/terminal_appearance.js.map +1 -1
  72. package/dist/tui/ui/autocomplete_item.js +2 -0
  73. package/dist/tui/ui/autocomplete_item.js.map +1 -0
  74. package/dist/tui/ui/chat_container.js +1 -14
  75. package/dist/tui/ui/chat_container.js.map +1 -1
  76. package/dist/tui/ui/chat_message_model.js +4 -12
  77. package/dist/tui/ui/chat_message_model.js.map +1 -1
  78. package/dist/tui/ui/components/autocomplete_list.js +138 -0
  79. package/dist/tui/ui/components/autocomplete_list.js.map +1 -0
  80. package/dist/tui/ui/components/editor.js +33 -40
  81. package/dist/tui/ui/components/editor.js.map +1 -1
  82. package/dist/tui/ui/components/one_line_segments.js +0 -67
  83. package/dist/tui/ui/components/one_line_segments.js.map +1 -1
  84. package/dist/tui/ui/custom_editor.js +15 -11
  85. package/dist/tui/ui/custom_editor.js.map +1 -1
  86. package/dist/tui/ui/diff_review_message.js +1 -11
  87. package/dist/tui/ui/diff_review_message.js.map +1 -1
  88. package/dist/tui/ui/footer.js +63 -13
  89. package/dist/tui/ui/footer.js.map +1 -1
  90. package/dist/tui/ui/rewind_picker.js +17 -47
  91. package/dist/tui/ui/rewind_picker.js.map +1 -1
  92. package/dist/tui/ui/slash_autocomplete.js +43 -64
  93. package/dist/tui/ui/slash_autocomplete.js.map +1 -1
  94. package/dist/tui/ui/subagent_editor_pane.js +3 -3
  95. package/dist/tui/ui/theme/index.js +1 -1
  96. package/dist/tui/ui/theme/index.js.map +1 -1
  97. package/dist/tui/ui/theme/palette.js +245 -8
  98. package/dist/tui/ui/theme/palette.js.map +1 -1
  99. package/dist/tui/ui/theme/theme.js +3 -30
  100. package/dist/tui/ui/theme/theme.js.map +1 -1
  101. package/dist/tui/ui/tool_card.js +137 -0
  102. package/dist/tui/ui/tool_card.js.map +1 -0
  103. package/dist/tui/ui/user_message.js +3 -6
  104. package/dist/tui/ui/user_message.js.map +1 -1
  105. package/package.json +2 -2
  106. package/dist/core/utils/tool_preview.js +0 -73
  107. package/dist/core/utils/tool_preview.js.map +0 -1
  108. package/dist/tui/chat_controller/command_hints.js +0 -35
  109. package/dist/tui/chat_controller/command_hints.js.map +0 -1
  110. package/dist/tui/ui/bash_execution.js +0 -129
  111. package/dist/tui/ui/bash_execution.js.map +0 -1
  112. package/dist/tui/ui/components/dynamic_border.js +0 -11
  113. package/dist/tui/ui/components/dynamic_border.js.map +0 -1
  114. package/dist/tui/ui/components/header_box.js +0 -107
  115. package/dist/tui/ui/components/header_box.js.map +0 -1
  116. package/dist/tui/ui/components/header_line.js +0 -60
  117. package/dist/tui/ui/components/header_line.js.map +0 -1
  118. package/dist/tui/ui/components/padded_container.js +0 -24
  119. package/dist/tui/ui/components/padded_container.js.map +0 -1
  120. package/dist/tui/ui/file_execution.js +0 -176
  121. package/dist/tui/ui/file_execution.js.map +0 -1
  122. package/dist/tui/ui/tool_output.js +0 -163
  123. package/dist/tui/ui/tool_output.js.map +0 -1
  124. package/dist/tui/ui/tool_ui_registry.js +0 -786
  125. package/dist/tui/ui/tool_ui_registry.js.map +0 -1
@@ -6,8 +6,10 @@ import { buildDiffReviewInstructions } from "../core/diff_review/review_instruct
6
6
  import { createDefaultCoreDeps } from "../core/runtime/deps.js";
7
7
  import { runDirectBashCommand } from "../core/session/direct_bash.js";
8
8
  import { SUBAGENT_ACTIVITY_FACET_KIND } from "../core/subagents/types.js";
9
+ import { buildToolRunPresentation, parseToolRunPresentation, TOOL_UI_FACET_VERSION, } from "../core/tools/presentation.js";
10
+ import { TOOL_NAME_BASH } from "../core/tools/tool_names.js";
9
11
  import { REASONING_LEVELS } from "../core/types.js";
10
- import { formatAdaptiveNumber, formatTokenWindow } from "../core/utils/format.js";
12
+ import { formatAdaptiveNumber, formatCwd, formatTokenWindow } from "../core/utils/format.js";
11
13
  import { extractAssistantText } from "../core/utils/messages.js";
12
14
  import { collectSpeechToTextContext } from "../core/utils/speech_to_text_context.js";
13
15
  import { getAutoCompactionMetadataFromMessage, hasAutoCompactionContinuationMetadata, } from "../core/utils/user_metadata.js";
@@ -15,10 +17,9 @@ import { APP_VERSION } from "../core/version.js";
15
17
  import { applySessionProtocolDelta } from "../protocol/session_protocol.js";
16
18
  import { TauSessionProtocolResponseError } from "../transport/errors.js";
17
19
  import { copyAssistantCodeToClipboard, copyAssistantTextToClipboard, } from "./chat_controller/assistant_clipboard.js";
18
- import { getCommandHint } from "./chat_controller/command_hints.js";
19
20
  import { DiffReviewService, } from "./chat_controller/diff_review_service.js";
20
21
  import { formatDiffReviewUserMessage } from "./chat_controller/diff_review_user_message.js";
21
- import { formatRewindCandidateLabel } from "./chat_controller/history_labels.js";
22
+ import { formatRewindCandidateAge, formatRewindCandidateLabel, } from "./chat_controller/history_labels.js";
22
23
  import { buildHistoryMessageModel, extractHistoryUserText, } from "./chat_controller/history_message_model.js";
23
24
  import { InterruptLifecycle } from "./chat_controller/interrupt_lifecycle.js";
24
25
  import { formatDurationMs, formatSessionCost } from "./chat_controller/status_format.js";
@@ -61,8 +62,6 @@ export class SessionChatController {
61
62
  isBashMode = false;
62
63
  isBashIncognito = false;
63
64
  showThinking = false;
64
- compactToolUi = true;
65
- commandHint;
66
65
  currentTurnStartedAt;
67
66
  lastTurnDurationMs = 0;
68
67
  turnTimer;
@@ -123,7 +122,6 @@ export class SessionChatController {
123
122
  }
124
123
  start() {
125
124
  this.view.setThinkingVisibility(this.showThinking);
126
- this.view.setCompactToolUi(this.compactToolUi);
127
125
  this.view.addMessage({
128
126
  type: "app_intro",
129
127
  title: this.buildStartupIntroTitle(),
@@ -161,7 +159,6 @@ export class SessionChatController {
161
159
  getInputHandlers() {
162
160
  return {
163
161
  onCtrlT: () => this.toggleThinkingVisibility(),
164
- onCtrlO: () => this.toggleCompactToolUi(),
165
162
  onShiftTab: () => void this.cycleReasoningLevel(),
166
163
  onCtrlP: () => void this.cyclePersonality(),
167
164
  onCtrlS: () => void this.stashEditorToClipboard(),
@@ -281,13 +278,12 @@ export class SessionChatController {
281
278
  }), "muted");
282
279
  }
283
280
  switchTheme(themeId) {
284
- this.view.updateTheme({ themeId });
281
+ this.view.updateTheme(themeId);
285
282
  this.view.addSystemMessage(`theme set to ${themeId}`, "success");
286
283
  }
287
284
  handleEditorChange(text) {
288
285
  const wasBash = this.isBashMode;
289
286
  const wasBashIncognito = this.isBashIncognito;
290
- const previousCommandHint = this.commandHint;
291
287
  if (text.trim().length > 0) {
292
288
  this.lastEmptySubmitAt = undefined;
293
289
  }
@@ -295,24 +291,10 @@ export class SessionChatController {
295
291
  const isIncognito = trimmed.startsWith("!!");
296
292
  this.isBashIncognito = isIncognito;
297
293
  this.isBashMode = trimmed.startsWith("!") && !isIncognito;
298
- this.commandHint = this.getCommandHintForInput(text);
299
- if (wasBash !== this.isBashMode ||
300
- wasBashIncognito !== this.isBashIncognito ||
301
- previousCommandHint !== this.commandHint) {
294
+ if (wasBash !== this.isBashMode || wasBashIncognito !== this.isBashIncognito) {
302
295
  this.refreshStatus();
303
296
  }
304
297
  }
305
- getCommandHintForInput(text) {
306
- const trimmed = text.trimStart();
307
- if (!trimmed.startsWith("/") || !this.isSingleLineInput(text)) {
308
- return undefined;
309
- }
310
- const parsed = this.commandRegistry.parse(trimmed);
311
- if (parsed.type === "unknown") {
312
- return undefined;
313
- }
314
- return getCommandHint(parsed);
315
- }
316
298
  getAutocompleteSources() {
317
299
  return {
318
300
  personas: () => this.snapshot.catalog.personas.map((persona) => ({
@@ -381,21 +363,18 @@ export class SessionChatController {
381
363
  return;
382
364
  }
383
365
  const toolCallId = `bash-user-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
384
- const headerTarget = command.split(/\r?\n/, 1)[0] ?? command;
366
+ const workingDirectory = this.snapshot.executionEnvironment.cwd;
385
367
  this.isStreaming = true;
386
368
  this.startTurnTimer();
387
369
  this.view.startWorkingIcon();
388
370
  this.view.updateLocalToolUi({
389
371
  toolCallId,
390
- toolName: "bash",
391
372
  status: "running",
392
- headerTarget,
393
- activity: {
394
- type: "bash_started",
395
- toolCallId,
396
- command,
397
- headerTarget,
398
- },
373
+ presentation: buildToolRunPresentation({
374
+ toolName: TOOL_NAME_BASH,
375
+ subject: command,
376
+ metadata: [formatCwd(workingDirectory)],
377
+ }),
399
378
  });
400
379
  this.refreshStatus();
401
380
  try {
@@ -406,6 +385,8 @@ export class SessionChatController {
406
385
  const result = await runDirectBashCommand({
407
386
  command,
408
387
  backend,
388
+ workingDirectory,
389
+ actionLabel: options.labelOverride,
409
390
  addToContext: options.addToContext,
410
391
  addUserText: async (text) => {
411
392
  const recorded = await this.session.record(text, {
@@ -420,21 +401,8 @@ export class SessionChatController {
420
401
  }
421
402
  this.view.updateLocalToolUi({
422
403
  toolCallId,
423
- toolName: "bash",
424
404
  status: result.exitCode === 0 ? "succeeded" : "failed",
425
- headerTarget,
426
- activity: {
427
- type: "bash_execution",
428
- toolCallId,
429
- command: result.command,
430
- headerTarget,
431
- exitCode: result.exitCode,
432
- truncationInfo: result.truncationInfo,
433
- uiText: result.uiText,
434
- durationMs: result.durationMs,
435
- labelOverride: options.labelOverride,
436
- },
437
- resultText: result.uiText.fullLines.map((line) => line.text).join("\n"),
405
+ presentation: result.presentation,
438
406
  });
439
407
  await this.syncFromSessionSnapshot();
440
408
  }
@@ -442,17 +410,13 @@ export class SessionChatController {
442
410
  const reason = error.message || "bash failed";
443
411
  this.view.updateLocalToolUi({
444
412
  toolCallId,
445
- toolName: "bash",
446
413
  status: "blocked",
447
- headerTarget,
448
- activity: {
449
- type: "bash_blocked",
450
- toolCallId,
451
- command,
452
- headerTarget,
453
- reason,
454
- },
455
- resultText: reason,
414
+ presentation: buildToolRunPresentation({
415
+ toolName: TOOL_NAME_BASH,
416
+ subject: command,
417
+ details: [{ text: reason }],
418
+ metadata: [formatCwd(workingDirectory)],
419
+ }),
456
420
  });
457
421
  await this.syncFromSessionSnapshot();
458
422
  }
@@ -1276,11 +1240,6 @@ export class SessionChatController {
1276
1240
  this.view.setThinkingVisibility(this.showThinking);
1277
1241
  this.view.addSystemMessage(this.showThinking ? "thoughts visible" : "thoughts hidden", "success");
1278
1242
  }
1279
- toggleCompactToolUi() {
1280
- this.compactToolUi = !this.compactToolUi;
1281
- this.view.setCompactToolUi(this.compactToolUi);
1282
- this.view.addSystemMessage(this.compactToolUi ? "compact tool ui enabled" : "compact tool ui disabled", "success");
1283
- }
1284
1243
  async cyclePersonality() {
1285
1244
  const personas = this.snapshot.catalog.personas;
1286
1245
  if (personas.length === 0) {
@@ -1515,15 +1474,15 @@ export class SessionChatController {
1515
1474
  refreshStatus() {
1516
1475
  this.view.updateStatus({
1517
1476
  footer: {
1477
+ cwdLabel: this.getFooterCwdLabel(),
1518
1478
  contextUsage: this.getContextUsageString(),
1519
1479
  sessionCost: this.getSessionCostString(),
1520
1480
  duration: this.getTurnDurationString(),
1521
- commandHint: this.diffReviewService.getCommandHint(this.getSessionOperationStatusHint() ?? this.speechStatusHint ?? this.commandHint),
1481
+ statusHint: this.diffReviewService.getStatusHint(this.getSessionOperationStatusHint() ?? this.speechStatusHint),
1522
1482
  pursuingGoal: this.snapshot.goal?.status === "active",
1523
1483
  },
1524
1484
  editor: {
1525
1485
  mode: this.getInputMode(),
1526
- cwdLabel: this.getFooterCwdLabel(),
1527
1486
  personaName: this.getCurrentPersonaSnapshot()?.label ?? this.snapshot.settings.personaId,
1528
1487
  reasoningLabel: this.snapshot.settings.reasoning ?? "none",
1529
1488
  reasoning: this.snapshot.settings.reasoning === undefined ||
@@ -1560,7 +1519,7 @@ export class SessionChatController {
1560
1519
  ? this.getContextWindowForLastTurn(last)
1561
1520
  : this.getBootstrapContextWindow();
1562
1521
  const { input, read, write, output } = this.getSessionUsageTotals();
1563
- const stats = `↑${formatTokenWindow(input)} ↓${formatTokenWindow(output)} (r${formatTokenWindow(read)} w${formatTokenWindow(write)})`;
1522
+ const stats = `↑${formatTokenWindow(input)} ↓${formatTokenWindow(output)} r${formatTokenWindow(read)} w${formatTokenWindow(write)}`;
1564
1523
  const contextWindowUsageTokens = getAssistantContextWindowUsage(last);
1565
1524
  const percent = windowTokens > 0 ? (contextWindowUsageTokens / windowTokens) * 100 : 0;
1566
1525
  const percentStr = `${formatAdaptiveNumber(percent, 1, 3)}%`;
@@ -1807,6 +1766,7 @@ export class SessionChatController {
1807
1766
  if (this.isSessionOperationActive()) {
1808
1767
  return;
1809
1768
  }
1769
+ const now = Date.now();
1810
1770
  const candidates = this.snapshot.messages.flatMap((entry) => {
1811
1771
  if (!entry.modelVisible ||
1812
1772
  entry.message.role !== "user" ||
@@ -1822,6 +1782,7 @@ export class SessionChatController {
1822
1782
  id: entry.id,
1823
1783
  text,
1824
1784
  label: formatRewindCandidateLabel(text),
1785
+ description: formatRewindCandidateAge(entry.message.timestamp, now),
1825
1786
  },
1826
1787
  ];
1827
1788
  });
@@ -1830,7 +1791,7 @@ export class SessionChatController {
1830
1791
  return;
1831
1792
  }
1832
1793
  this.view.showRewindPicker({
1833
- items: candidates.map(({ id, label }) => ({ id, label })),
1794
+ items: candidates.map(({ id, label, description }) => ({ id, label, description })),
1834
1795
  onSelect: (id) => {
1835
1796
  const selected = candidates.find((candidate) => candidate.id === id);
1836
1797
  if (!selected) {
@@ -2015,14 +1976,15 @@ function isCoreMessage(message) {
2015
1976
  return false;
2016
1977
  }
2017
1978
  }
2018
- function isToolUiEvent(value) {
2019
- return (typeof value === "object" &&
2020
- value !== null &&
2021
- "type" in value &&
2022
- typeof value.type === "string" &&
2023
- "toolCallId" in value &&
2024
- typeof value.toolCallId === "string");
2025
- }
1979
+ const RECOVERED_TOOL_ACTION_LABELS = {
1980
+ preparing: "preparing",
1981
+ queued: "queued",
1982
+ running: "running",
1983
+ succeeded: "completed",
1984
+ failed: "failed",
1985
+ blocked: "blocked",
1986
+ cancelled: "cancelled",
1987
+ };
2026
1988
  function getToolUiModelsInModelOrder(snapshot) {
2027
1989
  const facetsByToolId = new Map();
2028
1990
  for (const facet of Object.values(snapshot.facets)) {
@@ -2030,47 +1992,60 @@ function getToolUiModelsInModelOrder(snapshot) {
2030
1992
  facetsByToolId.set(facet.subject.id, facet);
2031
1993
  }
2032
1994
  }
2033
- const messagesById = new Map(snapshot.messages.map((message) => [message.id, message]));
2034
1995
  return getToolIdsInModelOrder(snapshot).map((toolId) => {
2035
1996
  const tool = snapshot.tools[toolId];
2036
- const activities = toolUiEventsFromFacet(facetsByToolId.get(toolId));
2037
- const activity = activities.at(-1);
2038
- const resultMessage = tool.status === "streaming" ? undefined : messagesById.get(tool.resultMessageId ?? "");
2039
- const resultMessageText = resultMessage?.message.role === "toolResult"
2040
- ? resultMessage.message.content
2041
- .flatMap((content) => (content.type === "text" ? [content.text] : []))
2042
- .join("\n")
2043
- : undefined;
2044
- const resultText = resultMessageText || (tool.status === "streaming" ? undefined : (tool.error ?? tool.summary));
2045
- const activityCode = activity && "code" in activity ? activity.code : undefined;
2046
- const code = activityCode ?? getToolCallCode(tool, messagesById);
2047
1997
  return {
2048
1998
  toolCallId: tool.toolCallId,
2049
- toolName: tool.toolName,
2050
1999
  status: tool.status,
2051
- headerTarget: activity?.headerTarget ?? tool.toolName,
2052
- ...(code !== undefined ? { code } : {}),
2053
- ...(activity ? { activity } : {}),
2054
- ...(resultText ? { resultText } : {}),
2000
+ presentation: getToolPresentation(snapshot, tool, facetsByToolId.get(toolId)),
2055
2001
  };
2056
2002
  });
2057
2003
  }
2058
- function getToolCallCode(tool, messagesById) {
2059
- if (tool.status === "streaming")
2060
- return undefined;
2061
- const message = messagesById.get(tool.call.messageId);
2062
- if (message?.message.role !== "assistant")
2063
- return undefined;
2064
- const content = message.message.content[tool.call.contentIndex];
2065
- if (content?.type !== "toolCall" ||
2066
- content.id !== tool.toolCallId ||
2067
- typeof content.arguments !== "object" ||
2068
- content.arguments === null ||
2069
- !("code" in content.arguments) ||
2070
- typeof content.arguments.code !== "string") {
2004
+ function getToolPresentation(snapshot, tool, facet) {
2005
+ if (facet?.kind !== "tau.tool-ui-events" || facet.version !== TOOL_UI_FACET_VERSION) {
2006
+ return buildRecoveredToolPresentation(snapshot, tool);
2007
+ }
2008
+ if (!Array.isArray(facet.data.events)) {
2009
+ throw new Error(`missing tool presentation for '${tool.toolCallId}'`);
2010
+ }
2011
+ const event = facet.data.events.at(-1);
2012
+ if (typeof event !== "object" ||
2013
+ event === null ||
2014
+ !("toolCallId" in event) ||
2015
+ event.toolCallId !== tool.toolCallId ||
2016
+ !("presentation" in event)) {
2017
+ throw new Error(`missing tool presentation for '${tool.toolCallId}'`);
2018
+ }
2019
+ return parseToolRunPresentation(event.presentation);
2020
+ }
2021
+ function buildRecoveredToolPresentation(snapshot, tool) {
2022
+ const result = getRecoveredToolResult(snapshot, tool);
2023
+ return buildToolRunPresentation({
2024
+ toolName: tool.toolName,
2025
+ subject: tool.toolName,
2026
+ details: result ? [{ text: result, wrap: "character" }] : [],
2027
+ actionOverrides: RECOVERED_TOOL_ACTION_LABELS,
2028
+ });
2029
+ }
2030
+ function getRecoveredToolResult(snapshot, tool) {
2031
+ if (tool.status === "streaming") {
2071
2032
  return undefined;
2072
2033
  }
2073
- return content.arguments.code;
2034
+ const resultEntry = tool.resultMessageId
2035
+ ? snapshot.messages.find((entry) => entry.id === tool.resultMessageId)
2036
+ : snapshot.messages.find((entry) => entry.message.role === "toolResult" && entry.message.toolCallId === tool.toolCallId);
2037
+ const resultMessage = resultEntry?.message;
2038
+ if (resultMessage?.role === "toolResult" && resultMessage.toolCallId === tool.toolCallId) {
2039
+ const text = resultMessage.content
2040
+ .filter((block) => block.type === "text")
2041
+ .map((block) => block.text)
2042
+ .join("\n")
2043
+ .trimEnd();
2044
+ if (text) {
2045
+ return text;
2046
+ }
2047
+ }
2048
+ return tool.error?.trim() || tool.summary?.trim() || undefined;
2074
2049
  }
2075
2050
  function getToolIdsInModelOrder(snapshot) {
2076
2051
  const messageOrder = new Map(snapshot.messages.map((message, index) => [message.id, index]));
@@ -2085,12 +2060,6 @@ function getToolIdsInModelOrder(snapshot) {
2085
2060
  })
2086
2061
  .map((tool) => tool.id);
2087
2062
  }
2088
- function toolUiEventsFromFacet(facet) {
2089
- if (facet?.kind !== "tau.tool-ui-events" || !Array.isArray(facet.data.events)) {
2090
- return [];
2091
- }
2092
- return facet.data.events.filter(isToolUiEvent);
2093
- }
2094
2063
  function getSubagentActivity(snapshot, agentId) {
2095
2064
  const facet = Object.values(snapshot.facets).find((candidate) => candidate.kind === SUBAGENT_ACTIVITY_FACET_KIND &&
2096
2065
  candidate.subject.type === "agent" &&