@within-7/minto 0.2.0 → 0.3.3

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 (308) hide show
  1. package/dist/commands/agents/AgentsCommand.js +22 -24
  2. package/dist/commands/agents/AgentsCommand.js.map +2 -2
  3. package/dist/commands/context.js +2 -1
  4. package/dist/commands/context.js.map +2 -2
  5. package/dist/commands/export.js +2 -1
  6. package/dist/commands/export.js.map +2 -2
  7. package/dist/commands/mcp-interactive.js +7 -6
  8. package/dist/commands/mcp-interactive.js.map +2 -2
  9. package/dist/commands/model.js +3 -2
  10. package/dist/commands/model.js.map +2 -2
  11. package/dist/commands/permissions.js +4 -3
  12. package/dist/commands/permissions.js.map +2 -2
  13. package/dist/commands/plugin/AddMarketplaceForm.js +3 -2
  14. package/dist/commands/plugin/AddMarketplaceForm.js.map +2 -2
  15. package/dist/commands/plugin/ConfirmDialog.js +2 -1
  16. package/dist/commands/plugin/ConfirmDialog.js.map +2 -2
  17. package/dist/commands/plugin/ErrorView.js +2 -1
  18. package/dist/commands/plugin/ErrorView.js.map +2 -2
  19. package/dist/commands/plugin/InstalledPluginsByMarketplace.js +5 -4
  20. package/dist/commands/plugin/InstalledPluginsByMarketplace.js.map +2 -2
  21. package/dist/commands/plugin/InstalledPluginsManager.js +5 -4
  22. package/dist/commands/plugin/InstalledPluginsManager.js.map +2 -2
  23. package/dist/commands/plugin/MainMenu.js +2 -1
  24. package/dist/commands/plugin/MainMenu.js.map +2 -2
  25. package/dist/commands/plugin/MarketplaceManager.js +5 -4
  26. package/dist/commands/plugin/MarketplaceManager.js.map +2 -2
  27. package/dist/commands/plugin/MarketplaceSelector.js +4 -3
  28. package/dist/commands/plugin/MarketplaceSelector.js.map +2 -2
  29. package/dist/commands/plugin/PlaceholderScreen.js +3 -2
  30. package/dist/commands/plugin/PlaceholderScreen.js.map +2 -2
  31. package/dist/commands/plugin/PluginBrowser.js +6 -5
  32. package/dist/commands/plugin/PluginBrowser.js.map +2 -2
  33. package/dist/commands/plugin/PluginDetailsInstall.js +5 -4
  34. package/dist/commands/plugin/PluginDetailsInstall.js.map +2 -2
  35. package/dist/commands/plugin/PluginDetailsManage.js +4 -3
  36. package/dist/commands/plugin/PluginDetailsManage.js.map +2 -2
  37. package/dist/commands/plugin.js +16 -15
  38. package/dist/commands/plugin.js.map +2 -2
  39. package/dist/commands/sandbox.js +4 -3
  40. package/dist/commands/sandbox.js.map +2 -2
  41. package/dist/commands/setup.js +2 -1
  42. package/dist/commands/setup.js.map +2 -2
  43. package/dist/commands/status.js +2 -1
  44. package/dist/commands/status.js.map +2 -2
  45. package/dist/commands/undo.js +245 -0
  46. package/dist/commands/undo.js.map +7 -0
  47. package/dist/commands.js +2 -0
  48. package/dist/commands.js.map +2 -2
  49. package/dist/components/AgentThinkingBlock.js +1 -1
  50. package/dist/components/AgentThinkingBlock.js.map +2 -2
  51. package/dist/components/AsciiLogo.js +7 -8
  52. package/dist/components/AsciiLogo.js.map +2 -2
  53. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js +3 -2
  54. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
  55. package/dist/components/AskUserQuestionDialog/QuestionView.js +2 -1
  56. package/dist/components/AskUserQuestionDialog/QuestionView.js.map +2 -2
  57. package/dist/components/CollapsibleHint.js +2 -1
  58. package/dist/components/CollapsibleHint.js.map +2 -2
  59. package/dist/components/Config.js +3 -2
  60. package/dist/components/Config.js.map +2 -2
  61. package/dist/components/ConsoleOAuthFlow.js +2 -1
  62. package/dist/components/ConsoleOAuthFlow.js.map +2 -2
  63. package/dist/components/Cost.js +2 -1
  64. package/dist/components/Cost.js.map +2 -2
  65. package/dist/components/HeaderBar.js +13 -8
  66. package/dist/components/HeaderBar.js.map +2 -2
  67. package/dist/components/HistorySearchOverlay.js +4 -3
  68. package/dist/components/HistorySearchOverlay.js.map +2 -2
  69. package/dist/components/HotkeyHelpPanel.js +8 -11
  70. package/dist/components/HotkeyHelpPanel.js.map +2 -2
  71. package/dist/components/InvalidConfigDialog.js +2 -1
  72. package/dist/components/InvalidConfigDialog.js.map +2 -2
  73. package/dist/components/Logo.js +23 -67
  74. package/dist/components/Logo.js.map +2 -2
  75. package/dist/components/MCPServerApprovalDialog.js +2 -1
  76. package/dist/components/MCPServerApprovalDialog.js.map +2 -2
  77. package/dist/components/MCPServerDialogCopy.js +2 -1
  78. package/dist/components/MCPServerDialogCopy.js.map +2 -2
  79. package/dist/components/MCPServerMultiselectDialog.js +2 -1
  80. package/dist/components/MCPServerMultiselectDialog.js.map +2 -2
  81. package/dist/components/MessageSelector.js +4 -3
  82. package/dist/components/MessageSelector.js.map +2 -2
  83. package/dist/components/ModeIndicator.js +2 -1
  84. package/dist/components/ModeIndicator.js.map +2 -2
  85. package/dist/components/ModelConfig.js +4 -3
  86. package/dist/components/ModelConfig.js.map +2 -2
  87. package/dist/components/ModelListManager.js +4 -3
  88. package/dist/components/ModelListManager.js.map +2 -2
  89. package/dist/components/ModelSelector/ModelSelector.js +26 -13
  90. package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
  91. package/dist/components/Onboarding.js +3 -2
  92. package/dist/components/Onboarding.js.map +2 -2
  93. package/dist/components/OperationSummary.js +130 -0
  94. package/dist/components/OperationSummary.js.map +7 -0
  95. package/dist/components/PromptInput.js +88 -75
  96. package/dist/components/PromptInput.js.map +2 -2
  97. package/dist/components/SensitiveFileWarning.js +31 -0
  98. package/dist/components/SensitiveFileWarning.js.map +7 -0
  99. package/dist/components/Spinner.js +71 -22
  100. package/dist/components/Spinner.js.map +2 -2
  101. package/dist/components/StructuredDiff.js +6 -8
  102. package/dist/components/StructuredDiff.js.map +2 -2
  103. package/dist/components/SubagentBlock.js +4 -2
  104. package/dist/components/SubagentBlock.js.map +2 -2
  105. package/dist/components/SubagentProgress.js +17 -6
  106. package/dist/components/SubagentProgress.js.map +2 -2
  107. package/dist/components/TaskCard.js +14 -11
  108. package/dist/components/TaskCard.js.map +2 -2
  109. package/dist/components/TextInput.js +9 -1
  110. package/dist/components/TextInput.js.map +2 -2
  111. package/dist/components/TodoPanel.js +44 -26
  112. package/dist/components/TodoPanel.js.map +2 -2
  113. package/dist/components/ToolUseLoader.js +2 -2
  114. package/dist/components/ToolUseLoader.js.map +2 -2
  115. package/dist/components/TreeConnector.js +4 -3
  116. package/dist/components/TreeConnector.js.map +2 -2
  117. package/dist/components/TrustDialog.js +2 -1
  118. package/dist/components/TrustDialog.js.map +2 -2
  119. package/dist/components/binary-feedback/BinaryFeedbackView.js +2 -1
  120. package/dist/components/binary-feedback/BinaryFeedbackView.js.map +2 -2
  121. package/dist/components/messages/AssistantTextMessage.js +17 -9
  122. package/dist/components/messages/AssistantTextMessage.js.map +2 -2
  123. package/dist/components/messages/AssistantToolUseMessage.js +8 -4
  124. package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
  125. package/dist/components/messages/GroupRenderer.js +2 -1
  126. package/dist/components/messages/GroupRenderer.js.map +2 -2
  127. package/dist/components/messages/NestedTasksPreview.js +13 -1
  128. package/dist/components/messages/NestedTasksPreview.js.map +2 -2
  129. package/dist/components/messages/ParallelTasksGroupView.js +4 -3
  130. package/dist/components/messages/ParallelTasksGroupView.js.map +2 -2
  131. package/dist/components/messages/TaskInModuleView.js +35 -15
  132. package/dist/components/messages/TaskInModuleView.js.map +2 -2
  133. package/dist/components/messages/TaskOutputContent.js +9 -6
  134. package/dist/components/messages/TaskOutputContent.js.map +2 -2
  135. package/dist/components/messages/UserPromptMessage.js +2 -2
  136. package/dist/components/messages/UserPromptMessage.js.map +2 -2
  137. package/dist/constants/colors.js +90 -72
  138. package/dist/constants/colors.js.map +2 -2
  139. package/dist/constants/prompts.js +22 -1
  140. package/dist/constants/prompts.js.map +2 -2
  141. package/dist/constants/toolInputExamples.js +84 -0
  142. package/dist/constants/toolInputExamples.js.map +7 -0
  143. package/dist/core/backupManager.js +321 -0
  144. package/dist/core/backupManager.js.map +7 -0
  145. package/dist/core/costTracker.js +9 -18
  146. package/dist/core/costTracker.js.map +2 -2
  147. package/dist/core/gitAutoCommit.js +287 -0
  148. package/dist/core/gitAutoCommit.js.map +7 -0
  149. package/dist/core/index.js +3 -0
  150. package/dist/core/index.js.map +2 -2
  151. package/dist/core/operationTracker.js +212 -0
  152. package/dist/core/operationTracker.js.map +7 -0
  153. package/dist/core/permissions/rules/allowedToolsRule.js +1 -1
  154. package/dist/core/permissions/rules/allowedToolsRule.js.map +2 -2
  155. package/dist/core/permissions/rules/autoEscalationRule.js +5 -0
  156. package/dist/core/permissions/rules/autoEscalationRule.js.map +2 -2
  157. package/dist/core/permissions/rules/projectBoundaryRule.js +5 -0
  158. package/dist/core/permissions/rules/projectBoundaryRule.js.map +2 -2
  159. package/dist/core/permissions/rules/sensitivePathsRule.js +5 -0
  160. package/dist/core/permissions/rules/sensitivePathsRule.js.map +2 -2
  161. package/dist/core/tokenStats.js +9 -0
  162. package/dist/core/tokenStats.js.map +7 -0
  163. package/dist/core/tokenStatsManager.js +331 -0
  164. package/dist/core/tokenStatsManager.js.map +7 -0
  165. package/dist/entrypoints/cli.js +122 -88
  166. package/dist/entrypoints/cli.js.map +2 -2
  167. package/dist/hooks/useAgentTokenStats.js +72 -0
  168. package/dist/hooks/useAgentTokenStats.js.map +7 -0
  169. package/dist/hooks/useAgentTranscripts.js +30 -6
  170. package/dist/hooks/useAgentTranscripts.js.map +2 -2
  171. package/dist/hooks/useLogMessages.js +12 -1
  172. package/dist/hooks/useLogMessages.js.map +2 -2
  173. package/dist/i18n/locales/en.js +6 -5
  174. package/dist/i18n/locales/en.js.map +2 -2
  175. package/dist/i18n/locales/zh-CN.js +6 -5
  176. package/dist/i18n/locales/zh-CN.js.map +2 -2
  177. package/dist/i18n/types.js.map +1 -1
  178. package/dist/permissions.js +147 -1
  179. package/dist/permissions.js.map +2 -2
  180. package/dist/query.js +78 -4
  181. package/dist/query.js.map +3 -3
  182. package/dist/screens/REPL.js +23 -3
  183. package/dist/screens/REPL.js.map +2 -2
  184. package/dist/screens/ResumeConversation.js +2 -0
  185. package/dist/screens/ResumeConversation.js.map +2 -2
  186. package/dist/services/claude.js +54 -3
  187. package/dist/services/claude.js.map +2 -2
  188. package/dist/services/intelligentCompactor.js +1 -1
  189. package/dist/services/intelligentCompactor.js.map +2 -2
  190. package/dist/services/mcpClient.js +81 -25
  191. package/dist/services/mcpClient.js.map +2 -2
  192. package/dist/services/sandbox/filesystemBoundary.js +58 -17
  193. package/dist/services/sandbox/filesystemBoundary.js.map +2 -2
  194. package/dist/services/taskStore.js +205 -0
  195. package/dist/services/taskStore.js.map +7 -0
  196. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +3 -2
  197. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
  198. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js +42 -4
  199. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +2 -2
  200. package/dist/tools/BashTool/BashTool.js +43 -7
  201. package/dist/tools/BashTool/BashTool.js.map +2 -2
  202. package/dist/tools/BashTool/prompt.js +184 -34
  203. package/dist/tools/BashTool/prompt.js.map +2 -2
  204. package/dist/tools/FileEditTool/FileEditTool.js +24 -9
  205. package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
  206. package/dist/tools/FileEditTool/prompt.js +10 -4
  207. package/dist/tools/FileEditTool/prompt.js.map +2 -2
  208. package/dist/tools/FileEditTool/utils.js +10 -4
  209. package/dist/tools/FileEditTool/utils.js.map +2 -2
  210. package/dist/tools/FileReadTool/FileReadTool.js +1 -1
  211. package/dist/tools/FileReadTool/FileReadTool.js.map +1 -1
  212. package/dist/tools/FileReadTool/prompt.js +16 -1
  213. package/dist/tools/FileReadTool/prompt.js.map +2 -2
  214. package/dist/tools/FileWriteTool/FileWriteTool.js +1 -1
  215. package/dist/tools/FileWriteTool/FileWriteTool.js.map +1 -1
  216. package/dist/tools/FileWriteTool/prompt.js +12 -3
  217. package/dist/tools/FileWriteTool/prompt.js.map +2 -2
  218. package/dist/tools/GlobTool/prompt.js +12 -1
  219. package/dist/tools/GlobTool/prompt.js.map +2 -2
  220. package/dist/tools/GrepTool/GrepTool.js +333 -65
  221. package/dist/tools/GrepTool/GrepTool.js.map +2 -2
  222. package/dist/tools/GrepTool/prompt.js +15 -8
  223. package/dist/tools/GrepTool/prompt.js.map +2 -2
  224. package/dist/tools/MultiEditTool/prompt.js +5 -3
  225. package/dist/tools/MultiEditTool/prompt.js.map +2 -2
  226. package/dist/tools/NotebookEditTool/NotebookEditTool.js +59 -46
  227. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
  228. package/dist/tools/NotebookEditTool/prompt.js +1 -1
  229. package/dist/tools/NotebookEditTool/prompt.js.map +1 -1
  230. package/dist/tools/PlanModeTool/EnterPlanModeTool.js +3 -2
  231. package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
  232. package/dist/tools/PlanModeTool/ExitPlanModeTool.js +3 -2
  233. package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
  234. package/dist/tools/PlanModeTool/prompt.js +1 -1
  235. package/dist/tools/PlanModeTool/prompt.js.map +1 -1
  236. package/dist/tools/SkillTool/SkillTool.js +4 -3
  237. package/dist/tools/SkillTool/SkillTool.js.map +2 -2
  238. package/dist/tools/SkillTool/prompt.js +1 -1
  239. package/dist/tools/SkillTool/prompt.js.map +1 -1
  240. package/dist/tools/TaskCreateTool/TaskCreateTool.js +102 -0
  241. package/dist/tools/TaskCreateTool/TaskCreateTool.js.map +7 -0
  242. package/dist/tools/TaskCreateTool/prompt.js +47 -0
  243. package/dist/tools/TaskCreateTool/prompt.js.map +7 -0
  244. package/dist/tools/TaskGetTool/TaskGetTool.js +115 -0
  245. package/dist/tools/TaskGetTool/TaskGetTool.js.map +7 -0
  246. package/dist/tools/TaskGetTool/prompt.js +28 -0
  247. package/dist/tools/TaskGetTool/prompt.js.map +7 -0
  248. package/dist/tools/TaskListTool/TaskListTool.js +102 -0
  249. package/dist/tools/TaskListTool/TaskListTool.js.map +7 -0
  250. package/dist/tools/TaskListTool/prompt.js +27 -0
  251. package/dist/tools/TaskListTool/prompt.js.map +7 -0
  252. package/dist/tools/TaskOutputTool/TaskOutputTool.js +3 -2
  253. package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +2 -2
  254. package/dist/tools/TaskStopTool/TaskStopTool.js +150 -0
  255. package/dist/tools/TaskStopTool/TaskStopTool.js.map +7 -0
  256. package/dist/tools/TaskStopTool/prompt.js +15 -0
  257. package/dist/tools/TaskStopTool/prompt.js.map +7 -0
  258. package/dist/tools/TaskTool/TaskTool.js +49 -1
  259. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  260. package/dist/tools/TaskUpdateTool/TaskUpdateTool.js +134 -0
  261. package/dist/tools/TaskUpdateTool/TaskUpdateTool.js.map +7 -0
  262. package/dist/tools/TaskUpdateTool/prompt.js +81 -0
  263. package/dist/tools/TaskUpdateTool/prompt.js.map +7 -0
  264. package/dist/tools/URLFetcherTool/prompt.js +1 -1
  265. package/dist/tools/URLFetcherTool/prompt.js.map +1 -1
  266. package/dist/tools.js +12 -0
  267. package/dist/tools.js.map +2 -2
  268. package/dist/utils/CircuitBreaker.js +242 -0
  269. package/dist/utils/CircuitBreaker.js.map +7 -0
  270. package/dist/utils/ask.js +2 -0
  271. package/dist/utils/ask.js.map +2 -2
  272. package/dist/utils/config.js +47 -5
  273. package/dist/utils/config.js.map +2 -2
  274. package/dist/utils/credentials/CredentialStore.js +1 -0
  275. package/dist/utils/credentials/CredentialStore.js.map +7 -0
  276. package/dist/utils/credentials/EncryptedFileStore.js +157 -0
  277. package/dist/utils/credentials/EncryptedFileStore.js.map +7 -0
  278. package/dist/utils/credentials/index.js +37 -0
  279. package/dist/utils/credentials/index.js.map +7 -0
  280. package/dist/utils/credentials/migration.js +82 -0
  281. package/dist/utils/credentials/migration.js.map +7 -0
  282. package/dist/utils/markdown.js +13 -1
  283. package/dist/utils/markdown.js.map +2 -2
  284. package/dist/utils/model.js +15 -2
  285. package/dist/utils/model.js.map +2 -2
  286. package/dist/utils/permissions/filesystem.js +5 -1
  287. package/dist/utils/permissions/filesystem.js.map +2 -2
  288. package/dist/utils/ripgrep.js +53 -1
  289. package/dist/utils/ripgrep.js.map +2 -2
  290. package/dist/utils/safePath.js +132 -0
  291. package/dist/utils/safePath.js.map +7 -0
  292. package/dist/utils/sensitiveFiles.js +125 -0
  293. package/dist/utils/sensitiveFiles.js.map +7 -0
  294. package/dist/utils/taskDisplayUtils.js +9 -9
  295. package/dist/utils/taskDisplayUtils.js.map +2 -2
  296. package/dist/utils/terminal.js +12 -0
  297. package/dist/utils/terminal.js.map +2 -2
  298. package/dist/utils/theme.js +6 -6
  299. package/dist/utils/theme.js.map +1 -1
  300. package/dist/utils/toolRiskClassification.js +207 -0
  301. package/dist/utils/toolRiskClassification.js.map +7 -0
  302. package/dist/utils/tooling/safeRender.js +17 -17
  303. package/dist/utils/tooling/safeRender.js.map +2 -2
  304. package/dist/version.js +2 -2
  305. package/dist/version.js.map +1 -1
  306. package/package.json +22 -28
  307. package/dist/hooks/useCancelRequest.js +0 -31
  308. package/dist/hooks/useCancelRequest.js.map +0 -7
