@slopus/rig 0.0.3 → 0.0.6

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 (382) hide show
  1. package/README.md +144 -1
  2. package/dist/agent/Agent.d.ts +16 -0
  3. package/dist/agent/Agent.d.ts.map +1 -1
  4. package/dist/agent/Agent.js +106 -0
  5. package/dist/agent/compaction/compactConversation.d.ts +22 -0
  6. package/dist/agent/compaction/compactConversation.d.ts.map +1 -0
  7. package/dist/agent/compaction/compactConversation.js +77 -0
  8. package/dist/agent/compaction/estimateMessagesTokens.d.ts +3 -0
  9. package/dist/agent/compaction/estimateMessagesTokens.d.ts.map +1 -0
  10. package/dist/agent/compaction/estimateMessagesTokens.js +39 -0
  11. package/dist/agent/compaction/formatMessagesForCompaction.d.ts +3 -0
  12. package/dist/agent/compaction/formatMessagesForCompaction.d.ts.map +1 -0
  13. package/dist/agent/compaction/formatMessagesForCompaction.js +30 -0
  14. package/dist/agent/compaction/requestCompactionSummary.d.ts +11 -0
  15. package/dist/agent/compaction/requestCompactionSummary.d.ts.map +1 -0
  16. package/dist/agent/compaction/requestCompactionSummary.js +43 -0
  17. package/dist/agent/context/AgentContext.d.ts +8 -0
  18. package/dist/agent/context/AgentContext.d.ts.map +1 -1
  19. package/dist/agent/context/BashContext.d.ts +23 -0
  20. package/dist/agent/context/BashContext.d.ts.map +1 -1
  21. package/dist/agent/context/GoalContext.d.ts +7 -0
  22. package/dist/agent/context/GoalContext.d.ts.map +1 -0
  23. package/dist/agent/context/GoalContext.js +1 -0
  24. package/dist/agent/context/SubagentContext.d.ts +20 -1
  25. package/dist/agent/context/SubagentContext.d.ts.map +1 -1
  26. package/dist/agent/context/TaskContext.d.ts +8 -0
  27. package/dist/agent/context/TaskContext.d.ts.map +1 -0
  28. package/dist/agent/context/TaskContext.js +1 -0
  29. package/dist/agent/context/UserInputContext.d.ts +7 -0
  30. package/dist/agent/context/UserInputContext.d.ts.map +1 -0
  31. package/dist/agent/context/UserInputContext.js +1 -0
  32. package/dist/agent/context/assertCanWritePath.d.ts +3 -0
  33. package/dist/agent/context/assertCanWritePath.d.ts.map +1 -0
  34. package/dist/agent/context/assertCanWritePath.js +11 -0
  35. package/dist/agent/context/createJustBashBashContext.d.ts.map +1 -1
  36. package/dist/agent/context/createJustBashBashContext.js +127 -0
  37. package/dist/agent/context/createNodeAgentContext.d.ts +8 -0
  38. package/dist/agent/context/createNodeAgentContext.d.ts.map +1 -1
  39. package/dist/agent/context/createNodeAgentContext.js +15 -2
  40. package/dist/agent/context/createNodeBashContext.d.ts +2 -0
  41. package/dist/agent/context/createNodeBashContext.d.ts.map +1 -1
  42. package/dist/agent/context/createNodeBashContext.js +123 -4
  43. package/dist/agent/context/createNodeFileSystemContext.d.ts +2 -0
  44. package/dist/agent/context/createNodeFileSystemContext.d.ts.map +1 -1
  45. package/dist/agent/context/createNodeFileSystemContext.js +18 -8
  46. package/dist/agent/context/createSandboxedCommand.d.ts +7 -0
  47. package/dist/agent/context/createSandboxedCommand.d.ts.map +1 -0
  48. package/dist/agent/context/createSandboxedCommand.js +47 -0
  49. package/dist/agent/context/findGitWritablePaths.d.ts +2 -0
  50. package/dist/agent/context/findGitWritablePaths.d.ts.map +1 -0
  51. package/dist/agent/context/findGitWritablePaths.js +54 -0
  52. package/dist/agent/context/isPathInsideWorkspace.d.ts +2 -0
  53. package/dist/agent/context/isPathInsideWorkspace.d.ts.map +1 -0
  54. package/dist/agent/context/isPathInsideWorkspace.js +14 -0
  55. package/dist/agent/context/resolvePotentialPath.d.ts +2 -0
  56. package/dist/agent/context/resolvePotentialPath.d.ts.map +1 -0
  57. package/dist/agent/context/resolvePotentialPath.js +32 -0
  58. package/dist/agent/createPermissionInstructions.d.ts +3 -0
  59. package/dist/agent/createPermissionInstructions.d.ts.map +1 -0
  60. package/dist/agent/createPermissionInstructions.js +12 -0
  61. package/dist/agent/createSubagentInstructions.js +1 -1
  62. package/dist/agent/createSystemPrompt.d.ts.map +1 -1
  63. package/dist/agent/createSystemPrompt.js +4 -0
  64. package/dist/agent/index.d.ts +7 -3
  65. package/dist/agent/index.d.ts.map +1 -1
  66. package/dist/agent/loop.d.ts +16 -2
  67. package/dist/agent/loop.d.ts.map +1 -1
  68. package/dist/agent/loop.js +115 -15
  69. package/dist/agent/printAgentMessageToConsole.d.ts +1 -0
  70. package/dist/agent/printAgentMessageToConsole.d.ts.map +1 -1
  71. package/dist/agent/selectSystemPromptForModel.js +1 -1
  72. package/dist/agent/types.d.ts +1 -0
  73. package/dist/agent/types.d.ts.map +1 -1
  74. package/dist/app/CodingAssistantAgentBackend.d.ts +11 -1
  75. package/dist/app/CodingAssistantAgentBackend.d.ts.map +1 -1
  76. package/dist/app/CodingAssistantApp.d.ts +7 -1
  77. package/dist/app/CodingAssistantApp.d.ts.map +1 -1
  78. package/dist/app/CodingAssistantApp.js +678 -45
  79. package/dist/app/ScrollbackPreservingTerminal.d.ts +5 -0
  80. package/dist/app/ScrollbackPreservingTerminal.d.ts.map +1 -0
  81. package/dist/app/ScrollbackPreservingTerminal.js +7 -0
  82. package/dist/app/StartupStatusApp.js +1 -1
  83. package/dist/app/createCodingAssistantAgent.d.ts +6 -1
  84. package/dist/app/createCodingAssistantAgent.d.ts.map +1 -1
  85. package/dist/app/createCodingAssistantAgent.js +24 -7
  86. package/dist/app/createSlashCommands.d.ts.map +1 -1
  87. package/dist/app/createSlashCommands.js +42 -0
  88. package/dist/app/describeReasoningLevel.d.ts.map +1 -1
  89. package/dist/app/describeReasoningLevel.js +1 -0
  90. package/dist/app/highlightAgentCode.d.ts.map +1 -1
  91. package/dist/app/highlightAgentCode.js +10 -7
  92. package/dist/app/humanizeGoalStatus.d.ts +3 -0
  93. package/dist/app/humanizeGoalStatus.d.ts.map +1 -0
  94. package/dist/app/humanizeGoalStatus.js +9 -0
  95. package/dist/app/humanizePermissionMode.d.ts +3 -0
  96. package/dist/app/humanizePermissionMode.d.ts.map +1 -0
  97. package/dist/app/humanizePermissionMode.js +9 -0
  98. package/dist/app/humanizeReasoningLevel.d.ts.map +1 -1
  99. package/dist/app/humanizeReasoningLevel.js +3 -0
  100. package/dist/app/humanizeToolName.d.ts +2 -0
  101. package/dist/app/humanizeToolName.d.ts.map +1 -0
  102. package/dist/app/humanizeToolName.js +40 -0
  103. package/dist/app/main.d.ts.map +1 -1
  104. package/dist/app/main.js +4 -0
  105. package/dist/app/renderAgentMarkdown.d.ts.map +1 -1
  106. package/dist/app/renderAgentMarkdown.js +2 -1
  107. package/dist/app/runApp.d.ts +2 -0
  108. package/dist/app/runApp.d.ts.map +1 -1
  109. package/dist/app/runApp.js +48 -3
  110. package/dist/app/sanitizeTerminalText.d.ts +2 -0
  111. package/dist/app/sanitizeTerminalText.d.ts.map +1 -0
  112. package/dist/app/sanitizeTerminalText.js +55 -0
  113. package/dist/app/upsertSubagentSummary.d.ts +3 -0
  114. package/dist/app/upsertSubagentSummary.d.ts.map +1 -0
  115. package/dist/app/upsertSubagentSummary.js +6 -0
  116. package/dist/client/ProtocolHttpClient.d.ts +12 -1
  117. package/dist/client/ProtocolHttpClient.d.ts.map +1 -1
  118. package/dist/client/ProtocolHttpClient.js +21 -0
  119. package/dist/client/RemoteAgent.d.ts +11 -1
  120. package/dist/client/RemoteAgent.d.ts.map +1 -1
  121. package/dist/client/RemoteAgent.js +91 -1
  122. package/dist/config/createConfigFile.d.ts.map +1 -1
  123. package/dist/config/createConfigFile.js +5 -0
  124. package/dist/config/defaultConfig.d.ts.map +1 -1
  125. package/dist/config/defaultConfig.js +2 -0
  126. package/dist/config/mergeConfigValues.d.ts.map +1 -1
  127. package/dist/config/mergeConfigValues.js +8 -1
  128. package/dist/config/parseConfigToml.d.ts.map +1 -1
  129. package/dist/config/parseConfigToml.js +88 -0
  130. package/dist/config/serializeMcpServers.d.ts +3 -0
  131. package/dist/config/serializeMcpServers.d.ts.map +1 -0
  132. package/dist/config/serializeMcpServers.js +34 -0
  133. package/dist/config/types.d.ts +6 -0
  134. package/dist/config/types.d.ts.map +1 -1
  135. package/dist/config/writeRuntimeConfig.d.ts.map +1 -1
  136. package/dist/config/writeRuntimeConfig.js +3 -0
  137. package/dist/goals/SessionGoal.d.ts +14 -0
  138. package/dist/goals/SessionGoal.d.ts.map +1 -0
  139. package/dist/goals/SessionGoal.js +1 -0
  140. package/dist/goals/createGoalContinuationPrompt.d.ts +3 -0
  141. package/dist/goals/createGoalContinuationPrompt.d.ts.map +1 -0
  142. package/dist/goals/createGoalContinuationPrompt.js +17 -0
  143. package/dist/goals/index.d.ts +5 -0
  144. package/dist/goals/index.d.ts.map +1 -0
  145. package/dist/goals/index.js +3 -0
  146. package/dist/goals/isGoalStatus.d.ts +3 -0
  147. package/dist/goals/isGoalStatus.d.ts.map +1 -0
  148. package/dist/goals/isGoalStatus.js +3 -0
  149. package/dist/goals/normalizeGoalObjective.d.ts +3 -0
  150. package/dist/goals/normalizeGoalObjective.d.ts.map +1 -0
  151. package/dist/goals/normalizeGoalObjective.js +11 -0
  152. package/dist/mcp/McpClientManager.d.ts +12 -0
  153. package/dist/mcp/McpClientManager.d.ts.map +1 -0
  154. package/dist/mcp/McpClientManager.js +123 -0
  155. package/dist/mcp/connectMcpServer.d.ts +8 -0
  156. package/dist/mcp/connectMcpServer.d.ts.map +1 -0
  157. package/dist/mcp/connectMcpServer.js +47 -0
  158. package/dist/mcp/createMcpTool.d.ts +11 -0
  159. package/dist/mcp/createMcpTool.d.ts.map +1 -0
  160. package/dist/mcp/createMcpTool.js +43 -0
  161. package/dist/mcp/index.d.ts +5 -0
  162. package/dist/mcp/index.d.ts.map +1 -0
  163. package/dist/mcp/index.js +3 -0
  164. package/dist/mcp/loadMcpServerConfigs.d.ts +4 -0
  165. package/dist/mcp/loadMcpServerConfigs.d.ts.map +1 -0
  166. package/dist/mcp/loadMcpServerConfigs.js +122 -0
  167. package/dist/mcp/mcpResultToContentBlocks.d.ts +3 -0
  168. package/dist/mcp/mcpResultToContentBlocks.d.ts.map +1 -0
  169. package/dist/mcp/mcpResultToContentBlocks.js +51 -0
  170. package/dist/mcp/mergeMcpTools.d.ts +7 -0
  171. package/dist/mcp/mergeMcpTools.d.ts.map +1 -0
  172. package/dist/mcp/mergeMcpTools.js +34 -0
  173. package/dist/mcp/normalizeMcpName.d.ts +2 -0
  174. package/dist/mcp/normalizeMcpName.d.ts.map +1 -0
  175. package/dist/mcp/normalizeMcpName.js +3 -0
  176. package/dist/mcp/types.d.ts +37 -0
  177. package/dist/mcp/types.d.ts.map +1 -0
  178. package/dist/mcp/types.js +1 -0
  179. package/dist/permissions/PermissionContext.d.ts +7 -0
  180. package/dist/permissions/PermissionContext.d.ts.map +1 -0
  181. package/dist/permissions/PermissionContext.js +1 -0
  182. package/dist/permissions/PermissionMode.d.ts +3 -0
  183. package/dist/permissions/PermissionMode.d.ts.map +1 -0
  184. package/dist/permissions/PermissionMode.js +1 -0
  185. package/dist/permissions/createPermissionContext.d.ts +4 -0
  186. package/dist/permissions/createPermissionContext.d.ts.map +1 -0
  187. package/dist/permissions/createPermissionContext.js +16 -0
  188. package/dist/permissions/index.d.ts +13 -0
  189. package/dist/permissions/index.d.ts.map +1 -0
  190. package/dist/permissions/index.js +9 -0
  191. package/dist/permissions/isPermissionMode.d.ts +3 -0
  192. package/dist/permissions/isPermissionMode.d.ts.map +1 -0
  193. package/dist/permissions/isPermissionMode.js +6 -0
  194. package/dist/permissions/parseAutoPermissionReview.d.ts +7 -0
  195. package/dist/permissions/parseAutoPermissionReview.d.ts.map +1 -0
  196. package/dist/permissions/parseAutoPermissionReview.js +33 -0
  197. package/dist/permissions/parsePermissionMode.d.ts +3 -0
  198. package/dist/permissions/parsePermissionMode.d.ts.map +1 -0
  199. package/dist/permissions/parsePermissionMode.js +6 -0
  200. package/dist/permissions/requestAutoPermissionApproval.d.ts +9 -0
  201. package/dist/permissions/requestAutoPermissionApproval.d.ts.map +1 -0
  202. package/dist/permissions/requestAutoPermissionApproval.js +26 -0
  203. package/dist/permissions/reviewAutoPermission.d.ts +13 -0
  204. package/dist/permissions/reviewAutoPermission.d.ts.map +1 -0
  205. package/dist/permissions/reviewAutoPermission.js +70 -0
  206. package/dist/permissions/shouldElevateToolInAutoMode.d.ts +2 -0
  207. package/dist/permissions/shouldElevateToolInAutoMode.d.ts.map +1 -0
  208. package/dist/permissions/shouldElevateToolInAutoMode.js +37 -0
  209. package/dist/permissions/shouldReviewToolInAutoMode.d.ts +2 -0
  210. package/dist/permissions/shouldReviewToolInAutoMode.d.ts.map +1 -0
  211. package/dist/permissions/shouldReviewToolInAutoMode.js +85 -0
  212. package/dist/permissions/summarizePermissionAction.d.ts +2 -0
  213. package/dist/permissions/summarizePermissionAction.d.ts.map +1 -0
  214. package/dist/permissions/summarizePermissionAction.js +29 -0
  215. package/dist/protocol/SessionProtocol.d.ts +49 -2
  216. package/dist/protocol/SessionProtocol.d.ts.map +1 -1
  217. package/dist/protocol/index.d.ts +4 -1
  218. package/dist/protocol/index.d.ts.map +1 -1
  219. package/dist/providers/claude-sdk.d.ts.map +1 -1
  220. package/dist/providers/claude-sdk.js +7 -6
  221. package/dist/providers/codex.d.ts.map +1 -1
  222. package/dist/providers/codex.js +52 -6
  223. package/dist/providers/models.d.ts +12 -9
  224. package/dist/providers/models.d.ts.map +1 -1
  225. package/dist/providers/models.js +36 -0
  226. package/dist/providers/types.d.ts +3 -0
  227. package/dist/providers/types.d.ts.map +1 -1
  228. package/dist/review/createCodeReviewPrompt.d.ts +2 -0
  229. package/dist/review/createCodeReviewPrompt.d.ts.map +1 -0
  230. package/dist/review/createCodeReviewPrompt.js +11 -0
  231. package/dist/review/index.d.ts +2 -0
  232. package/dist/review/index.d.ts.map +1 -0
  233. package/dist/review/index.js +1 -0
  234. package/dist/server/AgentSessionManager.d.ts +10 -1
  235. package/dist/server/AgentSessionManager.d.ts.map +1 -1
  236. package/dist/server/AgentSessionManager.js +203 -12
  237. package/dist/server/InMemorySession.d.ts +34 -1
  238. package/dist/server/InMemorySession.d.ts.map +1 -1
  239. package/dist/server/InMemorySession.js +558 -10
  240. package/dist/server/InMemorySessionStore.d.ts +2 -0
  241. package/dist/server/InMemorySessionStore.d.ts.map +1 -1
  242. package/dist/server/InMemorySessionStore.js +7 -0
  243. package/dist/server/PersistentSessionStore.d.ts +2 -0
  244. package/dist/server/PersistentSessionStore.d.ts.map +1 -1
  245. package/dist/server/PersistentSessionStore.js +80 -5
  246. package/dist/server/createGoalTitle.d.ts +2 -0
  247. package/dist/server/createGoalTitle.d.ts.map +1 -0
  248. package/dist/server/createGoalTitle.js +4 -0
  249. package/dist/server/createModelCatalog.js +2 -2
  250. package/dist/server/createProtocolHttpServer.d.ts.map +1 -1
  251. package/dist/server/createProtocolHttpServer.js +119 -2
  252. package/dist/server/runLocalProtocolServer.d.ts.map +1 -1
  253. package/dist/server/runLocalProtocolServer.js +18 -2
  254. package/dist/tasks/index.d.ts +2 -0
  255. package/dist/tasks/index.d.ts.map +1 -0
  256. package/dist/tasks/index.js +1 -0
  257. package/dist/tasks/types.d.ts +39 -0
  258. package/dist/tasks/types.d.ts.map +1 -0
  259. package/dist/tasks/types.js +1 -0
  260. package/dist/tools/Agent.d.ts +13 -3
  261. package/dist/tools/Agent.d.ts.map +1 -1
  262. package/dist/tools/Agent.js +40 -12
  263. package/dist/tools/claude/AskUserQuestion.d.ts +23 -0
  264. package/dist/tools/claude/AskUserQuestion.d.ts.map +1 -0
  265. package/dist/tools/claude/AskUserQuestion.js +52 -0
  266. package/dist/tools/claude/Bash.d.ts +2 -0
  267. package/dist/tools/claude/Bash.d.ts.map +1 -1
  268. package/dist/tools/claude/Bash.js +20 -2
  269. package/dist/tools/claude/SendMessage.d.ts +10 -0
  270. package/dist/tools/claude/SendMessage.d.ts.map +1 -0
  271. package/dist/tools/claude/SendMessage.js +33 -0
  272. package/dist/tools/claude/TaskCreate.d.ts +12 -0
  273. package/dist/tools/claude/TaskCreate.d.ts.map +1 -0
  274. package/dist/tools/claude/TaskCreate.js +34 -0
  275. package/dist/tools/claude/TaskGet.d.ts +16 -0
  276. package/dist/tools/claude/TaskGet.d.ts.map +1 -0
  277. package/dist/tools/claude/TaskGet.js +44 -0
  278. package/dist/tools/claude/TaskList.d.ts +10 -0
  279. package/dist/tools/claude/TaskList.d.ts.map +1 -0
  280. package/dist/tools/claude/TaskList.js +51 -0
  281. package/dist/tools/claude/TaskOutput.d.ts +17 -0
  282. package/dist/tools/claude/TaskOutput.d.ts.map +1 -0
  283. package/dist/tools/claude/TaskOutput.js +74 -0
  284. package/dist/tools/claude/TaskStop.d.ts +10 -0
  285. package/dist/tools/claude/TaskStop.d.ts.map +1 -0
  286. package/dist/tools/claude/TaskStop.js +42 -0
  287. package/dist/tools/claude/TaskUpdate.d.ts +21 -0
  288. package/dist/tools/claude/TaskUpdate.d.ts.map +1 -0
  289. package/dist/tools/claude/TaskUpdate.js +55 -0
  290. package/dist/tools/claude/index.d.ts +111 -5
  291. package/dist/tools/claude/index.d.ts.map +1 -1
  292. package/dist/tools/claude/index.js +24 -2
  293. package/dist/tools/claude/parseBackgroundTaskId.d.ts +2 -0
  294. package/dist/tools/claude/parseBackgroundTaskId.d.ts.map +1 -0
  295. package/dist/tools/claude/parseBackgroundTaskId.js +7 -0
  296. package/dist/tools/claude/taskSchemas.d.ts +14 -0
  297. package/dist/tools/claude/taskSchemas.d.ts.map +1 -0
  298. package/dist/tools/claude/taskSchemas.js +18 -0
  299. package/dist/tools/codex/exec_command.d.ts +8 -6
  300. package/dist/tools/codex/exec_command.d.ts.map +1 -1
  301. package/dist/tools/codex/exec_command.js +37 -18
  302. package/dist/tools/codex/followup_task.d.ts +11 -0
  303. package/dist/tools/codex/followup_task.d.ts.map +1 -0
  304. package/dist/tools/codex/followup_task.js +18 -0
  305. package/dist/tools/codex/humanizeTaskName.d.ts +2 -0
  306. package/dist/tools/codex/humanizeTaskName.d.ts.map +1 -0
  307. package/dist/tools/codex/humanizeTaskName.js +4 -0
  308. package/dist/tools/codex/index.d.ts +90 -7
  309. package/dist/tools/codex/index.d.ts.map +1 -1
  310. package/dist/tools/codex/index.js +24 -0
  311. package/dist/tools/codex/interrupt_agent.d.ts +10 -0
  312. package/dist/tools/codex/interrupt_agent.d.ts.map +1 -0
  313. package/dist/tools/codex/interrupt_agent.js +17 -0
  314. package/dist/tools/codex/list_agents.d.ts +12 -0
  315. package/dist/tools/codex/list_agents.d.ts.map +1 -0
  316. package/dist/tools/codex/list_agents.js +23 -0
  317. package/dist/tools/codex/request_user_input.d.ts +16 -0
  318. package/dist/tools/codex/request_user_input.d.ts.map +1 -0
  319. package/dist/tools/codex/request_user_input.js +58 -0
  320. package/dist/tools/codex/requireSubagentContext.d.ts +3 -0
  321. package/dist/tools/codex/requireSubagentContext.d.ts.map +1 -0
  322. package/dist/tools/codex/requireSubagentContext.js +6 -0
  323. package/dist/tools/codex/spawn_agent.d.ts +9 -0
  324. package/dist/tools/codex/spawn_agent.d.ts.map +1 -0
  325. package/dist/tools/codex/spawn_agent.js +35 -0
  326. package/dist/tools/codex/subagentSchemas.d.ts +8 -0
  327. package/dist/tools/codex/subagentSchemas.d.ts.map +1 -0
  328. package/dist/tools/codex/subagentSchemas.js +13 -0
  329. package/dist/tools/codex/unifiedExecOutput.d.ts +14 -0
  330. package/dist/tools/codex/unifiedExecOutput.d.ts.map +1 -0
  331. package/dist/tools/codex/unifiedExecOutput.js +44 -0
  332. package/dist/tools/codex/update_plan.d.ts +10 -0
  333. package/dist/tools/codex/update_plan.d.ts.map +1 -0
  334. package/dist/tools/codex/update_plan.js +34 -0
  335. package/dist/tools/codex/wait_agent.d.ts +13 -0
  336. package/dist/tools/codex/wait_agent.d.ts.map +1 -0
  337. package/dist/tools/codex/wait_agent.js +32 -0
  338. package/dist/tools/codex/write_stdin.d.ts +6 -1
  339. package/dist/tools/codex/write_stdin.d.ts.map +1 -1
  340. package/dist/tools/codex/write_stdin.js +44 -12
  341. package/dist/tools/goals/create_goal.d.ts +11 -0
  342. package/dist/tools/goals/create_goal.d.ts.map +1 -0
  343. package/dist/tools/goals/create_goal.js +29 -0
  344. package/dist/tools/goals/get_goal.d.ts +9 -0
  345. package/dist/tools/goals/get_goal.d.ts.map +1 -0
  346. package/dist/tools/goals/get_goal.js +26 -0
  347. package/dist/tools/goals/goalSchemas.d.ts +8 -0
  348. package/dist/tools/goals/goalSchemas.d.ts.map +1 -0
  349. package/dist/tools/goals/goalSchemas.js +13 -0
  350. package/dist/tools/goals/index.d.ts +30 -0
  351. package/dist/tools/goals/index.d.ts.map +1 -0
  352. package/dist/tools/goals/index.js +7 -0
  353. package/dist/tools/goals/update_goal.d.ts +11 -0
  354. package/dist/tools/goals/update_goal.d.ts.map +1 -0
  355. package/dist/tools/goals/update_goal.js +33 -0
  356. package/dist/tools/index.d.ts +1 -0
  357. package/dist/tools/index.d.ts.map +1 -1
  358. package/dist/tools/index.js +1 -0
  359. package/dist/tools/pi/bash.d.ts.map +1 -1
  360. package/dist/tools/pi/bash.js +2 -0
  361. package/dist/tools/utils/readSessionWithProgress.d.ts +9 -0
  362. package/dist/tools/utils/readSessionWithProgress.d.ts.map +1 -0
  363. package/dist/tools/utils/readSessionWithProgress.js +27 -0
  364. package/dist/tools/utils/shell.d.ts +2 -0
  365. package/dist/tools/utils/shell.d.ts.map +1 -1
  366. package/dist/tools/utils/shell.js +37 -0
  367. package/dist/tools/utils/summarizeShellOutput.d.ts.map +1 -1
  368. package/dist/tools/utils/summarizeShellOutput.js +2 -0
  369. package/dist/user-input/index.d.ts +2 -0
  370. package/dist/user-input/index.d.ts.map +1 -0
  371. package/dist/user-input/index.js +1 -0
  372. package/dist/user-input/types.d.ts +19 -0
  373. package/dist/user-input/types.d.ts.map +1 -0
  374. package/dist/user-input/types.js +1 -0
  375. package/dist/web/assets/highlighted-body-OFNGDK62-BuJOCQJD.js +1 -0
  376. package/dist/web/assets/highlighted-body-OFNGDK62-TwhCqUaJ.js +1 -0
  377. package/dist/web/assets/index-B6Bc6sUJ.css +1 -0
  378. package/dist/web/assets/index-BC99-iZC.js +341 -0
  379. package/dist/web/assets/index-BZN9jSiw.js +341 -0
  380. package/dist/web/assets/index-CcF0unjO.css +1 -0
  381. package/dist/web/index.html +2 -2
  382. package/package.json +2 -1
@@ -4,6 +4,7 @@ import { basename, join } from "node:path";
4
4
  import { CURSOR_MARKER, Editor, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "@earendil-works/pi-tui";
5
5
  import { formatSkillInvocation, loadSkills, } from "../agent/index.js";
6
6
  import { parseSkillFrontmatter } from "../agent/skills/parseSkillFrontmatter.js";
7
+ import { createCodeReviewPrompt } from "../review/index.js";
7
8
  import { createSelectionPanel } from "./createSelectionPanel.js";
8
9
  import { createSlashCommands } from "./createSlashCommands.js";
9
10
  import { describeModelChoice } from "./describeModelChoice.js";
@@ -13,9 +14,14 @@ import { formatActivityElapsedTime } from "./formatActivityElapsedTime.js";
13
14
  import { FileMentionAutocomplete } from "./FileMentionAutocomplete.js";
14
15
  import { formatFileMention } from "./formatFileMention.js";
15
16
  import { humanizeReasoningLevel } from "./humanizeReasoningLevel.js";
17
+ import { humanizePermissionMode } from "./humanizePermissionMode.js";
18
+ import { humanizeGoalStatus } from "./humanizeGoalStatus.js";
19
+ import { humanizeToolName } from "./humanizeToolName.js";
16
20
  import { readClipboardImage, } from "./readClipboardImage.js";
17
21
  import { ACTIVITY_WAVE_FRAME_COUNT, renderActivityWave } from "./renderActivityWave.js";
18
22
  import { renderAgentMarkdown } from "./renderAgentMarkdown.js";
23
+ import { sanitizeTerminalText } from "./sanitizeTerminalText.js";
24
+ import { upsertSubagentSummary } from "./upsertSubagentSummary.js";
19
25
  const RESET = "\x1b[0m";
20
26
  const BOLD = "\x1b[1m";
21
27
  const DIM = "\x1b[2m";
@@ -48,6 +54,8 @@ const IMAGE_PASTE_RAW_KEYS = new Set(["\x16", "\x1bv"]);
48
54
  const AUTOCOMPLETE_MAX_VISIBLE = 6;
49
55
  const BRACKETED_PASTE_START = "\x1b[200~";
50
56
  const BRACKETED_PASTE_END = "\x1b[201~";
57
+ const TERMINAL_FOCUS_IN = "\x1b[I";
58
+ const TERMINAL_FOCUS_OUT = "\x1b[O";
51
59
  const IMAGE_PLACEHOLDER_REGEX = /\[Image #(\d+) [A-Z0-9]+\]/gu;
52
60
  const IMAGE_CHIP_BG = "\x1b[48;5;240m";
53
61
  const IMAGE_CHIP_FG = "\x1b[38;5;255m";
@@ -73,6 +81,7 @@ export class CodingAssistantApp {
73
81
  #onDefaultModelChange;
74
82
  #onSettingsChange;
75
83
  #onExit;
84
+ #respondUserInput;
76
85
  #processManager;
77
86
  #readClipboardImage;
78
87
  #tui;
@@ -81,6 +90,8 @@ export class CodingAssistantApp {
81
90
  #abortController;
82
91
  #abortNotified = false;
83
92
  #activeRun;
93
+ #activeUserInput;
94
+ #answeringUserInputRequestId;
84
95
  #activityAnimationFrame = 0;
85
96
  #activityStartedAtMs;
86
97
  #activityAnimationTimer;
@@ -92,7 +103,10 @@ export class CodingAssistantApp {
92
103
  #exiting = false;
93
104
  #exitResolve;
94
105
  #focused = false;
106
+ #terminalFocused = true;
107
+ #freeformUserInput;
95
108
  #pendingPrompts = [];
109
+ #compacting = false;
96
110
  #pastedImagesById = new Map();
97
111
  #selectionPanel;
98
112
  #dismissedSlashCommandText;
@@ -101,6 +115,7 @@ export class CodingAssistantApp {
101
115
  #showReasoning;
102
116
  #sessionBacked;
103
117
  #modelLocked;
118
+ #mcpServers;
104
119
  #slashCommandSelectionIndex = 0;
105
120
  #slashCommands = createSlashCommands();
106
121
  #skillCommands = [];
@@ -115,8 +130,12 @@ export class CodingAssistantApp {
115
130
  #statusText = "Idle";
116
131
  #stopped = false;
117
132
  #streamEntryId;
133
+ #subagents;
134
+ #tasks;
118
135
  #thinkingEntryIdsByContentIndex = new Map();
119
136
  #toolCallEntryIdsByContentIndex = new Map();
137
+ #runningToolCallIds = new Set();
138
+ #userInputRequests = [];
120
139
  constructor(options) {
121
140
  this.#agent = options.agent;
122
141
  this.#cwd = options.cwd;
@@ -125,11 +144,15 @@ export class CodingAssistantApp {
125
144
  this.#onDefaultModelChange = options.onDefaultModelChange;
126
145
  this.#onSettingsChange = options.onSettingsChange;
127
146
  this.#onExit = options.onExit;
147
+ this.#respondUserInput = options.respondUserInput;
128
148
  this.#processManager = options.processManager;
129
149
  this.#readClipboardImage = options.readClipboardImage ?? readClipboardImage;
130
150
  this.#sessionBacked = options.sessionBacked ?? false;
131
151
  this.#showReasoning = options.showReasoning ?? false;
132
152
  this.#modelLocked = options.modelLocked ?? !options.agent.canChangeModel;
153
+ this.#mcpServers = options.initialMcpServers ?? [];
154
+ this.#subagents = options.initialSubagents ?? [];
155
+ this.#tasks = options.initialTasks ?? [];
133
156
  this.#tui = options.tui;
134
157
  this.#version = options.version ?? "0.0.0";
135
158
  this.#editor = new Editor(this.#tui, EDITOR_THEME, { paddingX: 0 });
@@ -143,6 +166,9 @@ export class CodingAssistantApp {
143
166
  this.#editor.onSubmit = (value) => {
144
167
  this.#submit(value);
145
168
  };
169
+ for (const request of options.initialUserInputs ?? []) {
170
+ this.#enqueueUserInputRequest(request);
171
+ }
146
172
  for (const event of options.initialSessionEvents ?? []) {
147
173
  this.applySessionEvent(event);
148
174
  }
@@ -153,14 +179,16 @@ export class CodingAssistantApp {
153
179
  }
154
180
  set focused(value) {
155
181
  this.#focused = value;
156
- this.#editor.focused = value;
182
+ this.#editor.focused = value && this.#terminalFocused;
157
183
  this.#cursorVisible = true;
158
- if (value) {
184
+ if (value && this.#terminalFocused) {
159
185
  this.#cursorTyping = false;
160
186
  this.#startCursorBlink();
161
187
  }
162
188
  else {
163
189
  this.#stopCursorBlink();
190
+ if (!this.#terminalFocused)
191
+ this.#cursorVisible = false;
164
192
  }
165
193
  }
166
194
  start(options = {}) {
@@ -222,6 +250,29 @@ export class CodingAssistantApp {
222
250
  this.#applyAgentMessage(event.data.message);
223
251
  return;
224
252
  }
253
+ if (event.type === "user_input_requested") {
254
+ this.#enqueueUserInputRequest(event.data);
255
+ return;
256
+ }
257
+ if (event.type === "user_input_resolved") {
258
+ this.#removeUserInputRequest(event.data.requestId);
259
+ return;
260
+ }
261
+ if (event.type === "mcp_servers_changed") {
262
+ this.#mcpServers = event.data.servers;
263
+ this.#requestRender();
264
+ return;
265
+ }
266
+ if (event.type === "tasks_changed") {
267
+ this.#tasks = event.data.tasks;
268
+ this.#requestRender();
269
+ return;
270
+ }
271
+ if (event.type === "subagent_changed") {
272
+ this.#subagents = upsertSubagentSummary(this.#subagents, event.data.subagent);
273
+ this.#requestRender();
274
+ return;
275
+ }
225
276
  if (event.type === "run_finished") {
226
277
  this.#running = false;
227
278
  this.#statusText =
@@ -230,6 +281,8 @@ export class CodingAssistantApp {
230
281
  this.#streamEntryId = undefined;
231
282
  this.#thinkingEntryIdsByContentIndex.clear();
232
283
  this.#toolCallEntryIdsByContentIndex.clear();
284
+ this.#runningToolCallIds.clear();
285
+ this.#clearUserInputRequests();
233
286
  this.#requestRender();
234
287
  return;
235
288
  }
@@ -237,6 +290,8 @@ export class CodingAssistantApp {
237
290
  this.#running = false;
238
291
  this.#statusText = "Error";
239
292
  this.#stopActivityAnimation();
293
+ this.#runningToolCallIds.clear();
294
+ this.#clearUserInputRequests();
240
295
  this.#appendEntry({ role: "error", text: event.data.errorMessage });
241
296
  return;
242
297
  }
@@ -247,6 +302,8 @@ export class CodingAssistantApp {
247
302
  this.#streamEntryId = undefined;
248
303
  this.#thinkingEntryIdsByContentIndex.clear();
249
304
  this.#toolCallEntryIdsByContentIndex.clear();
305
+ this.#runningToolCallIds.clear();
306
+ this.#clearUserInputRequests();
250
307
  this.#appendEntry({
251
308
  role: "system",
252
309
  text: "Session reset. Started a new session.",
@@ -272,6 +329,15 @@ export class CodingAssistantApp {
272
329
  });
273
330
  return;
274
331
  }
332
+ if (event.type === "permission_mode_changed") {
333
+ this.#appendEntry({
334
+ role: "event",
335
+ title: "permissions",
336
+ text: `Permissions changed to ${humanizePermissionMode(event.data.permissionMode)}.`,
337
+ });
338
+ this.#requestRender();
339
+ return;
340
+ }
275
341
  }
276
342
  async waitForIdle() {
277
343
  for (;;) {
@@ -291,15 +357,41 @@ export class CodingAssistantApp {
291
357
  if (this.#stopped || this.#exiting) {
292
358
  return;
293
359
  }
360
+ if (data === TERMINAL_FOCUS_IN || data === TERMINAL_FOCUS_OUT) {
361
+ this.#setTerminalFocused(data === TERMINAL_FOCUS_IN);
362
+ return;
363
+ }
294
364
  if (this.#selectionPanel !== undefined) {
295
365
  if (matchesKey(data, "ctrl+c") || data === "\x03") {
296
- void this.stop();
366
+ this.#selectionPanel.handleInput?.("\x1b");
367
+ this.#requestRender();
297
368
  return;
298
369
  }
299
370
  this.#selectionPanel.handleInput?.(data);
300
371
  this.#requestRender();
301
372
  return;
302
373
  }
374
+ if (this.#freeformUserInput !== undefined) {
375
+ if (this.#handlePastedInput(data))
376
+ return;
377
+ if (matchesKey(data, "ctrl+c") || data === "\x03") {
378
+ this.#handleCtrlC();
379
+ return;
380
+ }
381
+ if (matchesKey(data, "ctrl+d") && this.#editor.getText().length === 0) {
382
+ void this.stop();
383
+ return;
384
+ }
385
+ if (matchesKey(data, "escape")) {
386
+ this.#handleEscape();
387
+ this.#requestRender();
388
+ return;
389
+ }
390
+ this.#markTypingActivity();
391
+ this.#editor.handleInput(data);
392
+ this.#requestRender();
393
+ return;
394
+ }
303
395
  if (this.#handlePastedInput(data)) {
304
396
  return;
305
397
  }
@@ -307,13 +399,18 @@ export class CodingAssistantApp {
307
399
  return;
308
400
  }
309
401
  if (matchesKey(data, "ctrl+c") || data === "\x03") {
310
- void this.stop();
402
+ this.#handleCtrlC();
311
403
  return;
312
404
  }
313
405
  if (matchesKey(data, "ctrl+d") && this.#editor.getText().length === 0) {
314
406
  void this.stop();
315
407
  return;
316
408
  }
409
+ if (this.#running && matchesKey(data, "escape")) {
410
+ this.#handleEscape();
411
+ this.#requestRender();
412
+ return;
413
+ }
317
414
  const previousSlashCommandSuggestionCount = this.#slashCommandSuggestions().length;
318
415
  const previousFileMentionSuggestionCount = this.#fileMentionSnapshot()?.items.length ?? 0;
319
416
  if (this.#handleSlashCommandAutocompleteInput(data)) {
@@ -321,6 +418,12 @@ export class CodingAssistantApp {
321
418
  this.#requestRender(nextSlashCommandSuggestionCount < previousSlashCommandSuggestionCount);
322
419
  return;
323
420
  }
421
+ if (this.#running &&
422
+ matchesKey(data, "tab") &&
423
+ previousFileMentionSuggestionCount === 0 &&
424
+ this.#queueCurrentInput()) {
425
+ return;
426
+ }
324
427
  if (this.#fileMentionAutocomplete?.handleInput(data, this.#editor.getLines(), this.#editor.getCursor(), (path, context) => this.#completeFileMention(path, context)) === true) {
325
428
  const nextFileMentionSuggestionCount = this.#fileMentionSnapshot()?.items.length ?? 0;
326
429
  this.#requestRender(nextFileMentionSuggestionCount < previousFileMentionSuggestionCount);
@@ -478,6 +581,7 @@ export class CodingAssistantApp {
478
581
  ...header,
479
582
  ...this.#renderTranscript(safeWidth),
480
583
  "",
584
+ ...this.#renderQueuedPrompts(safeWidth),
481
585
  ...(this.#selectionPanel === undefined
482
586
  ? input
483
587
  : this.#selectionPanel.render(safeWidth)),
@@ -488,6 +592,10 @@ export class CodingAssistantApp {
488
592
  ];
489
593
  }
490
594
  #submit(value) {
595
+ if (this.#freeformUserInput !== undefined) {
596
+ this.#submitFreeformUserInput(value);
597
+ return;
598
+ }
491
599
  const submission = this.#submitAsync(value).catch((error) => {
492
600
  this.#appendEntry({ role: "error", text: this.#formatError(error) });
493
601
  });
@@ -500,6 +608,14 @@ export class CodingAssistantApp {
500
608
  this.#activeSubmission = trackedSubmission;
501
609
  }
502
610
  async #submitAsync(value) {
611
+ if (this.#compacting) {
612
+ this.#appendEntry({
613
+ role: "event",
614
+ title: "compact",
615
+ text: "Wait for conversation compaction to finish before submitting.",
616
+ });
617
+ return;
618
+ }
503
619
  const submission = this.#createPromptSubmission(value);
504
620
  if (submission === undefined) {
505
621
  return;
@@ -512,20 +628,27 @@ export class CodingAssistantApp {
512
628
  this.#requestRender();
513
629
  return;
514
630
  }
631
+ if (prompt === "/compact") {
632
+ await this.#compactSession();
633
+ this.#requestRender();
634
+ return;
635
+ }
515
636
  if (this.#handleCommand(prompt)) {
516
637
  this.#requestRender();
517
638
  return;
518
639
  }
519
640
  this.#modelLocked = true;
641
+ if (this.#running) {
642
+ await this.#agent.steer(submission.content, { displayText: submission.displayText });
643
+ this.#clearSubmittedImages(prompt);
644
+ if (!this.#sessionBacked)
645
+ this.#appendEntry({ role: "user", text: prompt });
646
+ this.#requestRender();
647
+ return;
648
+ }
520
649
  if (!this.#sessionBacked) {
521
650
  this.#appendEntry({ role: "user", text: prompt });
522
- }
523
- if (this.#running && !this.#sessionBacked) {
524
- this.#appendEntry({
525
- role: "event",
526
- title: "queue",
527
- text: `Queued behind the active run.`,
528
- });
651
+ submission.transcriptAppended = true;
529
652
  }
530
653
  this.#pendingPrompts.push(submission);
531
654
  this.#startDrainQueue();
@@ -536,8 +659,7 @@ export class CodingAssistantApp {
536
659
  if (prompt.length === 0) {
537
660
  return undefined;
538
661
  }
539
- const content = this.#contentFromPrompt(prompt);
540
- this.#clearSubmittedImages(prompt);
662
+ const content = createCodeReviewPrompt(prompt) ?? this.#contentFromPrompt(prompt);
541
663
  return {
542
664
  content,
543
665
  displayText: prompt,
@@ -620,6 +742,12 @@ export class CodingAssistantApp {
620
742
  }
621
743
  const expandedPrompt = formatSkillInvocation(skill, parseSkillFrontmatter(content).body, parsed[2] ?? "");
622
744
  this.#modelLocked = true;
745
+ if (this.#running) {
746
+ await this.#agent.steer(expandedPrompt, { displayText: prompt });
747
+ if (!this.#sessionBacked)
748
+ this.#appendEntry({ role: "user", text: prompt });
749
+ return;
750
+ }
623
751
  if (!this.#sessionBacked) {
624
752
  this.#appendEntry({ role: "user", text: prompt });
625
753
  }
@@ -634,6 +762,13 @@ export class CodingAssistantApp {
634
762
  this.#startDrainQueue();
635
763
  }
636
764
  #handleCommand(prompt) {
765
+ if (prompt === "/goal" || prompt.startsWith("/goal ")) {
766
+ void this.#handleGoalCommand(prompt).catch((error) => {
767
+ this.#appendEntry({ role: "error", text: this.#formatError(error) });
768
+ this.#requestRender();
769
+ });
770
+ return true;
771
+ }
637
772
  if (prompt === "/model") {
638
773
  this.#openModelMenu();
639
774
  return true;
@@ -649,6 +784,22 @@ export class CodingAssistantApp {
649
784
  this.#openConfigureMenu();
650
785
  return true;
651
786
  }
787
+ if (prompt === "/permissions" || prompt === "/permission") {
788
+ this.#openPermissionsMenu();
789
+ return true;
790
+ }
791
+ if (prompt === "/mcp") {
792
+ this.#showMcpStatus();
793
+ return true;
794
+ }
795
+ if (prompt === "/tasks" || prompt === "/todos") {
796
+ this.#showTasks();
797
+ return true;
798
+ }
799
+ if (prompt === "/agents") {
800
+ this.#showSubagents();
801
+ return true;
802
+ }
652
803
  if (prompt === "/new") {
653
804
  this.#resetSession();
654
805
  return true;
@@ -677,6 +828,145 @@ export class CodingAssistantApp {
677
828
  }
678
829
  return false;
679
830
  }
831
+ async #handleGoalCommand(prompt) {
832
+ const argument = prompt.slice("/goal".length).trim();
833
+ const goal = this.#agent.goal;
834
+ if (argument.length === 0) {
835
+ this.#appendEntry({
836
+ role: "event",
837
+ title: "goal",
838
+ text: goal === undefined
839
+ ? "No goal is set. Use /goal followed by an objective to start one."
840
+ : `Status: ${humanizeGoalStatus(goal.status)}\nObjective: ${goal.objective}\n\nCommands: /goal pause, /goal resume, /goal clear`,
841
+ });
842
+ return;
843
+ }
844
+ if (argument === "pause" || argument === "resume") {
845
+ if (this.#agent.changeGoalStatus === undefined) {
846
+ throw new Error("Goal controls are unavailable in this session.");
847
+ }
848
+ const status = argument === "pause" ? "paused" : "active";
849
+ await this.#agent.changeGoalStatus(status);
850
+ this.#appendEntry({
851
+ role: "event",
852
+ title: "goal",
853
+ text: argument === "pause" ? "Goal paused." : "Goal resumed.",
854
+ });
855
+ return;
856
+ }
857
+ if (argument === "clear") {
858
+ if (this.#agent.clearGoal === undefined) {
859
+ throw new Error("Goal controls are unavailable in this session.");
860
+ }
861
+ await this.#agent.clearGoal();
862
+ this.#appendEntry({ role: "event", title: "goal", text: "Goal cleared." });
863
+ return;
864
+ }
865
+ if (this.#agent.setGoal === undefined) {
866
+ throw new Error("Goal controls are unavailable in this session.");
867
+ }
868
+ await this.#agent.setGoal(argument);
869
+ this.#appendEntry({
870
+ role: "event",
871
+ title: "goal",
872
+ text: `Goal started: ${argument}`,
873
+ });
874
+ }
875
+ #showMcpStatus() {
876
+ if (this.#mcpServers.length === 0) {
877
+ this.#appendEntry({
878
+ role: "event",
879
+ title: "MCP servers",
880
+ text: "No MCP servers have connected in this session.",
881
+ });
882
+ return;
883
+ }
884
+ const text = this.#mcpServers
885
+ .map((server) => {
886
+ if (server.status === "connected") {
887
+ return `${server.name}: connected with ${server.toolCount} tool${server.toolCount === 1 ? "" : "s"}`;
888
+ }
889
+ if (server.status === "disabled")
890
+ return `${server.name}: disabled`;
891
+ return `${server.name}: could not connect${server.errorMessage === undefined ? "" : ` — ${server.errorMessage}`}`;
892
+ })
893
+ .join("\n");
894
+ this.#appendEntry({ role: "event", title: "MCP servers", text });
895
+ }
896
+ #showTasks() {
897
+ if (this.#tasks.length === 0) {
898
+ this.#appendEntry({
899
+ role: "event",
900
+ title: "Tasks",
901
+ text: "No tasks are being tracked in this session.",
902
+ });
903
+ return;
904
+ }
905
+ const status = {
906
+ completed: "Completed",
907
+ in_progress: "In progress",
908
+ pending: "Pending",
909
+ };
910
+ this.#appendEntry({
911
+ role: "event",
912
+ title: "Tasks",
913
+ text: this.#tasks
914
+ .map((task) => `#${task.id} · ${status[task.status]} · ${task.subject}`)
915
+ .join("\n"),
916
+ });
917
+ }
918
+ #showSubagents() {
919
+ if (this.#subagents.length === 0) {
920
+ this.#appendEntry({
921
+ role: "event",
922
+ title: "Subagents",
923
+ text: "No delegated work has been started in this session.",
924
+ });
925
+ return;
926
+ }
927
+ const labels = {
928
+ aborted: "Stopped",
929
+ completed: "Completed",
930
+ error: "Failed",
931
+ idle: "Idle",
932
+ queued: "Queued",
933
+ running: "Running",
934
+ };
935
+ this.#appendEntry({
936
+ role: "event",
937
+ title: "Subagents",
938
+ text: this.#subagents
939
+ .map((subagent) => `${labels[subagent.status]} · ${subagent.description}`)
940
+ .join("\n"),
941
+ });
942
+ }
943
+ async #compactSession() {
944
+ if (this.#compacting || this.#running || this.#pendingPrompts.length > 0) {
945
+ this.#appendEntry({
946
+ role: "event",
947
+ title: "compact",
948
+ text: "Wait for the active response to finish before compacting.",
949
+ });
950
+ return;
951
+ }
952
+ this.#compacting = true;
953
+ this.#statusText = "Compacting conversation";
954
+ this.#requestRender();
955
+ try {
956
+ const result = await this.#agent.compact();
957
+ this.#appendEntry({
958
+ role: "event",
959
+ title: "compact",
960
+ text: result.compacted
961
+ ? `Compacted ${result.compactedMessageCount} older messages. The full transcript remains visible.`
962
+ : "There is not enough conversation history to compact yet.",
963
+ });
964
+ }
965
+ finally {
966
+ this.#compacting = false;
967
+ this.#statusText = "Idle";
968
+ }
969
+ }
680
970
  #resetSession() {