@@ -0,0 +1,132 @@
1
+ import { realpathSync, existsSync, lstatSync } from "fs";
2
+ import { resolve, isAbsolute, relative, sep, join } from "path";
3
+ function safeResolvePath(inputPath, cwd, allowedRoot) {
4
+ try {
5
+ const absolutePath = isAbsolute(inputPath) ? resolve(inputPath) : resolve(cwd, inputPath);
6
+ let isSymlink = false;
7
+ let realPath = absolutePath;
8
+ if (existsSync(absolutePath)) {
9
+ try {
10
+ isSymlink = lstatSync(absolutePath).isSymbolicLink();
11
+ realPath = realpathSync(absolutePath);
12
+ } catch (e) {
13
+ realPath = absolutePath;
14
+ }
15
+ } else {
16
+ realPath = findRealPathForNonexistent(absolutePath);
17
+ }
18
+ return validateBoundary(realPath, allowedRoot, isSymlink);
19
+ } catch (e) {
20
+ return {
21
+ valid: false,
22
+ resolvedPath: inputPath,
23
+ isSymlink: false,
24
+ error: `Failed to validate path: ${e instanceof Error ? e.message : String(e)}`
25
+ };
26
+ }
27
+ }
28
+ function findRealPathForNonexistent(filePath) {
29
+ try {
30
+ let currentPath = filePath;
31
+ let depth = 0;
32
+ const maxDepth = 50;
33
+ while (!existsSync(currentPath) && depth < maxDepth) {
34
+ const parent = resolve(currentPath, "..");
35
+ if (parent === currentPath) break;
36
+ currentPath = parent;
37
+ depth++;
38
+ }
39
+ if (existsSync(currentPath)) {
40
+ const realParent = realpathSync(currentPath);
41
+ let remaining = filePath.slice(currentPath.length);
42
+ if (remaining.startsWith(sep)) {
43
+ remaining = remaining.slice(1);
44
+ }
45
+ return remaining ? join(realParent, remaining) : realParent;
46
+ }
47
+ return resolve(filePath);
48
+ } catch (e) {
49
+ return resolve(filePath);
50
+ }
51
+ }
52
+ function validateBoundary(resolvedPath, allowedRoot, isSymlink) {
53
+ let normalizedRoot;
54
+ try {
55
+ if (existsSync(allowedRoot)) {
56
+ normalizedRoot = realpathSync(allowedRoot);
57
+ } else {
58
+ normalizedRoot = resolve(allowedRoot);
59
+ }
60
+ } catch (e) {
61
+ normalizedRoot = resolve(allowedRoot);
62
+ }
63
+ normalizedRoot = normalizePath(normalizedRoot);
64
+ const normalizedResolved = normalizePath(resolvedPath);
65
+ const rel = relative(normalizedRoot, normalizedResolved);
66
+ if (rel.startsWith("..")) {
67
+ return {
68
+ valid: false,
69
+ resolvedPath,
70
+ isSymlink,
71
+ error: `Path escapes sandbox boundary: ${resolvedPath} is outside ${normalizedRoot}`
72
+ };
73
+ }
74
+ if (isAbsolute(rel)) {
75
+ return {
76
+ valid: false,
77
+ resolvedPath,
78
+ isSymlink,
79
+ error: `Path is on different drive or root: ${resolvedPath}`
80
+ };
81
+ }
82
+ return {
83
+ valid: true,
84
+ resolvedPath,
85
+ isSymlink
86
+ };
87
+ }
88
+ function normalizePath(p) {
89
+ const resolved = resolve(p);
90
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
91
+ }
92
+ function hasSymlinksInPath(filePath) {
93
+ try {
94
+ const absolutePath = isAbsolute(filePath) ? filePath : resolve(filePath);
95
+ const parts = absolutePath.split(sep).filter((p) => p);
96
+ let currentPath = "";
97
+ for (const part of parts) {
98
+ if (currentPath) {
99
+ currentPath = resolve(currentPath, part);
100
+ } else {
101
+ currentPath = sep === "\\" ? `${part}\\` : `/${part}`;
102
+ }
103
+ if (!existsSync(currentPath)) break;
104
+ try {
105
+ if (lstatSync(currentPath).isSymbolicLink()) {
106
+ return true;
107
+ }
108
+ } catch (e) {
109
+ }
110
+ }
111
+ return false;
112
+ } catch (e) {
113
+ return false;
114
+ }
115
+ }
116
+ function getRealPath(filePath) {
117
+ try {
118
+ const absolutePath = isAbsolute(filePath) ? filePath : resolve(filePath);
119
+ if (existsSync(absolutePath)) {
120
+ return realpathSync(absolutePath);
121
+ }
122
+ return absolutePath;
123
+ } catch (e) {
124
+ return filePath;
125
+ }
126
+ }
127
+ export {
128
+ getRealPath,
129
+ hasSymlinksInPath,
130
+ safeResolvePath
131
+ };
132
+ //# sourceMappingURL=safePath.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/safePath.ts"],
4
+ "sourcesContent": ["/**\n * Safe Path Resolution Utility\n *\n * Provides secure path resolution with symbolic link tracking and boundary validation.\n * Prevents attackers from escaping sandbox boundaries using symlinks.\n */\n\nimport { realpathSync, existsSync, lstatSync } from 'fs'\nimport { resolve, isAbsolute, relative, sep, join } from 'path'\n\n/**\n * Result of a path validation operation\n */\nexport interface PathValidationResult {\n /** Whether the path is valid and within boundaries */\n valid: boolean\n /** The resolved real path (after following symlinks) */\n resolvedPath: string\n /** Whether the path itself is a symbolic link */\n isSymlink: boolean\n /** Error message if validation failed */\n error?: string\n}\n\n/**\n * Safely resolves a path to its real location, tracking symbolic links.\n * Validates that the final resolved path stays within the allowed boundary.\n *\n * @param inputPath The path to resolve (relative or absolute)\n * @param cwd Current working directory for relative path resolution\n * @param allowedRoot The root directory boundary that must not be escaped\n * @returns PathValidationResult with validation status and resolved path\n *\n * @example\n * const result = safeResolvePath('./file.txt', '/project', '/project')\n * if (result.valid) {\n * // Path is valid and doesn't escape boundary\n * console.log(result.resolvedPath)\n * }\n */\nexport function safeResolvePath(\n inputPath: string,\n cwd: string,\n allowedRoot: string,\n): PathValidationResult {\n try {\n // 1. Resolve to absolute path\n const absolutePath = isAbsolute(inputPath)\n ? resolve(inputPath)\n : resolve(cwd, inputPath)\n\n // 2. Determine if path is a symlink and get real path\n let isSymlink = false\n let realPath = absolutePath\n\n if (existsSync(absolutePath)) {\n // Path exists - check if it's a symlink and follow to real location\n try {\n isSymlink = lstatSync(absolutePath).isSymbolicLink()\n realPath = realpathSync(absolutePath)\n } catch (e) {\n // If stat/realpath fails, use absolute path\n realPath = absolutePath\n }\n } else {\n // Path doesn't exist - try to resolve via parents to detect symlink escapes\n realPath = findRealPathForNonexistent(absolutePath)\n }\n\n // 3. Validate that realPath is within allowed boundary\n return validateBoundary(realPath, allowedRoot, isSymlink)\n } catch (e) {\n return {\n valid: false,\n resolvedPath: inputPath,\n isSymlink: false,\n error: `Failed to validate path: ${e instanceof Error ? e.message : String(e)}`,\n }\n }\n}\n\n/**\n * Attempts to resolve a non-existent path by resolving existing parents.\n * This helps detect symlink escapes for paths that don't yet exist.\n */\nfunction findRealPathForNonexistent(filePath: string): string {\n try {\n // Walk up the path to find the first existing parent\n let currentPath = filePath\n let depth = 0\n const maxDepth = 50 // Prevent infinite loops\n\n while (!existsSync(currentPath) && depth < maxDepth) {\n const parent = resolve(currentPath, '..')\n if (parent === currentPath) break // Reached filesystem root\n currentPath = parent\n depth++\n }\n\n // If we found an existing path, resolve it and reconstruct\n if (existsSync(currentPath)) {\n const realParent = realpathSync(currentPath)\n // Remove the parent path and any leading separator from the remaining portion\n let remaining = filePath.slice(currentPath.length)\n if (remaining.startsWith(sep)) {\n remaining = remaining.slice(1)\n }\n return remaining ? join(realParent, remaining) : realParent\n }\n\n // Fallback: just return resolved path\n return resolve(filePath)\n } catch (e) {\n return resolve(filePath)\n }\n}\n\n/**\n * Validates that a resolved path stays within the allowed boundary.\n *\n * @param resolvedPath The absolute path to validate\n * @param allowedRoot The boundary root directory\n * @param isSymlink Whether the original path was a symlink\n * @returns PathValidationResult indicating if boundary is respected\n */\nfunction validateBoundary(\n resolvedPath: string,\n allowedRoot: string,\n isSymlink: boolean,\n): PathValidationResult {\n let normalizedRoot: string\n\n try {\n // Resolve the boundary root to its real path\n if (existsSync(allowedRoot)) {\n normalizedRoot = realpathSync(allowedRoot)\n } else {\n normalizedRoot = resolve(allowedRoot)\n }\n } catch (e) {\n normalizedRoot = resolve(allowedRoot)\n }\n\n // Normalize both paths for comparison (handle case sensitivity)\n normalizedRoot = normalizePath(normalizedRoot)\n const normalizedResolved = normalizePath(resolvedPath)\n\n // Check if the resolved path is within the boundary\n const rel = relative(normalizedRoot, normalizedResolved)\n\n // Validation fails if:\n // 1. Path goes up to parent directory (..)\n if (rel.startsWith('..')) {\n return {\n valid: false,\n resolvedPath,\n isSymlink,\n error: `Path escapes sandbox boundary: ${resolvedPath} is outside ${normalizedRoot}`,\n }\n }\n\n // 2. Relative path is absolute (different drive on Windows)\n if (isAbsolute(rel)) {\n return {\n valid: false,\n resolvedPath,\n isSymlink,\n error: `Path is on different drive or root: ${resolvedPath}`,\n }\n }\n\n return {\n valid: true,\n resolvedPath,\n isSymlink,\n }\n}\n\n/**\n * Normalizes a path for platform-consistent comparison.\n * On Windows, converts to lowercase for case-insensitive comparison.\n *\n * @param p The path to normalize\n * @returns Normalized path string\n */\nfunction normalizePath(p: string): string {\n const resolved = resolve(p)\n return process.platform === 'win32' ? resolved.toLowerCase() : resolved\n}\n\n/**\n * Checks if a path contains any symlinks in its chain.\n * This is useful for detecting potential symlink attacks.\n *\n * @param filePath The path to check\n * @returns true if any component in the path is a symlink, false otherwise\n *\n * @example\n * const hasSymlinks = hasSymlinksInPath('/path/to/file')\n * if (hasSymlinks) {\n * console.log('Path contains one or more symlinks')\n * }\n */\nexport function hasSymlinksInPath(filePath: string): boolean {\n try {\n const absolutePath = isAbsolute(filePath) ? filePath : resolve(filePath)\n const parts = absolutePath.split(sep).filter(p => p)\n let currentPath = ''\n\n for (const part of parts) {\n if (currentPath) {\n currentPath = resolve(currentPath, part)\n } else {\n // Handle root directory\n currentPath = sep === '\\\\' ? `${part}\\\\` : `/${part}`\n }\n\n if (!existsSync(currentPath)) break\n\n try {\n if (lstatSync(currentPath).isSymbolicLink()) {\n return true\n }\n } catch (e) {\n // If we can't stat it, continue\n }\n }\n\n return false\n } catch (e) {\n return false\n }\n}\n\n/**\n * Gets the real path of a file, following symlinks.\n * Returns the original path if it can't be resolved.\n *\n * @param filePath The path to resolve\n * @returns The real path, or the input path if resolution fails\n */\nexport function getRealPath(filePath: string): string {\n try {\n const absolutePath = isAbsolute(filePath) ? filePath : resolve(filePath)\n if (existsSync(absolutePath)) {\n return realpathSync(absolutePath)\n }\n return absolutePath\n } catch (e) {\n return filePath\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,125 @@
1
+ const YOLO_WARNING_PATTERNS = [
2
+ // Environment files
3
+ "**/.env",
4
+ "**/.env.*",
5
+ // Credential files
6
+ "**/credentials.json",
7
+ "**/secrets.*",
8
+ // Private keys and certificates
9
+ "**/*.pem",
10
+ "**/*.key",
11
+ "**/*.p12",
12
+ "**/*.pfx",
13
+ // Package manager auth
14
+ "**/.npmrc",
15
+ "**/.pypirc",
16
+ // SSH keys
17
+ "**/id_rsa",
18
+ "**/id_ed25519",
19
+ "**/id_ecdsa",
20
+ "**/id_dsa",
21
+ // Other auth files
22
+ "**/.netrc",
23
+ "**/.pgpass",
24
+ "**/config/secrets.*",
25
+ // AWS credentials
26
+ "**/.aws/credentials",
27
+ "**/.aws/config",
28
+ // Google Cloud
29
+ "**/service-account*.json",
30
+ "**/gcloud/credentials.db",
31
+ // Docker secrets
32
+ "**/.docker/config.json",
33
+ // Kubernetes secrets
34
+ "**/kubeconfig",
35
+ "**/.kube/config"
36
+ ];
37
+ function isSensitiveFile(filePath) {
38
+ const normalizedPath = filePath.replace(/\\/g, "/");
39
+ const fileName = normalizedPath.split("/").pop() || "";
40
+ for (const pattern of YOLO_WARNING_PATTERNS) {
41
+ if (pattern.startsWith("**/")) {
42
+ const filePattern = pattern.slice(3);
43
+ if (filePattern.includes("/")) {
44
+ if (matchPathSuffix(normalizedPath, filePattern)) {
45
+ return true;
46
+ }
47
+ } else {
48
+ if (matchFilePattern(fileName, filePattern)) {
49
+ return true;
50
+ }
51
+ }
52
+ } else {
53
+ if (matchFilePattern(normalizedPath, pattern)) {
54
+ return true;
55
+ }
56
+ }
57
+ }
58
+ return false;
59
+ }
60
+ function matchPathSuffix(path, suffixPattern) {
61
+ const regexPattern = suffixPattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
62
+ const regex = new RegExp(`(^|/)${regexPattern}$`);
63
+ return regex.test(path);
64
+ }
65
+ function matchFilePattern(str, pattern) {
66
+ const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "\xA7\xA7").replace(/\*/g, "[^/]*").replace(/§§/g, ".*").replace(/\?/g, ".");
67
+ const regex = new RegExp(`^${regexPattern}$`);
68
+ return regex.test(str);
69
+ }
70
+ function getSensitiveFileReason(filePath) {
71
+ const normalizedPath = filePath.replace(/\\/g, "/").toLowerCase();
72
+ const fileName = normalizedPath.split("/").pop() || "";
73
+ if (fileName === ".env" || fileName.startsWith(".env.")) {
74
+ return "Environment variables file - may contain API keys and secrets";
75
+ }
76
+ if (fileName === "credentials.json") {
77
+ return "Credentials file - may contain authentication tokens";
78
+ }
79
+ if (fileName.startsWith("secrets.") || fileName.includes("/secrets.")) {
80
+ return "Secrets file - may contain sensitive configuration";
81
+ }
82
+ if (fileName.endsWith(".pem") || fileName.endsWith(".key")) {
83
+ return "Private key file - cryptographic key material";
84
+ }
85
+ if (fileName.endsWith(".p12") || fileName.endsWith(".pfx")) {
86
+ return "Certificate file - may contain private keys";
87
+ }
88
+ if (fileName === ".npmrc") {
89
+ return "npm configuration - may contain registry tokens";
90
+ }
91
+ if (fileName === ".pypirc") {
92
+ return "PyPI configuration - may contain upload tokens";
93
+ }
94
+ if (fileName === "id_rsa" || fileName === "id_ed25519" || fileName === "id_ecdsa" || fileName === "id_dsa") {
95
+ return "SSH private key - authentication key";
96
+ }
97
+ if (fileName === ".netrc") {
98
+ return "Netrc file - may contain login credentials";
99
+ }
100
+ if (fileName === ".pgpass") {
101
+ return "PostgreSQL password file";
102
+ }
103
+ if (normalizedPath.includes("/.aws/")) {
104
+ return "AWS configuration - may contain access keys";
105
+ }
106
+ if (fileName.startsWith("service-account") && fileName.endsWith(".json")) {
107
+ return "Service account key - cloud authentication";
108
+ }
109
+ if (normalizedPath.includes("/.docker/config.json")) {
110
+ return "Docker configuration - may contain registry credentials";
111
+ }
112
+ if (fileName === "kubeconfig" || normalizedPath.includes("/.kube/config")) {
113
+ return "Kubernetes configuration - cluster access credentials";
114
+ }
115
+ if (isSensitiveFile(filePath)) {
116
+ return "May contain sensitive information";
117
+ }
118
+ return void 0;
119
+ }
120
+ export {
121
+ YOLO_WARNING_PATTERNS,
122
+ getSensitiveFileReason,
123
+ isSensitiveFile
124
+ };
125
+ //# sourceMappingURL=sensitiveFiles.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/sensitiveFiles.ts"],
4
+ "sourcesContent": ["/**\n * Sensitive File Detection Utility\n *\n * Provides detection for files that may contain sensitive information\n * (API keys, credentials, private keys, etc.).\n *\n * Used in YOLO mode to display informational warnings without blocking operations.\n */\n\n/**\n * Patterns for files that should trigger warnings in YOLO mode.\n * These files commonly contain sensitive information like:\n * - API keys and tokens\n * - Database credentials\n * - Private keys and certificates\n * - Authentication secrets\n */\nexport const YOLO_WARNING_PATTERNS = [\n // Environment files\n '**/.env',\n '**/.env.*',\n\n // Credential files\n '**/credentials.json',\n '**/secrets.*',\n\n // Private keys and certificates\n '**/*.pem',\n '**/*.key',\n '**/*.p12',\n '**/*.pfx',\n\n // Package manager auth\n '**/.npmrc',\n '**/.pypirc',\n\n // SSH keys\n '**/id_rsa',\n '**/id_ed25519',\n '**/id_ecdsa',\n '**/id_dsa',\n\n // Other auth files\n '**/.netrc',\n '**/.pgpass',\n '**/config/secrets.*',\n\n // AWS credentials\n '**/.aws/credentials',\n '**/.aws/config',\n\n // Google Cloud\n '**/service-account*.json',\n '**/gcloud/credentials.db',\n\n // Docker secrets\n '**/.docker/config.json',\n\n // Kubernetes secrets\n '**/kubeconfig',\n '**/.kube/config',\n] as const\n\n/**\n * Check if a file path matches sensitive file patterns.\n * Returns true if the file should trigger a warning (but not block) in YOLO mode.\n *\n * @param filePath - The file path to check (absolute or relative)\n * @returns true if the file matches a sensitive file pattern\n *\n * @example\n * ```typescript\n * isSensitiveFile('/path/to/.env') // true\n * isSensitiveFile('/path/to/config.ts') // false\n * isSensitiveFile('credentials.json') // true\n * isSensitiveFile('/home/user/.aws/credentials') // true\n * ```\n */\nexport function isSensitiveFile(filePath: string): boolean {\n // Normalize path separators (Windows backslashes to forward slashes)\n const normalizedPath = filePath.replace(/\\\\/g, '/')\n\n // Extract the file name for patterns that only match file names\n const fileName = normalizedPath.split('/').pop() || ''\n\n for (const pattern of YOLO_WARNING_PATTERNS) {\n // Handle ** prefix (any directory depth)\n if (pattern.startsWith('**/')) {\n const filePattern = pattern.slice(3)\n\n // Check if the pattern itself contains path separators (like .aws/credentials)\n if (filePattern.includes('/')) {\n // Match against the full path suffix\n if (matchPathSuffix(normalizedPath, filePattern)) {\n return true\n }\n } else {\n // Match just the filename\n if (matchFilePattern(fileName, filePattern)) {\n return true\n }\n }\n } else {\n // Direct pattern match against the full path\n if (matchFilePattern(normalizedPath, pattern)) {\n return true\n }\n }\n }\n\n return false\n}\n\n/**\n * Check if a path ends with a given suffix pattern.\n * Handles glob patterns like .aws/credentials or config/secrets.*\n *\n * @param path - The full normalized path\n * @param suffixPattern - The pattern to match at the end of the path\n */\nfunction matchPathSuffix(path: string, suffixPattern: string): boolean {\n // Build regex from the suffix pattern\n const regexPattern = suffixPattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.')\n\n // Match at the end of the path (with optional leading /)\n const regex = new RegExp(`(^|/)${regexPattern}$`)\n return regex.test(path)\n}\n\n/**\n * Match a string against a glob-like pattern.\n * Supports * (any characters) and ? (single character) wildcards.\n *\n * @param str - The string to test\n * @param pattern - The glob pattern (e.g., \"*.pem\", \".env.*\")\n */\nfunction matchFilePattern(str: string, pattern: string): boolean {\n // Convert glob pattern to regex\n const regexPattern = pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*\\*/g, '\u00A7\u00A7') // Temporarily replace ** to avoid double processing\n .replace(/\\*/g, '[^/]*') // * matches anything except path separator\n .replace(/\u00A7\u00A7/g, '.*') // ** matches anything including path separators\n .replace(/\\?/g, '.')\n\n const regex = new RegExp(`^${regexPattern}$`)\n return regex.test(str)\n}\n\n/**\n * Get a human-readable description of why a file is considered sensitive.\n *\n * @param filePath - The file path to describe\n * @returns A description string or undefined if not sensitive\n */\nexport function getSensitiveFileReason(filePath: string): string | undefined {\n const normalizedPath = filePath.replace(/\\\\/g, '/').toLowerCase()\n const fileName = normalizedPath.split('/').pop() || ''\n\n // Check specific patterns and return appropriate descriptions\n if (fileName === '.env' || fileName.startsWith('.env.')) {\n return 'Environment variables file - may contain API keys and secrets'\n }\n\n if (fileName === 'credentials.json') {\n return 'Credentials file - may contain authentication tokens'\n }\n\n if (fileName.startsWith('secrets.') || fileName.includes('/secrets.')) {\n return 'Secrets file - may contain sensitive configuration'\n }\n\n if (fileName.endsWith('.pem') || fileName.endsWith('.key')) {\n return 'Private key file - cryptographic key material'\n }\n\n if (fileName.endsWith('.p12') || fileName.endsWith('.pfx')) {\n return 'Certificate file - may contain private keys'\n }\n\n if (fileName === '.npmrc') {\n return 'npm configuration - may contain registry tokens'\n }\n\n if (fileName === '.pypirc') {\n return 'PyPI configuration - may contain upload tokens'\n }\n\n if (\n fileName === 'id_rsa' ||\n fileName === 'id_ed25519' ||\n fileName === 'id_ecdsa' ||\n fileName === 'id_dsa'\n ) {\n return 'SSH private key - authentication key'\n }\n\n if (fileName === '.netrc') {\n return 'Netrc file - may contain login credentials'\n }\n\n if (fileName === '.pgpass') {\n return 'PostgreSQL password file'\n }\n\n if (normalizedPath.includes('/.aws/')) {\n return 'AWS configuration - may contain access keys'\n }\n\n if (fileName.startsWith('service-account') && fileName.endsWith('.json')) {\n return 'Service account key - cloud authentication'\n }\n\n if (normalizedPath.includes('/.docker/config.json')) {\n return 'Docker configuration - may contain registry credentials'\n }\n\n if (fileName === 'kubeconfig' || normalizedPath.includes('/.kube/config')) {\n return 'Kubernetes configuration - cluster access credentials'\n }\n\n // Generic fallback for files that match patterns but don't have specific descriptions\n if (isSensitiveFile(filePath)) {\n return 'May contain sensitive information'\n }\n\n return undefined\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,5 +1,5 @@
1
1
  import { getAgentTranscript, getAgentIdByToolUseId } from "./agentTranscripts.js";
2
- import { SEMANTIC_COLORS } from "../constants/colors.js";
2
+ import { SYMBOL_COLORS } from "../constants/colors.js";
3
3
  import { SYMBOLS } from "../constants/symbols.js";
4
4
  function getLatestOutput(transcript) {
5
5
  const messages = transcript.messages;
@@ -74,8 +74,8 @@ function getStatusIcon(status) {
74
74
  return SYMBOLS.TOOL_RUNNING;
75
75
  // ◐ 半圆 - in progress
76
76
  case "completed":
77
- return "\u25CF";
78
- // 实心圆 - completed (视觉进度: ○→◐→●)
77
+ return SYMBOLS.TOOL_SUCCESS;
78
+ // - completed
79
79
  case "failed":
80
80
  return SYMBOLS.TOOL_ERROR;
81
81
  // ✗ - failed
@@ -88,17 +88,17 @@ function getStatusIcon(status) {
88
88
  function getStatusColor(status) {
89
89
  switch (status) {
90
90
  case "pending":
91
- return SEMANTIC_COLORS.dim;
91
+ return SYMBOL_COLORS.pending;
92
92
  case "running":
93
- return SEMANTIC_COLORS.running;
93
+ return SYMBOL_COLORS.running;
94
94
  case "completed":
95
- return SEMANTIC_COLORS.success;
95
+ return SYMBOL_COLORS.success;
96
96
  case "failed":
97
- return SEMANTIC_COLORS.error;
97
+ return SYMBOL_COLORS.error;
98
98
  case "interrupted":
99
- return SEMANTIC_COLORS.dim;
99
+ return SYMBOL_COLORS.pending;
100
100
  default:
101
- return SEMANTIC_COLORS.dim;
101
+ return SYMBOL_COLORS.pending;
102
102
  }
103
103
  }
104
104
  function formatDuration(durationMs) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/taskDisplayUtils.ts"],
4
- "sourcesContent": ["/**\n * Task Display Utilities\n *\n * Utilities for computing task display content with truncation rules.\n * Implements V3's intermediate state display logic.\n */\n\nimport type { ToolUseBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport type { AgentTranscript } from './agentTranscripts'\nimport type {\n DisplayConfig,\n TaskDisplayContent,\n} from '@minto-types/messageGroup'\nimport { getAgentTranscript, getAgentIdByToolUseId } from './agentTranscripts'\nimport { SEMANTIC_COLORS } from '@constants/colors'\nimport { SYMBOLS } from '@constants/symbols'\n\n/**\n * Get the latest text output from a transcript\n */\nexport function getLatestOutput(transcript: AgentTranscript): string {\n const messages = transcript.messages\n\n // Search from the end for the most recent text output\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (msg.type === 'assistant') {\n const content = msg.message.content\n if (!content || !Array.isArray(content)) continue\n\n const textBlock = content.find(b => b.type === 'text')\n if (textBlock && 'text' in textBlock) {\n return textBlock.text\n }\n }\n }\n\n return ''\n}\n\n/**\n * Get all nested Task transcripts from a parent transcript\n */\nexport function getNestedTasks(transcript: AgentTranscript): AgentTranscript[] {\n const nested: AgentTranscript[] = []\n\n for (const msg of transcript.messages) {\n if (msg.type === 'assistant') {\n const content = msg.message.content\n if (!content || !Array.isArray(content)) continue\n\n for (const block of content) {\n if (block.type === 'tool_use' && block.name === 'Task') {\n const toolUse = block as ToolUseBlockParam\n const nestedAgentId = getAgentIdByToolUseId(toolUse.id)\n if (nestedAgentId) {\n const nestedTranscript = getAgentTranscript(nestedAgentId)\n if (nestedTranscript) {\n nested.push(nestedTranscript)\n }\n }\n }\n }\n }\n }\n\n return nested\n}\n\n/**\n * Truncate text to specified length with ellipsis\n */\nexport function truncate(text: string, maxLength: number): string {\n if (!text) return ''\n // Clean up whitespace\n const cleaned = text.trim().replace(/\\n+/g, ' ').replace(/\\s+/g, ' ')\n if (cleaned.length <= maxLength) return cleaned\n return cleaned.slice(0, maxLength - 3) + '...'\n}\n\n/**\n * Get task display content with truncation rules (from V3)\n */\nexport function getTaskDisplayContent(\n transcript: AgentTranscript,\n config: DisplayConfig,\n): TaskDisplayContent {\n const nestedTasks = getNestedTasks(transcript)\n\n if (nestedTasks.length === 0) {\n // No children: show latest output, truncated\n const latestOutput = getLatestOutput(transcript)\n return {\n type: 'simple',\n content: truncate(latestOutput, config.maxCharsWithoutChildren),\n }\n }\n\n // Has children: show recent children based on config\n const tasksToShow = config.showAllChildren\n ? nestedTasks\n : nestedTasks.slice(-config.maxRecentChildren)\n\n const children = tasksToShow.map(task => ({\n description: task.description,\n status: task.status,\n content: truncate(getLatestOutput(task), config.maxCharsPerChild),\n }))\n\n return {\n type: 'nested',\n children,\n hiddenCount: nestedTasks.length - tasksToShow.length,\n }\n}\n\n/**\n * Get status icon for a task status\n */\nexport function getStatusIcon(status: string): string {\n switch (status) {\n case 'pending':\n return '\u25CB' // \u7A7A\u5FC3\u5706 - pending\n case 'running':\n return SYMBOLS.TOOL_RUNNING // \u25D0 \u534A\u5706 - in progress\n case 'completed':\n return '\u25CF' // \u5B9E\u5FC3\u5706 - completed (\u89C6\u89C9\u8FDB\u5EA6: \u25CB\u2192\u25D0\u2192\u25CF)\n case 'failed':\n return SYMBOLS.TOOL_ERROR // \u2717 - failed\n case 'interrupted':\n return '\u2298'\n default:\n return '\u25CB'\n }\n}\n\n/**\n * Get status color for a task status\n */\nexport function getStatusColor(status: string): string {\n switch (status) {\n case 'pending':\n return SEMANTIC_COLORS.dim\n case 'running':\n return SEMANTIC_COLORS.running\n case 'completed':\n return SEMANTIC_COLORS.success\n case 'failed':\n return SEMANTIC_COLORS.error\n case 'interrupted':\n return SEMANTIC_COLORS.dim\n default:\n return SEMANTIC_COLORS.dim\n }\n}\n\n// ============================================================================\n// \u5143\u4FE1\u606F\u683C\u5F0F\u5316 (Meta Info Formatting)\n// \u57FA\u4E8E REPL \u663E\u793A\u89C4\u8303\uFF1A\u8017\u65F6 \u00B7 \u6A21\u578B \u00B7 \u65F6\u95F4\n// ============================================================================\n\n/**\n * \u5143\u4FE1\u606F\u6570\u636E\u7ED3\u6784\n */\nexport interface MetaInfo {\n /** \u8017\u65F6\uFF08\u6BEB\u79D2\uFF09 */\n duration?: number\n /** \u6A21\u578B\u540D\u79F0 */\n model?: string\n /** \u65F6\u95F4\u6233 */\n timestamp?: Date | number\n}\n\n/**\n * \u683C\u5F0F\u5316\u8017\u65F6\n * - < 60s: X.Xs (\u5982 2.4s)\n * - >= 60s: Xm Xs (\u5982 1m 23s)\n * - \u672A\u77E5: --\n */\nexport function formatDuration(durationMs?: number): string {\n if (durationMs === undefined || durationMs === null) {\n return '--'\n }\n\n const seconds = durationMs / 1000\n\n if (seconds < 60) {\n // \u5C0F\u4E8E 60 \u79D2\uFF0C\u663E\u793A X.Xs\n return `${seconds.toFixed(1)}s`\n }\n\n // \u5927\u4E8E\u7B49\u4E8E 60 \u79D2\uFF0C\u663E\u793A Xm Xs\n const minutes = Math.floor(seconds / 60)\n const remainingSeconds = Math.floor(seconds % 60)\n return `${minutes}m ${remainingSeconds}s`\n}\n\n/**\n * \u683C\u5F0F\u5316\u6A21\u578B\u540D\u79F0\uFF08\u7B80\u5199\uFF09\n * - claude-3-opus-20240229 -> opus\n * - claude-3-sonnet-20240229 -> sonnet\n * - glm-4.7 -> glm-4.7\n */\nexport function formatModelName(model?: string): string {\n if (!model) return ''\n\n // Claude \u6A21\u578B\u7B80\u5199\n if (model.includes('opus')) return 'opus'\n if (model.includes('sonnet')) return 'sonnet'\n if (model.includes('haiku')) return 'haiku'\n\n // \u5176\u4ED6\u6A21\u578B\u4FDD\u6301\u539F\u6837\u6216\u622A\u65AD\n // \u5982\u679C\u540D\u79F0\u592A\u957F\uFF0C\u53D6\u6700\u540E\u4E00\u90E8\u5206\n const parts = model.split('-')\n if (parts.length > 2 && model.length > 15) {\n // \u5C1D\u8BD5\u63D0\u53D6\u6709\u610F\u4E49\u7684\u90E8\u5206\n return parts.slice(0, 2).join('-')\n }\n\n return model\n}\n\n/**\n * \u683C\u5F0F\u5316\u65F6\u95F4\u6233\n * \u683C\u5F0F: H:MM (24\u5C0F\u65F6\u5236)\n */\nexport function formatTimestamp(timestamp?: Date | number): string {\n if (!timestamp) return ''\n\n const date = timestamp instanceof Date ? timestamp : new Date(timestamp)\n const hours = date.getHours()\n const minutes = date.getMinutes().toString().padStart(2, '0')\n\n return `${hours}:${minutes}`\n}\n\n/**\n * \u683C\u5F0F\u5316\u5143\u4FE1\u606F\n *\n * \u89C4\u5219\uFF1A\n * - \u6709\u8017\u65F6\uFF1A\u8017\u65F6 \u00B7 \u6A21\u578B \u00B7 \u65F6\u95F4 (\u5982 \"2.4s \u00B7 glm-4.7 \u00B7 9:21\")\n * - \u65E0\u8017\u65F6\uFF1A\u6A21\u578B \u00B7 \u65F6\u95F4 (\u5982 \"glm-4.7 \u00B7 9:21\")\n *\n * @param meta - \u5143\u4FE1\u606F\u5BF9\u8C61\n * @returns \u683C\u5F0F\u5316\u540E\u7684\u5B57\u7B26\u4E32\uFF0C\u5982\u679C\u6CA1\u6709\u4EFB\u4F55\u4FE1\u606F\u5219\u8FD4\u56DE\u7A7A\u5B57\u7B26\u4E32\n */\nexport function formatMetaInfo(meta: MetaInfo): string {\n const parts: string[] = []\n\n // \u6709\u8017\u65F6\u65F6\uFF0C\u8017\u65F6\u653E\u5728\u6700\u524D\u9762\n if (meta.duration !== undefined && meta.duration !== null) {\n parts.push(formatDuration(meta.duration))\n }\n\n // \u6A21\u578B\u540D\u79F0\n const modelName = formatModelName(meta.model)\n if (modelName) {\n parts.push(modelName)\n }\n\n // \u65F6\u95F4\u6233\n const time = formatTimestamp(meta.timestamp)\n if (time) {\n parts.push(time)\n }\n\n // \u4F7F\u7528 \" \u00B7 \" \u5206\u9694\n return parts.join(' \u00B7 ')\n}\n\n/**\n * Tool use history item\n */\nexport interface ToolUseHistoryItem {\n /** Tool name */\n name: string\n /** Tool use ID */\n id: string\n /** Brief description of what the tool did */\n description: string\n /** Timestamp (message index for ordering) */\n messageIndex: number\n /** Whether this tool is currently executing (no result yet) */\n isExecuting?: boolean\n}\n\n/**\n * Get tool use history from a transcript\n * Returns tool uses in chronological order (oldest first)\n * Also marks tools that are currently executing (no result yet)\n */\nexport function getToolUseHistory(\n transcript: AgentTranscript,\n): ToolUseHistoryItem[] {\n const history: ToolUseHistoryItem[] = []\n\n // Collect all tool_result IDs to identify completed tools\n const completedToolIds = new Set<string>()\n for (const msg of transcript.messages) {\n if (msg.type === 'user') {\n const content = msg.message.content\n if (!Array.isArray(content)) continue\n\n for (const block of content) {\n if (block.type === 'tool_result' && 'tool_use_id' in block) {\n completedToolIds.add(block.tool_use_id)\n }\n }\n }\n }\n\n for (let i = 0; i < transcript.messages.length; i++) {\n const msg = transcript.messages[i]\n if (msg.type === 'assistant') {\n const content = msg.message.content\n if (!content || !Array.isArray(content)) continue\n\n for (const block of content) {\n if (block.type === 'tool_use') {\n const toolUse = block as ToolUseBlockParam\n const description = getToolUseDescription(toolUse)\n const isExecuting = !completedToolIds.has(toolUse.id)\n\n history.push({\n name: toolUse.name,\n id: toolUse.id,\n description,\n messageIndex: i,\n isExecuting,\n })\n }\n }\n }\n }\n\n return history\n}\n\n/**\n * Extract just the filename from a path for concise display\n */\nfunction getFileName(filePath: string): string {\n const parts = filePath.split('/')\n return parts[parts.length - 1] || filePath\n}\n\n/**\n * Map internal tool names to user-friendly display names\n * Note: Some tools have different internal names (e.g., 'View' \u2192 'Read')\n */\nconst TOOL_DISPLAY_NAMES: Record<string, string> = {\n View: 'Read',\n // Add more mappings as needed\n}\n\n/**\n * Get the user-friendly display name for a tool\n */\nexport function getToolDisplayName(internalName: string): string {\n return TOOL_DISPLAY_NAMES[internalName] || internalName\n}\n\n/**\n * Get a brief description of what a tool use did\n * Note: Description should NOT include the tool name - it's displayed separately\n */\nfunction getToolUseDescription(toolUse: ToolUseBlockParam): string {\n const input = toolUse.input as Record<string, unknown>\n\n switch (toolUse.name) {\n case 'Read':\n case 'View': // View is aliased to Read\n return input.file_path ? getFileName(String(input.file_path)) : 'file'\n case 'Write':\n return input.file_path ? getFileName(String(input.file_path)) : 'file'\n case 'Edit':\n return input.file_path ? getFileName(String(input.file_path)) : 'file'\n case 'Glob':\n return input.pattern ? String(input.pattern) : 'files'\n case 'Grep':\n return input.pattern ? `\"${input.pattern}\"` : 'content'\n case 'Bash':\n return input.command\n ? String(input.command).slice(0, 50) +\n (String(input.command).length > 50 ? '...' : '')\n : 'command'\n case 'Task':\n return input.description ? String(input.description) : 'sub-task'\n case 'WebFetch':\n return input.url ? String(input.url) : 'URL'\n case 'WebSearch':\n return input.query ? String(input.query) : 'web'\n default:\n // Try to extract a meaningful description from common input fields\n // Note: Don't include tool name in description - it's displayed separately\n if (input.file_path) return getFileName(String(input.file_path))\n if (input.path) return getFileName(String(input.path))\n if (input.query) return String(input.query)\n if (input.pattern) return String(input.pattern)\n return ''\n }\n}\n\n/**\n * Get tool use history for display, respecting display config\n * @param transcript The agent transcript\n * @param config Display configuration\n * @returns Tool uses to display (recent first for normal mode, chronological for verbose)\n */\nexport function getToolUseHistoryForDisplay(\n transcript: AgentTranscript,\n config: DisplayConfig,\n): { tools: ToolUseHistoryItem[]; hiddenCount: number } {\n const allTools = getToolUseHistory(transcript)\n\n if (config.showAllChildren) {\n // Verbose mode: show all tools in chronological order\n return { tools: allTools, hiddenCount: 0 }\n }\n\n // Normal mode: show recent N tools (most recent last for display)\n const maxTools = config.maxRecentChildren\n if (allTools.length <= maxTools) {\n return { tools: allTools, hiddenCount: 0 }\n }\n\n // Take the most recent tools\n const recentTools = allTools.slice(-maxTools)\n return {\n tools: recentTools,\n hiddenCount: allTools.length - maxTools,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Task Display Utilities\n *\n * Utilities for computing task display content with truncation rules.\n * Implements V3's intermediate state display logic.\n */\n\nimport type { ToolUseBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport type { AgentTranscript } from './agentTranscripts'\nimport type {\n DisplayConfig,\n TaskDisplayContent,\n} from '@minto-types/messageGroup'\nimport { getAgentTranscript, getAgentIdByToolUseId } from './agentTranscripts'\nimport { SEMANTIC_COLORS, SYMBOL_COLORS } from '@constants/colors'\nimport { SYMBOLS } from '@constants/symbols'\n\n/**\n * Get the latest text output from a transcript\n */\nexport function getLatestOutput(transcript: AgentTranscript): string {\n const messages = transcript.messages\n\n // Search from the end for the most recent text output\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (msg.type === 'assistant') {\n const content = msg.message.content\n if (!content || !Array.isArray(content)) continue\n\n const textBlock = content.find(b => b.type === 'text')\n if (textBlock && 'text' in textBlock) {\n return textBlock.text\n }\n }\n }\n\n return ''\n}\n\n/**\n * Get all nested Task transcripts from a parent transcript\n */\nexport function getNestedTasks(transcript: AgentTranscript): AgentTranscript[] {\n const nested: AgentTranscript[] = []\n\n for (const msg of transcript.messages) {\n if (msg.type === 'assistant') {\n const content = msg.message.content\n if (!content || !Array.isArray(content)) continue\n\n for (const block of content) {\n if (block.type === 'tool_use' && block.name === 'Task') {\n const toolUse = block as ToolUseBlockParam\n const nestedAgentId = getAgentIdByToolUseId(toolUse.id)\n if (nestedAgentId) {\n const nestedTranscript = getAgentTranscript(nestedAgentId)\n if (nestedTranscript) {\n nested.push(nestedTranscript)\n }\n }\n }\n }\n }\n }\n\n return nested\n}\n\n/**\n * Truncate text to specified length with ellipsis\n */\nexport function truncate(text: string, maxLength: number): string {\n if (!text) return ''\n // Clean up whitespace\n const cleaned = text.trim().replace(/\\n+/g, ' ').replace(/\\s+/g, ' ')\n if (cleaned.length <= maxLength) return cleaned\n return cleaned.slice(0, maxLength - 3) + '...'\n}\n\n/**\n * Get task display content with truncation rules (from V3)\n */\nexport function getTaskDisplayContent(\n transcript: AgentTranscript,\n config: DisplayConfig,\n): TaskDisplayContent {\n const nestedTasks = getNestedTasks(transcript)\n\n if (nestedTasks.length === 0) {\n // No children: show latest output, truncated\n const latestOutput = getLatestOutput(transcript)\n return {\n type: 'simple',\n content: truncate(latestOutput, config.maxCharsWithoutChildren),\n }\n }\n\n // Has children: show recent children based on config\n const tasksToShow = config.showAllChildren\n ? nestedTasks\n : nestedTasks.slice(-config.maxRecentChildren)\n\n const children = tasksToShow.map(task => ({\n description: task.description,\n status: task.status,\n content: truncate(getLatestOutput(task), config.maxCharsPerChild),\n }))\n\n return {\n type: 'nested',\n children,\n hiddenCount: nestedTasks.length - tasksToShow.length,\n }\n}\n\n/**\n * Get status icon for a task status\n * \u9075\u5FAA REPL \u89C4\u8303\uFF1A\n * - pending: \u25CB (\u7A7A\u5FC3\u5706)\n * - running: \u25D0 (\u534A\u5706\uFF0CTOOL_RUNNING)\n * - completed: \u2713 (\u52FE\uFF0CTOOL_SUCCESS)\n * - failed: \u2717 (\u53C9\uFF0CTOOL_ERROR)\n */\nexport function getStatusIcon(status: string): string {\n switch (status) {\n case 'pending':\n return '\u25CB' // \u7A7A\u5FC3\u5706 - pending\n case 'running':\n return SYMBOLS.TOOL_RUNNING // \u25D0 \u534A\u5706 - in progress\n case 'completed':\n return SYMBOLS.TOOL_SUCCESS // \u2713 \u52FE - completed\n case 'failed':\n return SYMBOLS.TOOL_ERROR // \u2717 - failed\n case 'interrupted':\n return '\u2298'\n default:\n return '\u25CB'\n }\n}\n\n/**\n * Get status color for a task status\n * \u4F7F\u7528 SYMBOL_COLORS \u4E0E Logo \u54C1\u724C\u914D\u8272\u4FDD\u6301\u4E00\u81F4\n */\nexport function getStatusColor(status: string): string {\n switch (status) {\n case 'pending':\n return SYMBOL_COLORS.pending\n case 'running':\n return SYMBOL_COLORS.running\n case 'completed':\n return SYMBOL_COLORS.success\n case 'failed':\n return SYMBOL_COLORS.error\n case 'interrupted':\n return SYMBOL_COLORS.pending\n default:\n return SYMBOL_COLORS.pending\n }\n}\n\n// ============================================================================\n// \u5143\u4FE1\u606F\u683C\u5F0F\u5316 (Meta Info Formatting)\n// \u57FA\u4E8E REPL \u663E\u793A\u89C4\u8303\uFF1A\u8017\u65F6 \u00B7 \u6A21\u578B \u00B7 \u65F6\u95F4\n// ============================================================================\n\n/**\n * \u5143\u4FE1\u606F\u6570\u636E\u7ED3\u6784\n */\nexport interface MetaInfo {\n /** \u8017\u65F6\uFF08\u6BEB\u79D2\uFF09 */\n duration?: number\n /** \u6A21\u578B\u540D\u79F0 */\n model?: string\n /** \u65F6\u95F4\u6233 */\n timestamp?: Date | number\n}\n\n/**\n * \u683C\u5F0F\u5316\u8017\u65F6\n * - < 60s: X.Xs (\u5982 2.4s)\n * - >= 60s: Xm Xs (\u5982 1m 23s)\n * - \u672A\u77E5: --\n */\nexport function formatDuration(durationMs?: number): string {\n if (durationMs === undefined || durationMs === null) {\n return '--'\n }\n\n const seconds = durationMs / 1000\n\n if (seconds < 60) {\n // \u5C0F\u4E8E 60 \u79D2\uFF0C\u663E\u793A X.Xs\n return `${seconds.toFixed(1)}s`\n }\n\n // \u5927\u4E8E\u7B49\u4E8E 60 \u79D2\uFF0C\u663E\u793A Xm Xs\n const minutes = Math.floor(seconds / 60)\n const remainingSeconds = Math.floor(seconds % 60)\n return `${minutes}m ${remainingSeconds}s`\n}\n\n/**\n * \u683C\u5F0F\u5316\u6A21\u578B\u540D\u79F0\uFF08\u7B80\u5199\uFF09\n * - claude-3-opus-20240229 -> opus\n * - claude-3-sonnet-20240229 -> sonnet\n * - glm-4.7 -> glm-4.7\n */\nexport function formatModelName(model?: string): string {\n if (!model) return ''\n\n // Claude \u6A21\u578B\u7B80\u5199\n if (model.includes('opus')) return 'opus'\n if (model.includes('sonnet')) return 'sonnet'\n if (model.includes('haiku')) return 'haiku'\n\n // \u5176\u4ED6\u6A21\u578B\u4FDD\u6301\u539F\u6837\u6216\u622A\u65AD\n // \u5982\u679C\u540D\u79F0\u592A\u957F\uFF0C\u53D6\u6700\u540E\u4E00\u90E8\u5206\n const parts = model.split('-')\n if (parts.length > 2 && model.length > 15) {\n // \u5C1D\u8BD5\u63D0\u53D6\u6709\u610F\u4E49\u7684\u90E8\u5206\n return parts.slice(0, 2).join('-')\n }\n\n return model\n}\n\n/**\n * \u683C\u5F0F\u5316\u65F6\u95F4\u6233\n * \u683C\u5F0F: H:MM (24\u5C0F\u65F6\u5236)\n */\nexport function formatTimestamp(timestamp?: Date | number): string {\n if (!timestamp) return ''\n\n const date = timestamp instanceof Date ? timestamp : new Date(timestamp)\n const hours = date.getHours()\n const minutes = date.getMinutes().toString().padStart(2, '0')\n\n return `${hours}:${minutes}`\n}\n\n/**\n * \u683C\u5F0F\u5316\u5143\u4FE1\u606F\n *\n * \u89C4\u5219\uFF1A\n * - \u6709\u8017\u65F6\uFF1A\u8017\u65F6 \u00B7 \u6A21\u578B \u00B7 \u65F6\u95F4 (\u5982 \"2.4s \u00B7 glm-4.7 \u00B7 9:21\")\n * - \u65E0\u8017\u65F6\uFF1A\u6A21\u578B \u00B7 \u65F6\u95F4 (\u5982 \"glm-4.7 \u00B7 9:21\")\n *\n * @param meta - \u5143\u4FE1\u606F\u5BF9\u8C61\n * @returns \u683C\u5F0F\u5316\u540E\u7684\u5B57\u7B26\u4E32\uFF0C\u5982\u679C\u6CA1\u6709\u4EFB\u4F55\u4FE1\u606F\u5219\u8FD4\u56DE\u7A7A\u5B57\u7B26\u4E32\n */\nexport function formatMetaInfo(meta: MetaInfo): string {\n const parts: string[] = []\n\n // \u6709\u8017\u65F6\u65F6\uFF0C\u8017\u65F6\u653E\u5728\u6700\u524D\u9762\n if (meta.duration !== undefined && meta.duration !== null) {\n parts.push(formatDuration(meta.duration))\n }\n\n // \u6A21\u578B\u540D\u79F0\n const modelName = formatModelName(meta.model)\n if (modelName) {\n parts.push(modelName)\n }\n\n // \u65F6\u95F4\u6233\n const time = formatTimestamp(meta.timestamp)\n if (time) {\n parts.push(time)\n }\n\n // \u4F7F\u7528 \" \u00B7 \" \u5206\u9694\n return parts.join(' \u00B7 ')\n}\n\n/**\n * Tool use history item\n */\nexport interface ToolUseHistoryItem {\n /** Tool name */\n name: string\n /** Tool use ID */\n id: string\n /** Brief description of what the tool did */\n description: string\n /** Timestamp (message index for ordering) */\n messageIndex: number\n /** Whether this tool is currently executing (no result yet) */\n isExecuting?: boolean\n}\n\n/**\n * Get tool use history from a transcript\n * Returns tool uses in chronological order (oldest first)\n * Also marks tools that are currently executing (no result yet)\n */\nexport function getToolUseHistory(\n transcript: AgentTranscript,\n): ToolUseHistoryItem[] {\n const history: ToolUseHistoryItem[] = []\n\n // Collect all tool_result IDs to identify completed tools\n const completedToolIds = new Set<string>()\n for (const msg of transcript.messages) {\n if (msg.type === 'user') {\n const content = msg.message.content\n if (!Array.isArray(content)) continue\n\n for (const block of content) {\n if (block.type === 'tool_result' && 'tool_use_id' in block) {\n completedToolIds.add(block.tool_use_id)\n }\n }\n }\n }\n\n for (let i = 0; i < transcript.messages.length; i++) {\n const msg = transcript.messages[i]\n if (msg.type === 'assistant') {\n const content = msg.message.content\n if (!content || !Array.isArray(content)) continue\n\n for (const block of content) {\n if (block.type === 'tool_use') {\n const toolUse = block as ToolUseBlockParam\n const description = getToolUseDescription(toolUse)\n const isExecuting = !completedToolIds.has(toolUse.id)\n\n history.push({\n name: toolUse.name,\n id: toolUse.id,\n description,\n messageIndex: i,\n isExecuting,\n })\n }\n }\n }\n }\n\n return history\n}\n\n/**\n * Extract just the filename from a path for concise display\n */\nfunction getFileName(filePath: string): string {\n const parts = filePath.split('/')\n return parts[parts.length - 1] || filePath\n}\n\n/**\n * Map internal tool names to user-friendly display names\n * Note: Some tools have different internal names (e.g., 'View' \u2192 'Read')\n */\nconst TOOL_DISPLAY_NAMES: Record<string, string> = {\n View: 'Read',\n // Add more mappings as needed\n}\n\n/**\n * Get the user-friendly display name for a tool\n */\nexport function getToolDisplayName(internalName: string): string {\n return TOOL_DISPLAY_NAMES[internalName] || internalName\n}\n\n/**\n * Get a brief description of what a tool use did\n * Note: Description should NOT include the tool name - it's displayed separately\n */\nfunction getToolUseDescription(toolUse: ToolUseBlockParam): string {\n const input = toolUse.input as Record<string, unknown>\n\n switch (toolUse.name) {\n case 'Read':\n case 'View': // View is aliased to Read\n return input.file_path ? getFileName(String(input.file_path)) : 'file'\n case 'Write':\n return input.file_path ? getFileName(String(input.file_path)) : 'file'\n case 'Edit':\n return input.file_path ? getFileName(String(input.file_path)) : 'file'\n case 'Glob':\n return input.pattern ? String(input.pattern) : 'files'\n case 'Grep':\n return input.pattern ? `\"${input.pattern}\"` : 'content'\n case 'Bash':\n return input.command\n ? String(input.command).slice(0, 50) +\n (String(input.command).length > 50 ? '...' : '')\n : 'command'\n case 'Task':\n return input.description ? String(input.description) : 'sub-task'\n case 'WebFetch':\n return input.url ? String(input.url) : 'URL'\n case 'WebSearch':\n return input.query ? String(input.query) : 'web'\n default:\n // Try to extract a meaningful description from common input fields\n // Note: Don't include tool name in description - it's displayed separately\n if (input.file_path) return getFileName(String(input.file_path))\n if (input.path) return getFileName(String(input.path))\n if (input.query) return String(input.query)\n if (input.pattern) return String(input.pattern)\n return ''\n }\n}\n\n/**\n * Get tool use history for display, respecting display config\n * @param transcript The agent transcript\n * @param config Display configuration\n * @returns Tool uses to display (recent first for normal mode, chronological for verbose)\n */\nexport function getToolUseHistoryForDisplay(\n transcript: AgentTranscript,\n config: DisplayConfig,\n): { tools: ToolUseHistoryItem[]; hiddenCount: number } {\n const allTools = getToolUseHistory(transcript)\n\n if (config.showAllChildren) {\n // Verbose mode: show all tools in chronological order\n return { tools: allTools, hiddenCount: 0 }\n }\n\n // Normal mode: show recent N tools (most recent last for display)\n const maxTools = config.maxRecentChildren\n if (allTools.length <= maxTools) {\n return { tools: allTools, hiddenCount: 0 }\n }\n\n // Take the most recent tools\n const recentTools = allTools.slice(-maxTools)\n return {\n tools: recentTools,\n hiddenCount: allTools.length - maxTools,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -38,9 +38,21 @@ function clearTerminal() {
38
38
  function clearScreen() {
39
39
  process.stdout.write("\x1B[2J\x1B[H");
40
40
  }
41
+ function prepareTerminalForREPL() {
42
+ return new Promise((resolve) => {
43
+ if (!process.stdout.isTTY) {
44
+ resolve();
45
+ return;
46
+ }
47
+ process.stdout.write("\x1B[2J\x1B[3J\x1B[H\x1B[?25h", () => {
48
+ resolve();
49
+ });
50
+ });
51
+ }
41
52
  export {
42
53
  clearScreen,
43
54
  clearTerminal,
55
+ prepareTerminalForREPL,
44
56
  setTerminalTitle,
45
57
  updateTerminalTitle
46
58
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/terminal.ts"],
4
- "sourcesContent": ["import { safeParseJSON } from './json'\nimport { logError } from './log'\nimport { queryQuick } from '@services/claude'\n\nexport function setTerminalTitle(title: string): void {\n if (process.platform === 'win32') {\n process.title = title ? `\u2733 ${title}` : title\n } else {\n process.stdout.write(`\\x1b]0;${title ? `\u2733 ${title}` : ''}\\x07`)\n }\n}\n\nexport async function updateTerminalTitle(message: string): Promise<void> {\n try {\n const result = await queryQuick({\n systemPrompt: [\n \"Analyze if this message indicates a new conversation topic. If it does, extract a 2-3 word title that captures the new topic. Format your response as a JSON object with two fields: 'isNewTopic' (boolean) and 'title' (string, or null if isNewTopic is false). Only include these fields, no other text.\",\n ],\n userPrompt: message,\n enablePromptCaching: true,\n })\n\n const content = result.message.content\n .filter(_ => _.type === 'text')\n .map(_ => _.text)\n .join('')\n\n const response = safeParseJSON(content)\n if (\n response &&\n typeof response === 'object' &&\n 'isNewTopic' in response &&\n 'title' in response\n ) {\n if (response.isNewTopic && response.title) {\n setTerminalTitle(response.title as string)\n }\n }\n } catch (error) {\n logError(error)\n }\n}\n\nexport function clearTerminal(): Promise<void> {\n return new Promise(resolve => {\n process.stdout.write('\\x1b[2J\\x1b[3J\\x1b[H', () => {\n resolve()\n })\n })\n}\n\n/**\n * Synchronous clear screen (for immediate feedback with hotkeys like Ctrl-L)\n * Uses ANSI escape sequences: \\x1b[2J clears screen, \\x1b[H moves cursor to home\n */\nexport function clearScreen(): void {\n process.stdout.write('\\x1b[2J\\x1b[H')\n}\n"],
5
- "mappings": "AAAA,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAEpB,SAAS,iBAAiB,OAAqB;AACpD,MAAI,QAAQ,aAAa,SAAS;AAChC,YAAQ,QAAQ,QAAQ,UAAK,KAAK,KAAK;AAAA,EACzC,OAAO;AACL,YAAQ,OAAO,MAAM,UAAU,QAAQ,UAAK,KAAK,KAAK,EAAE,MAAM;AAAA,EAChE;AACF;AAEA,eAAsB,oBAAoB,SAAgC;AACxE,MAAI;AACF,UAAM,SAAS,MAAM,WAAW;AAAA,MAC9B,cAAc;AAAA,QACZ;AAAA,MACF;AAAA,MACA,YAAY;AAAA,MACZ,qBAAqB;AAAA,IACvB,CAAC;AAED,UAAM,UAAU,OAAO,QAAQ,QAC5B,OAAO,OAAK,EAAE,SAAS,MAAM,EAC7B,IAAI,OAAK,EAAE,IAAI,EACf,KAAK,EAAE;AAEV,UAAM,WAAW,cAAc,OAAO;AACtC,QACE,YACA,OAAO,aAAa,YACpB,gBAAgB,YAChB,WAAW,UACX;AACA,UAAI,SAAS,cAAc,SAAS,OAAO;AACzC,yBAAiB,SAAS,KAAe;AAAA,MAC3C;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,aAAS,KAAK;AAAA,EAChB;AACF;AAEO,SAAS,gBAA+B;AAC7C,SAAO,IAAI,QAAQ,aAAW;AAC5B,YAAQ,OAAO,MAAM,wBAAwB,MAAM;AACjD,cAAQ;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AACH;AAMO,SAAS,cAAoB;AAClC,UAAQ,OAAO,MAAM,eAAe;AACtC;",
4
+ "sourcesContent": ["import { safeParseJSON } from './json'\nimport { logError } from './log'\nimport { queryQuick } from '@services/claude'\n\nexport function setTerminalTitle(title: string): void {\n if (process.platform === 'win32') {\n process.title = title ? `\u2733 ${title}` : title\n } else {\n process.stdout.write(`\\x1b]0;${title ? `\u2733 ${title}` : ''}\\x07`)\n }\n}\n\nexport async function updateTerminalTitle(message: string): Promise<void> {\n try {\n const result = await queryQuick({\n systemPrompt: [\n \"Analyze if this message indicates a new conversation topic. If it does, extract a 2-3 word title that captures the new topic. Format your response as a JSON object with two fields: 'isNewTopic' (boolean) and 'title' (string, or null if isNewTopic is false). Only include these fields, no other text.\",\n ],\n userPrompt: message,\n enablePromptCaching: true,\n })\n\n const content = result.message.content\n .filter(_ => _.type === 'text')\n .map(_ => _.text)\n .join('')\n\n const response = safeParseJSON(content)\n if (\n response &&\n typeof response === 'object' &&\n 'isNewTopic' in response &&\n 'title' in response\n ) {\n if (response.isNewTopic && response.title) {\n setTerminalTitle(response.title as string)\n }\n }\n } catch (error) {\n logError(error)\n }\n}\n\nexport function clearTerminal(): Promise<void> {\n return new Promise(resolve => {\n process.stdout.write('\\x1b[2J\\x1b[3J\\x1b[H', () => {\n resolve()\n })\n })\n}\n\n/**\n * Synchronous clear screen (for immediate feedback with hotkeys like Ctrl-L)\n * Uses ANSI escape sequences: \\x1b[2J clears screen, \\x1b[H moves cursor to home\n */\nexport function clearScreen(): void {\n process.stdout.write('\\x1b[2J\\x1b[H')\n}\n\n/**\n * Prepare terminal for REPL rendering\n * Clears screen and normalizes terminal state before Ink starts rendering\n *\n * This ensures a clean slate for the REPL interface:\n * - \\x1b[2J - Clear entire screen\n * - \\x1b[3J - Clear scrollback buffer (provides fresh start)\n * - \\x1b[H - Move cursor to home position (0,0)\n * - \\x1b[?25h - Show cursor (in case it was hidden)\n *\n * @returns Promise that resolves when terminal is ready\n */\nexport function prepareTerminalForREPL(): Promise<void> {\n return new Promise(resolve => {\n if (!process.stdout.isTTY) {\n resolve()\n return\n }\n\n // Clear screen, scrollback, move cursor home, show cursor\n process.stdout.write('\\x1b[2J\\x1b[3J\\x1b[H\\x1b[?25h', () => {\n resolve()\n })\n })\n}\n"],
5
+ "mappings": "AAAA,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAEpB,SAAS,iBAAiB,OAAqB;AACpD,MAAI,QAAQ,aAAa,SAAS;AAChC,YAAQ,QAAQ,QAAQ,UAAK,KAAK,KAAK;AAAA,EACzC,OAAO;AACL,YAAQ,OAAO,MAAM,UAAU,QAAQ,UAAK,KAAK,KAAK,EAAE,MAAM;AAAA,EAChE;AACF;AAEA,eAAsB,oBAAoB,SAAgC;AACxE,MAAI;AACF,UAAM,SAAS,MAAM,WAAW;AAAA,MAC9B,cAAc;AAAA,QACZ;AAAA,MACF;AAAA,MACA,YAAY;AAAA,MACZ,qBAAqB;AAAA,IACvB,CAAC;AAED,UAAM,UAAU,OAAO,QAAQ,QAC5B,OAAO,OAAK,EAAE,SAAS,MAAM,EAC7B,IAAI,OAAK,EAAE,IAAI,EACf,KAAK,EAAE;AAEV,UAAM,WAAW,cAAc,OAAO;AACtC,QACE,YACA,OAAO,aAAa,YACpB,gBAAgB,YAChB,WAAW,UACX;AACA,UAAI,SAAS,cAAc,SAAS,OAAO;AACzC,yBAAiB,SAAS,KAAe;AAAA,MAC3C;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,aAAS,KAAK;AAAA,EAChB;AACF;AAEO,SAAS,gBAA+B;AAC7C,SAAO,IAAI,QAAQ,aAAW;AAC5B,YAAQ,OAAO,MAAM,wBAAwB,MAAM;AACjD,cAAQ;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AACH;AAMO,SAAS,cAAoB;AAClC,UAAQ,OAAO,MAAM,eAAe;AACtC;AAcO,SAAS,yBAAwC;AACtD,SAAO,IAAI,QAAQ,aAAW;AAC5B,QAAI,CAAC,QAAQ,OAAO,OAAO;AACzB,cAAQ;AACR;AAAA,IACF;AAGA,YAAQ,OAAO,MAAM,iCAAiC,MAAM;AAC1D,cAAQ;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AACH;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import { getGlobalConfig } from "./config.js";
2
2
  const lightTheme = {
3
3
  bashBorder: "#FF6E57",
4
- minto: "#FFC233",
4
+ minto: "#B668C8",
5
5
  noting: "#222222",
6
6
  permission: "#e9c61aff",
7
7
  secondaryBorder: "#999",
@@ -19,7 +19,7 @@ const lightTheme = {
19
19
  addedDimmed: "#c7e1cb",
20
20
  removedDimmed: "#fdd2d8"
21
21
  },
22
- brand: "#D4BBFF",
22
+ brand: "#667EEA",
23
23
  dimmedText: "#888",
24
24
  mutedText: "#999",
25
25
  secondaryTextNew: "#666",
@@ -30,7 +30,7 @@ const lightTheme = {
30
30
  };
31
31
  const lightDaltonizedTheme = {
32
32
  bashBorder: "#FF6E57",
33
- minto: "#FFC233",
33
+ minto: "#B668C8",
34
34
  noting: "#222222",
35
35
  permission: "#3366ff",
36
36
  secondaryBorder: "#999",
@@ -59,7 +59,7 @@ const lightDaltonizedTheme = {
59
59
  };
60
60
  const darkTheme = {
61
61
  bashBorder: "#FF6E57",
62
- minto: "#FFC233",
62
+ minto: "#B668C8",
63
63
  noting: "#222222",
64
64
  permission: "#b1b9f9",
65
65
  secondaryBorder: "#888",
@@ -77,7 +77,7 @@ const darkTheme = {
77
77
  addedDimmed: "#47584a",
78
78
  removedDimmed: "#69484d"
79
79
  },
80
- brand: "#D4BBFF",
80
+ brand: "#667EEA",
81
81
  dimmedText: "#666",
82
82
  mutedText: "#555",
83
83
  secondaryTextNew: "#999",
@@ -88,7 +88,7 @@ const darkTheme = {
88
88
  };
89
89
  const darkDaltonizedTheme = {
90
90
  bashBorder: "#FF6E57",
91
- minto: "#FFC233",
91
+ minto: "#B668C8",
92
92
  noting: "#222222",
93
93
  permission: "#99ccff",
94
94
  secondaryBorder: "#888",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/theme.ts"],
4
- "sourcesContent": ["import { getGlobalConfig } from './config'\n\nexport interface Theme {\n bashBorder: string\n minto: string\n noting: string\n permission: string\n secondaryBorder: string\n text: string\n secondaryText: string\n suggestion: string\n success: string\n error: string\n warning: string\n primary: string\n secondary: string\n diff: {\n added: string\n removed: string\n addedDimmed: string\n removedDimmed: string\n }\n // Additional color properties used in components\n brand: string\n dimmedText: string\n mutedText: string\n secondaryTextNew: string\n selectionBg: string\n info: string\n dim: string\n primaryText: string\n}\n\nconst lightTheme: Theme = {\n bashBorder: '#FF6E57',\n minto: '#FFC233',\n noting: '#222222',\n permission: '#e9c61aff',\n secondaryBorder: '#999',\n text: '#000',\n secondaryText: '#666',\n suggestion: '#32e98aff',\n success: '#2c7a39',\n error: '#ab2b3f',\n warning: '#966c1e',\n primary: '#000',\n secondary: '#666',\n diff: {\n added: '#69db7c',\n removed: '#ffa8b4',\n addedDimmed: '#c7e1cb',\n removedDimmed: '#fdd2d8',\n },\n brand: '#D4BBFF',\n dimmedText: '#888',\n mutedText: '#999',\n secondaryTextNew: '#666',\n selectionBg: '#3C3C3C',\n info: '#3399ff',\n dim: '#aaa',\n primaryText: '#000',\n}\n\nconst lightDaltonizedTheme: Theme = {\n bashBorder: '#FF6E57',\n minto: '#FFC233',\n noting: '#222222',\n permission: '#3366ff',\n secondaryBorder: '#999',\n text: '#000',\n secondaryText: '#666',\n suggestion: '#3366ff',\n success: '#006699',\n error: '#cc0000',\n warning: '#ff9900',\n primary: '#000',\n secondary: '#666',\n diff: {\n added: '#99ccff',\n removed: '#ffcccc',\n addedDimmed: '#d1e7fd',\n removedDimmed: '#ffe9e9',\n },\n brand: '#3366ff',\n dimmedText: '#888',\n mutedText: '#999',\n secondaryTextNew: '#666',\n selectionBg: '#3C3C3C',\n info: '#3366ff',\n dim: '#aaa',\n primaryText: '#000',\n}\n\nconst darkTheme: Theme = {\n bashBorder: '#FF6E57',\n minto: '#FFC233',\n noting: '#222222',\n permission: '#b1b9f9',\n secondaryBorder: '#888',\n text: '#fff',\n secondaryText: '#999',\n suggestion: '#b1b9f9',\n success: '#4eba65',\n error: '#ff6b80',\n warning: '#ffc107',\n primary: '#fff',\n secondary: '#999',\n diff: {\n added: '#225c2b',\n removed: '#7a2936',\n addedDimmed: '#47584a',\n removedDimmed: '#69484d',\n },\n brand: '#D4BBFF',\n dimmedText: '#666',\n mutedText: '#555',\n secondaryTextNew: '#999',\n selectionBg: '#3C3C3C',\n info: '#5DADE2',\n dim: '#444',\n primaryText: '#fff',\n}\n\nconst darkDaltonizedTheme: Theme = {\n bashBorder: '#FF6E57',\n minto: '#FFC233',\n noting: '#222222',\n permission: '#99ccff',\n secondaryBorder: '#888',\n text: '#fff',\n secondaryText: '#999',\n suggestion: '#99ccff',\n success: '#3399ff',\n error: '#ff6666',\n warning: '#ffcc00',\n primary: '#fff',\n secondary: '#999',\n diff: {\n added: '#004466',\n removed: '#660000',\n addedDimmed: '#3e515b',\n removedDimmed: '#3e2c2c',\n },\n brand: '#99ccff',\n dimmedText: '#666',\n mutedText: '#555',\n secondaryTextNew: '#999',\n selectionBg: '#3C3C3C',\n info: '#99ccff',\n dim: '#444',\n primaryText: '#fff',\n}\n\nexport type ThemeNames =\n | 'dark'\n | 'light'\n | 'light-daltonized'\n | 'dark-daltonized'\n\nexport function getTheme(overrideTheme?: ThemeNames): Theme {\n const config = getGlobalConfig()\n switch (overrideTheme ?? config.theme) {\n case 'light':\n return lightTheme\n case 'light-daltonized':\n return lightDaltonizedTheme\n case 'dark-daltonized':\n return darkDaltonizedTheme\n default:\n return darkTheme\n }\n}\n"],
4
+ "sourcesContent": ["import { getGlobalConfig } from './config'\n\nexport interface Theme {\n bashBorder: string\n minto: string\n noting: string\n permission: string\n secondaryBorder: string\n text: string\n secondaryText: string\n suggestion: string\n success: string\n error: string\n warning: string\n primary: string\n secondary: string\n diff: {\n added: string\n removed: string\n addedDimmed: string\n removedDimmed: string\n }\n // Additional color properties used in components\n brand: string\n dimmedText: string\n mutedText: string\n secondaryTextNew: string\n selectionBg: string\n info: string\n dim: string\n primaryText: string\n}\n\nconst lightTheme: Theme = {\n bashBorder: '#FF6E57',\n minto: '#B668C8',\n noting: '#222222',\n permission: '#e9c61aff',\n secondaryBorder: '#999',\n text: '#000',\n secondaryText: '#666',\n suggestion: '#32e98aff',\n success: '#2c7a39',\n error: '#ab2b3f',\n warning: '#966c1e',\n primary: '#000',\n secondary: '#666',\n diff: {\n added: '#69db7c',\n removed: '#ffa8b4',\n addedDimmed: '#c7e1cb',\n removedDimmed: '#fdd2d8',\n },\n brand: '#667EEA',\n dimmedText: '#888',\n mutedText: '#999',\n secondaryTextNew: '#666',\n selectionBg: '#3C3C3C',\n info: '#3399ff',\n dim: '#aaa',\n primaryText: '#000',\n}\n\nconst lightDaltonizedTheme: Theme = {\n bashBorder: '#FF6E57',\n minto: '#B668C8',\n noting: '#222222',\n permission: '#3366ff',\n secondaryBorder: '#999',\n text: '#000',\n secondaryText: '#666',\n suggestion: '#3366ff',\n success: '#006699',\n error: '#cc0000',\n warning: '#ff9900',\n primary: '#000',\n secondary: '#666',\n diff: {\n added: '#99ccff',\n removed: '#ffcccc',\n addedDimmed: '#d1e7fd',\n removedDimmed: '#ffe9e9',\n },\n brand: '#3366ff',\n dimmedText: '#888',\n mutedText: '#999',\n secondaryTextNew: '#666',\n selectionBg: '#3C3C3C',\n info: '#3366ff',\n dim: '#aaa',\n primaryText: '#000',\n}\n\nconst darkTheme: Theme = {\n bashBorder: '#FF6E57',\n minto: '#B668C8',\n noting: '#222222',\n permission: '#b1b9f9',\n secondaryBorder: '#888',\n text: '#fff',\n secondaryText: '#999',\n suggestion: '#b1b9f9',\n success: '#4eba65',\n error: '#ff6b80',\n warning: '#ffc107',\n primary: '#fff',\n secondary: '#999',\n diff: {\n added: '#225c2b',\n removed: '#7a2936',\n addedDimmed: '#47584a',\n removedDimmed: '#69484d',\n },\n brand: '#667EEA',\n dimmedText: '#666',\n mutedText: '#555',\n secondaryTextNew: '#999',\n selectionBg: '#3C3C3C',\n info: '#5DADE2',\n dim: '#444',\n primaryText: '#fff',\n}\n\nconst darkDaltonizedTheme: Theme = {\n bashBorder: '#FF6E57',\n minto: '#B668C8',\n noting: '#222222',\n permission: '#99ccff',\n secondaryBorder: '#888',\n text: '#fff',\n secondaryText: '#999',\n suggestion: '#99ccff',\n success: '#3399ff',\n error: '#ff6666',\n warning: '#ffcc00',\n primary: '#fff',\n secondary: '#999',\n diff: {\n added: '#004466',\n removed: '#660000',\n addedDimmed: '#3e515b',\n removedDimmed: '#3e2c2c',\n },\n brand: '#99ccff',\n dimmedText: '#666',\n mutedText: '#555',\n secondaryTextNew: '#999',\n selectionBg: '#3C3C3C',\n info: '#99ccff',\n dim: '#444',\n primaryText: '#fff',\n}\n\nexport type ThemeNames =\n | 'dark'\n | 'light'\n | 'light-daltonized'\n | 'dark-daltonized'\n\nexport function getTheme(overrideTheme?: ThemeNames): Theme {\n const config = getGlobalConfig()\n switch (overrideTheme ?? config.theme) {\n case 'light':\n return lightTheme\n case 'light-daltonized':\n return lightDaltonizedTheme\n case 'dark-daltonized':\n return darkDaltonizedTheme\n default:\n return darkTheme\n }\n}\n"],
5
5
  "mappings": "AAAA,SAAS,uBAAuB;AAiChC,MAAM,aAAoB;AAAA,EACxB,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,MAAM;AAAA,EACN,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AAAA,EACX,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,SAAS;AAAA,IACT,aAAa;AAAA,IACb,eAAe;AAAA,EACjB;AAAA,EACA,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,MAAM;AAAA,EACN,KAAK;AAAA,EACL,aAAa;AACf;AAEA,MAAM,uBAA8B;AAAA,EAClC,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,MAAM;AAAA,EACN,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AAAA,EACX,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,SAAS;AAAA,IACT,aAAa;AAAA,IACb,eAAe;AAAA,EACjB;AAAA,EACA,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,MAAM;AAAA,EACN,KAAK;AAAA,EACL,aAAa;AACf;AAEA,MAAM,YAAmB;AAAA,EACvB,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,MAAM;AAAA,EACN,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AAAA,EACX,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,SAAS;AAAA,IACT,aAAa;AAAA,IACb,eAAe;AAAA,EACjB;AAAA,EACA,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,MAAM;AAAA,EACN,KAAK;AAAA,EACL,aAAa;AACf;AAEA,MAAM,sBAA6B;AAAA,EACjC,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,MAAM;AAAA,EACN,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AAAA,EACX,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,SAAS;AAAA,IACT,aAAa;AAAA,IACb,eAAe;AAAA,EACjB;AAAA,EACA,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,MAAM;AAAA,EACN,KAAK;AAAA,EACL,aAAa;AACf;AAQO,SAAS,SAAS,eAAmC;AAC1D,QAAM,SAAS,gBAAgB;AAC/B,UAAQ,iBAAiB,OAAO,OAAO;AAAA,IACrC,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;",
6
6
  "names": []
7
7
  }