681
971
  this.#abortActiveRun({ silent: true });
682
972
  this.#runToken += 1;
@@ -688,6 +978,7 @@ export class CodingAssistantApp {
688
978
  this.#streamEntryId = undefined;
689
979
  this.#thinkingEntryIdsByContentIndex.clear();
690
980
  this.#toolCallEntryIdsByContentIndex.clear();
981
+ this.#runningToolCallIds.clear();
691
982
  this.#abortNotified = false;
692
983
  this.#statusText = "Idle";
693
984
  this.#agent.reset();
@@ -697,11 +988,14 @@ export class CodingAssistantApp {
697
988
  });
698
989
  }
699
990
  #startDrainQueue() {
700
- if (this.#activeRun !== undefined) {
991
+ if (this.#activeRun !== undefined ||
992
+ this.#compacting ||
993
+ this.#pendingPrompts.length === 0) {
701
994
  return;
702
995
  }
703
996
  this.#activeRun = this.#drainQueue().finally(() => {
704
997
  this.#activeRun = undefined;
998
+ this.#startDrainQueue();
705
999
  this.#requestRender();
706
1000
  });
707
1001
  }
@@ -727,6 +1021,10 @@ export class CodingAssistantApp {
727
1021
  this.#activityStartedAtMs = this.#now();
728
1022
  this.#startActivityAnimation();
729
1023
  this.#requestRender();
1024
+ this.#clearSubmittedImages(prompt.displayText);
1025
+ if (!this.#sessionBacked && prompt.transcriptAppended !== true) {
1026
+ this.#appendEntry({ role: "user", text: prompt.displayText });
1027
+ }
730
1028
  try {
731
1029
  await this.#refreshSkillCommands({ force: true });
732
1030
  if (!this.#isCurrentRun(runToken)) {
@@ -783,10 +1081,19 @@ export class CodingAssistantApp {
783
1081
  }
784
1082
  }
785
1083
  #handleEscape() {
786
- if (this.#abortActiveRun()) {
1084
+ this.#restoreQueuedPromptsToComposer();
1085
+ this.#abortActiveRun();
1086
+ }
1087
+ #restoreQueuedPromptsToComposer() {
1088
+ if (this.#pendingPrompts.length === 0)
787
1089
  return;
788
- }
789
- void this.stop();
1090
+ const draft = this.#editor.getText().trim();
1091
+ const restored = this.#pendingPrompts.map((prompt) => prompt.displayText);
1092
+ if (draft.length > 0)
1093
+ restored.push(draft);
1094
+ this.#pendingPrompts = [];
1095
+ this.#editor.setText(restored.join("\n"));
1096
+ this.#syncAutocompleteState();
790
1097
  }
791
1098
  #abortActiveRun(options = {}) {
792
1099
  if (!this.#running || this.#abortController === undefined) {
@@ -796,12 +1103,12 @@ export class CodingAssistantApp {
796
1103
  this.#runToken += 1;
797
1104
  controller.abort();
798
1105
  this.#abortController = undefined;
799
- this.#pendingPrompts = [];
800
1106
  this.#running = false;
801
1107
  this.#statusText = "Idle";
802
1108
  this.#streamEntryId = undefined;
803
1109
  this.#thinkingEntryIdsByContentIndex.clear();
804
1110
  this.#toolCallEntryIdsByContentIndex.clear();
1111
+ this.#runningToolCallIds.clear();
805
1112
  this.#stopActivityAnimation();
806
1113
  void this.#processManager.killAll({ forceAfterMs: 500 }).catch((error) => {
807
1114
  this.#appendEntry({ role: "error", text: this.#formatError(error) });
@@ -862,6 +1169,23 @@ export class CodingAssistantApp {
862
1169
  else if (event.type === "toolcall_end") {
863
1170
  this.#finishToolCall(event.contentIndex, event.toolCall);
864
1171
  }
1172
+ else if (event.type === "tool_execution_start") {
1173
+ this.#runningToolCallIds.add(event.toolCall.id);
1174
+ this.#statusText = `Running ${this.#runningToolCallIds.size} tool${this.#runningToolCallIds.size === 1 ? "" : "s"}`;
1175
+ }
1176
+ else if (event.type === "tool_execution_end") {
1177
+ this.#runningToolCallIds.delete(event.result.toolCallId);
1178
+ this.#finishToolResult(event.result);
1179
+ this.#statusText =
1180
+ this.#runningToolCallIds.size === 0
1181
+ ? "Working"
1182
+ : `Running ${this.#runningToolCallIds.size} tool${this.#runningToolCallIds.size === 1 ? "" : "s"}`;
1183
+ }
1184
+ else if (event.type === "tool_execution_progress") {
1185
+ const entry = this.#entries.find((candidate) => candidate.id === event.toolCallId);
1186
+ if (entry !== undefined)
1187
+ entry.detail = this.#singleLine(event.display);
1188
+ }
865
1189
  else if (event.type === "done") {
866
1190
  this.#statusText = event.reason === "toolUse" ? "Running tools" : "Idle";
867
1191
  }
@@ -925,7 +1249,7 @@ export class CodingAssistantApp {
925
1249
  this.#appendEntry({
926
1250
  id: block.id,
927
1251
  role: "tool",
928
- title: block.name,
1252
+ title: this.#toolDisplayName(block.name),
929
1253
  text: this.#formatToolCall(block.name, block.arguments),
930
1254
  });
931
1255
  }
@@ -999,14 +1323,14 @@ export class CodingAssistantApp {
999
1323
  }
1000
1324
  #finishToolCall(contentIndex, toolCall) {
1001
1325
  this.#seenToolCallIds.add(toolCall.id);
1002
- this.#statusText = `Calling ${toolCall.name}`;
1326
+ this.#statusText = `Calling ${this.#toolDisplayName(toolCall.name)}`;
1003
1327
  const existingId = this.#toolCallEntryIdsByContentIndex.get(contentIndex);
1004
1328
  const existing = existingId === undefined
1005
1329
  ? undefined
1006
1330
  : this.#entries.find((entry) => entry.id === existingId);
1007
1331
  if (existing !== undefined) {
1008
1332
  existing.id = toolCall.id;
1009
- existing.title = toolCall.name;
1333
+ existing.title = this.#toolDisplayName(toolCall.name);
1010
1334
  existing.text = this.#formatToolCall(toolCall.name, toolCall.arguments);
1011
1335
  this.#toolCallEntryIdsByContentIndex.delete(contentIndex);
1012
1336
  return;
@@ -1015,7 +1339,7 @@ export class CodingAssistantApp {
1015
1339
  this.#appendEntry({
1016
1340
  id: toolCall.id,
1017
1341
  role: "tool",
1018
- title: toolCall.name,
1342
+ title: this.#toolDisplayName(toolCall.name),
1019
1343
  text: this.#formatToolCall(toolCall.name, toolCall.arguments),
1020
1344
  });
1021
1345
  }
@@ -1124,6 +1448,9 @@ export class CodingAssistantApp {
1124
1448
  return this.#renderAutocomplete(width, suggestions, selectedIndex);
1125
1449
  }
1126
1450
  const parts = [`${FOOTER_MODEL_FG}${this.#modelWithReasoningDisplayName()}${RESET}`];
1451
+ if (this.#running) {
1452
+ parts.push(`${FOOTER_QUEUED_FG}Enter steers · Tab queues${RESET}`);
1453
+ }
1127
1454
  parts.push(`${FOOTER_CWD_FG}${this.#cwdDisplayName()}${RESET}`);
1128
1455
  if (this.#pendingPrompts.length > 0) {
1129
1456
  parts.push(`${FOOTER_QUEUED_FG}queued ${this.#pendingPrompts.length}${RESET}`);
@@ -1131,6 +1458,15 @@ export class CodingAssistantApp {
1131
1458
  const line = `${" ".repeat(visibleWidth(INPUT_PROMPT))}${parts.join(`${DIM} • ${RESET}`)}`;
1132
1459
  return [this.#fitLine(line, width)];
1133
1460
  }
1461
+ #renderQueuedPrompts(width) {
1462
+ return this.#pendingPrompts.flatMap((prompt) => {
1463
+ const prefix = `${DIM}↳ queued${RESET} `;
1464
+ const prefixWidth = visibleWidth(prefix);
1465
+ const wrapped = wrapTextWithAnsi(prompt.displayText, Math.max(1, width - prefixWidth)).slice(0, 3);
1466
+ const indent = " ".repeat(prefixWidth);
1467
+ return wrapped.map((line, index) => this.#fitLine(`${index === 0 ? prefix : indent}${DIM}${line}${RESET}`, width));
1468
+ });
1469
+ }
1134
1470
  #renderAutocomplete(width, suggestions, selectedIndex, maxVisible = AUTOCOMPLETE_MAX_VISIBLE) {
1135
1471
  const rowWidth = Math.max(1, width - 1);
1136
1472
  const visibleCount = Math.max(1, Math.min(maxVisible, AUTOCOMPLETE_MAX_VISIBLE));
@@ -1295,6 +1631,221 @@ export class CodingAssistantApp {
1295
1631
  });
1296
1632
  this.#showSelectionPanel(panel);
1297
1633
  }
1634
+ #openPermissionsMenu() {
1635
+ const panel = createSelectionPanel({
1636
+ title: "Choose Permissions",
1637
+ subtitle: "Applies to this session and its subagents",
1638
+ selectedValue: this.#agent.permissionMode,
1639
+ items: [
1640
+ {
1641
+ value: "auto",
1642
+ label: "Auto",
1643
+ description: "Automatically review risky actions; ask only when needed.",
1644
+ },
1645
+ {
1646
+ value: "workspace_write",
1647
+ label: "Workspace write",
1648
+ description: "Write in the workspace; block shell network access.",
1649
+ },
1650
+ {
1651
+ value: "read_only",
1652
+ label: "Read only",
1653
+ description: "Keep project files read only; allow temporary files.",
1654
+ },
1655
+ {
1656
+ value: "full_access",
1657
+ label: "Full access",
1658
+ description: "Allow unrestricted filesystem, shell, and network access.",
1659
+ },
1660
+ ],
1661
+ onSelect: (item) => {
1662
+ const mode = item.value;
1663
+ this.#agent.setPermissionMode(mode);
1664
+ if (!this.#sessionBacked) {
1665
+ this.#appendEntry({
1666
+ role: "event",
1667
+ title: "permissions",
1668
+ text: `Permissions changed to ${humanizePermissionMode(mode)}.`,
1669
+ });
1670
+ }
1671
+ this.#closeSelectionPanel();
1672
+ this.#requestRender();
1673
+ },
1674
+ onCancel: () => this.#closeSelectionPanel(),
1675
+ });
1676
+ this.#showSelectionPanel(panel);
1677
+ }
1678
+ #enqueueUserInputRequest(request) {
1679
+ if (this.#userInputRequests.some((candidate) => candidate.requestId === request.requestId)) {
1680
+ return;
1681
+ }
1682
+ this.#userInputRequests.push(request);
1683
+ this.#openNextUserInputRequest();
1684
+ }
1685
+ #openNextUserInputRequest() {
1686
+ if (this.#activeUserInput !== undefined ||
1687
+ this.#answeringUserInputRequestId !== undefined ||
1688
+ this.#freeformUserInput !== undefined) {
1689
+ return;
1690
+ }
1691
+ const request = this.#userInputRequests[0];
1692
+ if (request === undefined)
1693
+ return;
1694
+ this.#activeUserInput = {
1695
+ answers: {},
1696
+ questionIndex: 0,
1697
+ request,
1698
+ selected: new Set(),
1699
+ };
1700
+ this.#openUserInputQuestion();
1701
+ }
1702
+ #openUserInputQuestion() {
1703
+ const active = this.#activeUserInput;
1704
+ const question = active?.request.questions[active.questionIndex];
1705
+ if (active === undefined || question === undefined)
1706
+ return;
1707
+ const items = question.options.map((option, index) => ({
1708
+ value: `option:${index}`,
1709
+ label: active.selected.has(option.label) ? `✓ ${option.label}` : option.label,
1710
+ description: option.description,
1711
+ }));
1712
+ if (question.multiSelect && active.selected.size > 0) {
1713
+ items.push({
1714
+ value: "done",
1715
+ label: "Done",
1716
+ description: `Submit ${active.selected.size} selected answer${active.selected.size === 1 ? "" : "s"}.`,
1717
+ });
1718
+ }
1719
+ items.push({
1720
+ value: "other",
1721
+ label: "Type another answer",
1722
+ description: "Enter a response that is not listed.",
1723
+ });
1724
+ this.#showSelectionPanel(createSelectionPanel({
1725
+ title: question.header,
1726
+ subtitle: `${question.question} · ${active.questionIndex + 1} of ${active.request.questions.length}`,
1727
+ items,
1728
+ onSelect: (item) => {
1729
+ if (item.value === "other") {
1730
+ this.#freeformUserInput = {
1731
+ existingAnswers: [...active.selected],
1732
+ questionId: question.id,
1733
+ requestId: active.request.requestId,
1734
+ };
1735
+ this.#closeSelectionPanel();
1736
+ this.#editor.setText("");
1737
+ this.#requestRender();
1738
+ return;
1739
+ }
1740
+ if (item.value === "done") {
1741
+ this.#commitUserInputAnswer([...active.selected]);
1742
+ return;
1743
+ }
1744
+ const optionIndex = Number.parseInt(item.value.slice("option:".length), 10);
1745
+ const option = question.options[optionIndex];
1746
+ if (option === undefined)
1747
+ return;
1748
+ if (!question.multiSelect) {
1749
+ this.#commitUserInputAnswer([option.label]);
1750
+ return;
1751
+ }
1752
+ if (active.selected.has(option.label))
1753
+ active.selected.delete(option.label);
1754
+ else
1755
+ active.selected.add(option.label);
1756
+ this.#openUserInputQuestion();
1757
+ this.#requestRender();
1758
+ },
1759
+ onCancel: () => {
1760
+ this.#closeSelectionPanel();
1761
+ this.#handleEscape();
1762
+ },
1763
+ }));
1764
+ this.#requestRender();
1765
+ }
1766
+ #commitUserInputAnswer(answers) {
1767
+ const active = this.#activeUserInput;
1768
+ const question = active?.request.questions[active.questionIndex];
1769
+ if (active === undefined || question === undefined || answers.length === 0)
1770
+ return;
1771
+ active.answers[question.id] = [...answers];
1772
+ active.questionIndex += 1;
1773
+ active.selected = new Set();
1774
+ if (active.questionIndex < active.request.questions.length) {
1775
+ this.#openUserInputQuestion();
1776
+ return;
1777
+ }
1778
+ this.#sendUserInputResponse(active.request, { answers: active.answers });
1779
+ }
1780
+ #submitFreeformUserInput(value) {
1781
+ const answer = value.trim();
1782
+ const freeform = this.#freeformUserInput;
1783
+ const active = this.#activeUserInput;
1784
+ if (answer.length === 0 || freeform === undefined || active === undefined)
1785
+ return;
1786
+ if (active.request.requestId !== freeform.requestId)
1787
+ return;
1788
+ if (active.request.questions[active.questionIndex]?.id !== freeform.questionId)
1789
+ return;
1790
+ this.#freeformUserInput = undefined;
1791
+ this.#editor.setText("");
1792
+ this.#commitUserInputAnswer([...freeform.existingAnswers, answer]);
1793
+ this.#requestRender();
1794
+ }
1795
+ #sendUserInputResponse(request, response) {
1796
+ this.#activeUserInput = undefined;
1797
+ this.#closeSelectionPanel();
1798
+ if (this.#respondUserInput === undefined) {
1799
+ this.#appendEntry({
1800
+ role: "error",
1801
+ text: "This client cannot send interactive answers.",
1802
+ });
1803
+ this.#removeUserInputRequest(request.requestId);
1804
+ this.#handleEscape();
1805
+ return;
1806
+ }
1807
+ this.#answeringUserInputRequestId = request.requestId;
1808
+ void Promise.resolve(this.#respondUserInput(request.requestId, response))
1809
+ .then(() => this.#removeUserInputRequest(request.requestId))
1810
+ .catch((error) => {
1811
+ this.#answeringUserInputRequestId = undefined;
1812
+ this.#appendEntry({
1813
+ role: "error",
1814
+ text: `The answer could not be sent: ${this.#formatError(error)}`,
1815
+ });
1816
+ this.#openNextUserInputRequest();
1817
+ this.#requestRender();
1818
+ });
1819
+ }
1820
+ #removeUserInputRequest(requestId) {
1821
+ const wasActive = this.#activeUserInput?.request.requestId === requestId;
1822
+ const wasFreeform = this.#freeformUserInput?.requestId === requestId;
1823
+ this.#userInputRequests = this.#userInputRequests.filter((request) => request.requestId !== requestId);
1824
+ if (wasActive)
1825
+ this.#activeUserInput = undefined;
1826
+ if (wasFreeform) {
1827
+ this.#freeformUserInput = undefined;
1828
+ this.#editor.setText("");
1829
+ }
1830
+ if (this.#answeringUserInputRequestId === requestId) {
1831
+ this.#answeringUserInputRequestId = undefined;
1832
+ }
1833
+ if (wasActive || wasFreeform)
1834
+ this.#closeSelectionPanel();
1835
+ this.#openNextUserInputRequest();
1836
+ this.#requestRender();
1837
+ }
1838
+ #clearUserInputRequests() {
1839
+ const hadVisibleRequest = this.#activeUserInput !== undefined || this.#freeformUserInput !== undefined;
1840
+ this.#userInputRequests = [];
1841
+ this.#activeUserInput = undefined;
1842
+ this.#answeringUserInputRequestId = undefined;
1843
+ if (this.#freeformUserInput !== undefined)
1844
+ this.#editor.setText("");
1845
+ this.#freeformUserInput = undefined;
1846
+ if (hadVisibleRequest)
1847
+ this.#closeSelectionPanel();
1848
+ }
1298
1849
  #showSelectionPanel(component) {
1299
1850
  this.#selectionPanel = component;
1300
1851
  }
@@ -1316,6 +1867,9 @@ export class CodingAssistantApp {
1316
1867
  const editorWidth = Math.max(1, width - promptWidth);
1317
1868
  const contentLines = this.#stripSpuriousLeadingEmptyLine(this.#stripEditorChrome(this.#editor.render(editorWidth)));
1318
1869
  return contentLines.map((line, index) => {
1870
+ if (this.#isEditorScrollIndicator(line)) {
1871
+ return this.#inputSurfaceLine(`${INPUT_LINE_INDENT}${DIM}${line}${RESET}`, width);
1872
+ }
1319
1873
  const prefix = index === 0 ? this.#inputPrompt() : INPUT_LINE_INDENT;
1320
1874
  const rendered = `${prefix}${this.#styleImagePlaceholders(line)}`;
1321
1875
  return this.#inputSurfaceLine(this.#cursorVisible ? rendered : this.#hideCursor(rendered), width);
@@ -1326,15 +1880,15 @@ export class CodingAssistantApp {
1326
1880
  const detail = this.#formatToolResult(block);
1327
1881
  if (existing !== undefined) {
1328
1882
  existing.role = block.isError ? "error" : "tool";
1329
- existing.title = block.toolName;
1883
+ existing.title = this.#toolDisplayName(block.toolName);
1330
1884
  existing.detail = detail;
1331
1885
  return;
1332
1886
  }
1333
1887
  this.#appendEntry({
1334
1888
  id: block.toolCallId,
1335
1889
  role: block.isError ? "error" : "tool",
1336
- title: block.toolName,
1337
- text: block.toolName,
1890
+ title: this.#toolDisplayName(block.toolName),
1891
+ text: this.#toolDisplayName(block.toolName),
1338
1892
  detail,
1339
1893
  });
1340
1894
  }
@@ -1345,6 +1899,14 @@ export class CodingAssistantApp {
1345
1899
  return typeof value === "string" && value.length > 0 ? value : undefined;
1346
1900
  };
1347
1901
  const normalized = toolName.toLowerCase();
1902
+ if (normalized === "request_user_input" || normalized === "askuserquestion") {
1903
+ const questions = record.questions;
1904
+ const firstQuestion = Array.isArray(questions) ? questions[0] : undefined;
1905
+ if (this.#isRecord(firstQuestion) && typeof firstQuestion.question === "string") {
1906
+ return this.#singleLine(firstQuestion.question);
1907
+ }
1908
+ return "Waiting for your answer";
1909
+ }
1348
1910
  const command = stringField("cmd") ?? stringField("command");
1349
1911
  if (command !== undefined) {
1350
1912
  return this.#singleLine(command);
@@ -1367,7 +1929,43 @@ export class CodingAssistantApp {
1367
1929
  ? `${todos.length} todo${todos.length === 1 ? "" : "s"}`
1368
1930
  : "todos";
1369
1931
  }
1370
- return toolName;
1932
+ if (normalized === "taskcreate") {
1933
+ return stringField("subject") ?? "Create task";
1934
+ }
1935
+ if (normalized === "taskget") {
1936
+ const taskId = stringField("taskId");
1937
+ return taskId === undefined ? "Read task" : `Read task ${taskId}`;
1938
+ }
1939
+ if (normalized === "taskupdate") {
1940
+ const taskId = stringField("taskId");
1941
+ return taskId === undefined ? "Update task" : `Update task ${taskId}`;
1942
+ }
1943
+ if (normalized === "tasklist")
1944
+ return "Current tasks";
1945
+ if (normalized === "agent") {
1946
+ return stringField("description") ?? "Delegated work";
1947
+ }
1948
+ if (normalized === "spawn_agent") {
1949
+ const taskName = stringField("task_name");
1950
+ return taskName === undefined
1951
+ ? "Start delegated work"
1952
+ : taskName
1953
+ .replaceAll("_", " ")
1954
+ .replace(/^./u, (character) => character.toUpperCase());
1955
+ }
1956
+ if (normalized === "followup_task" || normalized === "sendmessage") {
1957
+ return stringField("summary") ?? "Send follow-up work";
1958
+ }
1959
+ if (normalized === "wait_agent")
1960
+ return "Wait for delegated work";
1961
+ if (normalized === "list_agents")
1962
+ return "Show delegated work";
1963
+ if (normalized === "interrupt_agent")
1964
+ return "Stop delegated work";
1965
+ return this.#toolDisplayName(toolName);
1966
+ }
1967
+ #toolDisplayName(toolName) {
1968
+ return humanizeToolName(toolName);
1371
1969
  }
1372
1970
  #formatToolResult(block) {
1373
1971
  return this.#singleLine(block.display.length > 0 ? block.display : "(empty result)");
@@ -1454,9 +2052,7 @@ export class CodingAssistantApp {
1454
2052
  if (!isError &&
1455
2053
  entry.title === PENDING_TOOL_CALL_TITLE &&
1456
2054
  entry.text === PENDING_TOOL_CALL_TITLE) {
1457
- return [
1458
- this.#fitLine(`${DIM}•${RESET} ${renderActivityWave(PENDING_TOOL_CALL_TITLE, this.#activityAnimationFrame)}`, width),
1459
- ];
2055
+ return [this.#fitLine(`${DIM}• ${PENDING_TOOL_CALL_TITLE}${RESET}`, width)];
1460
2056
  }
1461
2057
  const toolName = entry.title ?? "tool";
1462
2058
  const verb = isError ? "Failed" : this.#toolVerb(toolName);
@@ -1481,12 +2077,13 @@ export class CodingAssistantApp {
1481
2077
  return wrapped.map((line, index) => this.#fitLine(`${index === 0 ? prefix : indent}${line}`, width));
1482
2078
  }
1483
2079
  #emptyInputLine() {
2080
+ const placeholder = this.#freeformUserInput === undefined ? INPUT_PLACEHOLDER : "Type another answer";
1484
2081
  const marker = this.#focused ? CURSOR_MARKER : "";
1485
2082
  if (!this.#focused || !this.#cursorVisible) {
1486
- return `${this.#inputPrompt()}${marker}${SURFACE_MUTED_FG}${INPUT_PLACEHOLDER}${INPUT_FG}`;
2083
+ return `${this.#inputPrompt()}${marker}${SURFACE_MUTED_FG}${placeholder}${INPUT_FG}`;
1487
2084
  }
1488
- const firstCharacter = INPUT_PLACEHOLDER[0] ?? " ";
1489
- const rest = INPUT_PLACEHOLDER.slice(firstCharacter.length);
2085
+ const firstCharacter = placeholder[0] ?? " ";
2086
+ const rest = placeholder.slice(firstCharacter.length);
1490
2087
  return `${this.#inputPrompt()}${marker}${CURSOR_BG}${CURSOR_FG}${firstCharacter}${RESET}${SURFACE_MUTED_FG}${rest}${INPUT_FG}`;
1491
2088
  }
1492
2089
  #surfaceLine(line, width) {
@@ -1532,21 +2129,14 @@ export class CodingAssistantApp {
1532
2129
  return this.#statusText;
1533
2130
  }
1534
2131
  #shouldRenderActivityAsLastMessage() {
1535
- if (this.#streamEntryId !== undefined) {
1536
- return false;
1537
- }
1538
- const lastEntry = this.#entries.at(-1);
1539
- return (lastEntry === undefined ||
1540
- lastEntry.role === "separator" ||
1541
- lastEntry.role === "user" ||
1542
- lastEntry.role === "system" ||
1543
- (lastEntry.role === "thinking" && !this.#showReasoning));
2132
+ return this.#running || this.#compacting;
1544
2133
  }
1545
2134
  #renderActivityLine(label, width) {
1546
2135
  const prefix = `${RIG_ORANGE}•${RESET} `;
1547
2136
  const frame = this.#activityAnimationFrame;
1548
2137
  const elapsed = this.#activityElapsedText();
1549
- const elapsedSuffix = elapsed === undefined ? "" : ` ${DIM}${SURFACE_MUTED_FG}(${elapsed})${RESET}`;
2138
+ const elapsedText = elapsed ?? "0s";
2139
+ const elapsedSuffix = ` ${DIM}${SURFACE_MUTED_FG}(${elapsedText} • Esc to interrupt)${RESET}`;
1550
2140
  return [
1551
2141
  this.#fitLine(`${prefix}${renderActivityWave(label, frame)}${elapsedSuffix}`, width),
1552
2142
  ];
@@ -1819,7 +2409,7 @@ export class CodingAssistantApp {
1819
2409
  return typeof value === "object" && value !== null && !Array.isArray(value);
1820
2410
  }
1821
2411
  #singleLine(text) {
1822
- return text.replace(/\s+/gu, " ").trim();
2412
+ return sanitizeTerminalText(text).replace(/\s+/gu, " ").trim();
1823
2413
  }
1824
2414
  #markTypingActivity() {
1825
2415
  this.#cursorTyping = true;
@@ -1841,7 +2431,11 @@ export class CodingAssistantApp {
1841
2431
  while (content.length > 0 && this.#isEditorBorderLine(content[content.length - 1] ?? "")) {
1842
2432
  content = content.slice(0, -1);
1843
2433
  }
1844
- return content.filter((line) => !line.includes(" more ") && !this.#isEditorBorderLine(line));
2434
+ return content.filter((line) => !this.#isEditorBorderLine(line));
2435
+ }
2436
+ #isEditorScrollIndicator(line) {
2437
+ const stripped = line.replace(/\x1b\[[0-9;]*m/g, "");
2438
+ return stripped.includes(" more ") && (stripped.includes("↑") || stripped.includes("↓"));
1845
2439
  }
1846
2440
  #isEditorBorderLine(line) {
1847
2441
  const stripped = line.replace(/\x1b\[[0-9;]*m/g, "");
@@ -1901,7 +2495,7 @@ export class CodingAssistantApp {
1901
2495
  }
1902
2496
  this.#activityAnimationFrame =
1903
2497
  (this.#activityAnimationFrame + 1) % ACTIVITY_WAVE_FRAME_COUNT;
1904
- if (this.#activityText() !== undefined && this.#shouldRenderActivityAsLastMessage()) {
2498
+ if (this.#activityText() !== undefined) {
1905
2499
  this.#requestRender();
1906
2500
  }
1907
2501
  }, ACTIVITY_ANIMATION_MS);
@@ -1928,4 +2522,43 @@ export class CodingAssistantApp {
1928
2522
  setTimeout(resolve, 25);
1929
2523
  });
1930
2524
  }
2525
+ #handleCtrlC() {
2526
+ if (this.#editor.getText().length > 0) {
2527
+ this.#editor.setText("");
2528
+ this.#fileMentionAutocomplete?.clear();
2529
+ this.#dismissedSlashCommandText = undefined;
2530
+ this.#pastedImagesById.clear();
2531
+ this.#requestRender();
2532
+ return;
2533
+ }
2534
+ if (this.#abortActiveRun())
2535
+ return;
2536
+ void this.stop();
2537
+ }
2538
+ #queueCurrentInput() {
2539
+ const submission = this.#createPromptSubmission(this.#editor.getText());
2540
+ if (submission === undefined)
2541
+ return false;
2542
+ this.#editor.setText("");
2543
+ this.#fileMentionAutocomplete?.clear();
2544
+ this.#modelLocked = true;
2545
+ this.#pendingPrompts.push(submission);
2546
+ this.#requestRender();
2547
+ return true;
2548
+ }
2549
+ #setTerminalFocused(focused) {
2550
+ if (this.#terminalFocused === focused)
2551
+ return;
2552
+ this.#terminalFocused = focused;
2553
+ this.#editor.focused = this.#focused && focused;
2554
+ if (focused && this.#focused) {
2555
+ this.#cursorVisible = true;
2556
+ this.#startCursorBlink();
2557
+ }
2558
+ else {
2559
+ this.#stopCursorBlink();
2560
+ this.#cursorVisible = false;
2561
+ }
2562
+ this.#requestRender();
2563
+ }
1931
2564
  }