actoviq-agent-sdk 0.4.11 → 0.4.13

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/CHANGELOG.md +42 -0
  2. package/dist/src/computer/hadamardComputerUse.d.ts +15 -0
  3. package/dist/src/computer/hadamardComputerUse.d.ts.map +1 -1
  4. package/dist/src/computer/hadamardComputerUse.js +120 -4
  5. package/dist/src/computer/hadamardComputerUse.js.map +1 -1
  6. package/dist/src/config/agentDefinitionMigration.d.ts +125 -0
  7. package/dist/src/config/agentDefinitionMigration.d.ts.map +1 -0
  8. package/dist/src/config/agentDefinitionMigration.js +540 -0
  9. package/dist/src/config/agentDefinitionMigration.js.map +1 -0
  10. package/dist/src/config/agentProfiles.d.ts +23 -0
  11. package/dist/src/config/agentProfiles.d.ts.map +1 -1
  12. package/dist/src/config/agentProfiles.js +69 -13
  13. package/dist/src/config/agentProfiles.js.map +1 -1
  14. package/dist/src/config/hadamardHome.js +0 -1
  15. package/dist/src/config/hadamardHome.js.map +1 -1
  16. package/dist/src/config/modelConfigurationCatalog.d.ts +25 -0
  17. package/dist/src/config/modelConfigurationCatalog.d.ts.map +1 -0
  18. package/dist/src/config/modelConfigurationCatalog.js +50 -0
  19. package/dist/src/config/modelConfigurationCatalog.js.map +1 -0
  20. package/dist/src/config/projectSettings.d.ts +62 -0
  21. package/dist/src/config/projectSettings.d.ts.map +1 -0
  22. package/dist/src/config/projectSettings.js +242 -0
  23. package/dist/src/config/projectSettings.js.map +1 -0
  24. package/dist/src/config/resolveRuntimeConfig.d.ts.map +1 -1
  25. package/dist/src/config/resolveRuntimeConfig.js +97 -4
  26. package/dist/src/config/resolveRuntimeConfig.js.map +1 -1
  27. package/dist/src/goal/goalCommandService.d.ts +11 -0
  28. package/dist/src/goal/goalCommandService.d.ts.map +1 -0
  29. package/dist/src/goal/goalCommandService.js +138 -0
  30. package/dist/src/goal/goalCommandService.js.map +1 -0
  31. package/dist/src/goal/goalContinuation.d.ts +50 -0
  32. package/dist/src/goal/goalContinuation.d.ts.map +1 -0
  33. package/dist/src/goal/goalContinuation.js +131 -0
  34. package/dist/src/goal/goalContinuation.js.map +1 -0
  35. package/dist/src/goal/goalController.d.ts +42 -0
  36. package/dist/src/goal/goalController.d.ts.map +1 -0
  37. package/dist/src/goal/goalController.js +281 -0
  38. package/dist/src/goal/goalController.js.map +1 -0
  39. package/dist/src/goal/goalPrompt.d.ts +3 -0
  40. package/dist/src/goal/goalPrompt.d.ts.map +1 -1
  41. package/dist/src/goal/goalPrompt.js +28 -2
  42. package/dist/src/goal/goalPrompt.js.map +1 -1
  43. package/dist/src/goal/goalService.d.ts +68 -2
  44. package/dist/src/goal/goalService.d.ts.map +1 -1
  45. package/dist/src/goal/goalService.js +500 -8
  46. package/dist/src/goal/goalService.js.map +1 -1
  47. package/dist/src/goal/goalStore.d.ts.map +1 -1
  48. package/dist/src/goal/goalStore.js +172 -5
  49. package/dist/src/goal/goalStore.js.map +1 -1
  50. package/dist/src/goal/goalTools.d.ts +2 -1
  51. package/dist/src/goal/goalTools.d.ts.map +1 -1
  52. package/dist/src/goal/goalTools.js +70 -5
  53. package/dist/src/goal/goalTools.js.map +1 -1
  54. package/dist/src/goal/goalWorkCoordinator.d.ts +43 -0
  55. package/dist/src/goal/goalWorkCoordinator.d.ts.map +1 -0
  56. package/dist/src/goal/goalWorkCoordinator.js +72 -0
  57. package/dist/src/goal/goalWorkCoordinator.js.map +1 -0
  58. package/dist/src/goal/index.d.ts +10 -4
  59. package/dist/src/goal/index.d.ts.map +1 -1
  60. package/dist/src/goal/index.js +7 -1
  61. package/dist/src/goal/index.js.map +1 -1
  62. package/dist/src/goal/projectGoalApi.d.ts +82 -0
  63. package/dist/src/goal/projectGoalApi.d.ts.map +1 -0
  64. package/dist/src/goal/projectGoalApi.js +287 -0
  65. package/dist/src/goal/projectGoalApi.js.map +1 -0
  66. package/dist/src/goal/projectGoalStore.d.ts +131 -0
  67. package/dist/src/goal/projectGoalStore.d.ts.map +1 -0
  68. package/dist/src/goal/projectGoalStore.js +808 -0
  69. package/dist/src/goal/projectGoalStore.js.map +1 -0
  70. package/dist/src/goal/types.d.ts +127 -3
  71. package/dist/src/goal/types.d.ts.map +1 -1
  72. package/dist/src/goal/types.js +2 -2
  73. package/dist/src/goal/types.js.map +1 -1
  74. package/dist/src/gui/agentTargetPicker.d.ts +32 -0
  75. package/dist/src/gui/agentTargetPicker.d.ts.map +1 -0
  76. package/dist/src/gui/agentTargetPicker.js +99 -0
  77. package/dist/src/gui/agentTargetPicker.js.map +1 -0
  78. package/dist/src/gui/electronMain.js +10 -3
  79. package/dist/src/gui/electronMain.js.map +1 -1
  80. package/dist/src/gui/hadamardGui.d.ts +24 -0
  81. package/dist/src/gui/hadamardGui.d.ts.map +1 -1
  82. package/dist/src/gui/hadamardGui.js +6321 -1327
  83. package/dist/src/gui/hadamardGui.js.map +1 -1
  84. package/dist/src/gui/projectWorkbench.d.ts +13 -2
  85. package/dist/src/gui/projectWorkbench.d.ts.map +1 -1
  86. package/dist/src/gui/projectWorkbench.js +109 -67
  87. package/dist/src/gui/projectWorkbench.js.map +1 -1
  88. package/dist/src/gui/promptTemplates.d.ts +18 -0
  89. package/dist/src/gui/promptTemplates.d.ts.map +1 -0
  90. package/dist/src/gui/promptTemplates.js +178 -0
  91. package/dist/src/gui/promptTemplates.js.map +1 -0
  92. package/dist/src/gui/terminalManager.d.ts +14 -0
  93. package/dist/src/gui/terminalManager.d.ts.map +1 -1
  94. package/dist/src/gui/terminalManager.js +25 -5
  95. package/dist/src/gui/terminalManager.js.map +1 -1
  96. package/dist/src/gui/transcript/clientBundle.d.ts +6 -1
  97. package/dist/src/gui/transcript/clientBundle.d.ts.map +1 -1
  98. package/dist/src/gui/transcript/clientBundle.js +6 -1
  99. package/dist/src/gui/transcript/clientBundle.js.map +1 -1
  100. package/dist/src/help/productCapabilities.d.ts +17 -0
  101. package/dist/src/help/productCapabilities.d.ts.map +1 -0
  102. package/dist/src/help/productCapabilities.js +146 -0
  103. package/dist/src/help/productCapabilities.js.map +1 -0
  104. package/dist/src/index.d.ts +21 -6
  105. package/dist/src/index.d.ts.map +1 -1
  106. package/dist/src/index.js +15 -5
  107. package/dist/src/index.js.map +1 -1
  108. package/dist/src/issues/issueExecution.d.ts.map +1 -1
  109. package/dist/src/issues/issueExecution.js +29 -9
  110. package/dist/src/issues/issueExecution.js.map +1 -1
  111. package/dist/src/manager/assistantGlobalTools.d.ts +44 -0
  112. package/dist/src/manager/assistantGlobalTools.d.ts.map +1 -1
  113. package/dist/src/manager/assistantGlobalTools.js +565 -34
  114. package/dist/src/manager/assistantGlobalTools.js.map +1 -1
  115. package/dist/src/manager/assistantProposals.d.ts +87 -0
  116. package/dist/src/manager/assistantProposals.d.ts.map +1 -0
  117. package/dist/src/manager/assistantProposals.js +207 -0
  118. package/dist/src/manager/assistantProposals.js.map +1 -0
  119. package/dist/src/manager/referenceIndex.d.ts +82 -0
  120. package/dist/src/manager/referenceIndex.d.ts.map +1 -0
  121. package/dist/src/manager/referenceIndex.js +167 -0
  122. package/dist/src/manager/referenceIndex.js.map +1 -0
  123. package/dist/src/manager/referenceOperations.d.ts +108 -0
  124. package/dist/src/manager/referenceOperations.d.ts.map +1 -0
  125. package/dist/src/manager/referenceOperations.js +821 -0
  126. package/dist/src/manager/referenceOperations.js.map +1 -0
  127. package/dist/src/manager/resolveTargetRef.d.ts +67 -0
  128. package/dist/src/manager/resolveTargetRef.d.ts.map +1 -0
  129. package/dist/src/manager/resolveTargetRef.js +81 -0
  130. package/dist/src/manager/resolveTargetRef.js.map +1 -0
  131. package/dist/src/memory/durableMemoryPipeline.d.ts +51 -0
  132. package/dist/src/memory/durableMemoryPipeline.d.ts.map +1 -0
  133. package/dist/src/memory/durableMemoryPipeline.js +266 -0
  134. package/dist/src/memory/durableMemoryPipeline.js.map +1 -0
  135. package/dist/src/memory/durableMemoryStore.d.ts +59 -0
  136. package/dist/src/memory/durableMemoryStore.d.ts.map +1 -0
  137. package/dist/src/memory/durableMemoryStore.js +194 -0
  138. package/dist/src/memory/durableMemoryStore.js.map +1 -0
  139. package/dist/src/memory/hadamardDream.d.ts +18 -0
  140. package/dist/src/memory/hadamardDream.d.ts.map +1 -1
  141. package/dist/src/memory/hadamardDream.js +119 -39
  142. package/dist/src/memory/hadamardDream.js.map +1 -1
  143. package/dist/src/memory/hadamardMemory.d.ts +20 -39
  144. package/dist/src/memory/hadamardMemory.d.ts.map +1 -1
  145. package/dist/src/memory/hadamardMemory.js +112 -450
  146. package/dist/src/memory/hadamardMemory.js.map +1 -1
  147. package/dist/src/memory/hadamardSessionMemoryState.d.ts +2 -5
  148. package/dist/src/memory/hadamardSessionMemoryState.d.ts.map +1 -1
  149. package/dist/src/memory/hadamardSessionMemoryState.js +5 -61
  150. package/dist/src/memory/hadamardSessionMemoryState.js.map +1 -1
  151. package/dist/src/memory/memoryCommandService.d.ts +25 -0
  152. package/dist/src/memory/memoryCommandService.d.ts.map +1 -0
  153. package/dist/src/memory/memoryCommandService.js +93 -0
  154. package/dist/src/memory/memoryCommandService.js.map +1 -0
  155. package/dist/src/memory/memoryProposalCommandService.d.ts.map +1 -1
  156. package/dist/src/memory/memoryProposalCommandService.js +2 -1
  157. package/dist/src/memory/memoryProposalCommandService.js.map +1 -1
  158. package/dist/src/memory/memoryProposalTools.d.ts +1 -0
  159. package/dist/src/memory/memoryProposalTools.d.ts.map +1 -1
  160. package/dist/src/memory/memoryProposalTools.js +16 -11
  161. package/dist/src/memory/memoryProposalTools.js.map +1 -1
  162. package/dist/src/parity/bridgeConfigs.d.ts +4 -0
  163. package/dist/src/parity/bridgeConfigs.d.ts.map +1 -1
  164. package/dist/src/parity/bridgeConfigs.js +30 -1
  165. package/dist/src/parity/bridgeConfigs.js.map +1 -1
  166. package/dist/src/router/modelRouter.d.ts +22 -2
  167. package/dist/src/router/modelRouter.d.ts.map +1 -1
  168. package/dist/src/router/modelRouter.js +76 -9
  169. package/dist/src/router/modelRouter.js.map +1 -1
  170. package/dist/src/runtime/agentClient.d.ts +24 -4
  171. package/dist/src/runtime/agentClient.d.ts.map +1 -1
  172. package/dist/src/runtime/agentClient.js +581 -262
  173. package/dist/src/runtime/agentClient.js.map +1 -1
  174. package/dist/src/runtime/agentSession.d.ts +8 -3
  175. package/dist/src/runtime/agentSession.d.ts.map +1 -1
  176. package/dist/src/runtime/agentSession.js +10 -3
  177. package/dist/src/runtime/agentSession.js.map +1 -1
  178. package/dist/src/runtime/agentTemplates.d.ts +28 -0
  179. package/dist/src/runtime/agentTemplates.d.ts.map +1 -0
  180. package/dist/src/runtime/agentTemplates.js +73 -0
  181. package/dist/src/runtime/agentTemplates.js.map +1 -0
  182. package/dist/src/runtime/conversationEngine.d.ts +4 -0
  183. package/dist/src/runtime/conversationEngine.d.ts.map +1 -1
  184. package/dist/src/runtime/conversationEngine.js +52 -3
  185. package/dist/src/runtime/conversationEngine.js.map +1 -1
  186. package/dist/src/runtime/defaultHadamardAgents.d.ts.map +1 -1
  187. package/dist/src/runtime/defaultHadamardAgents.js +9 -47
  188. package/dist/src/runtime/defaultHadamardAgents.js.map +1 -1
  189. package/dist/src/runtime/effectiveAgentRunOptions.d.ts +49 -0
  190. package/dist/src/runtime/effectiveAgentRunOptions.d.ts.map +1 -0
  191. package/dist/src/runtime/effectiveAgentRunOptions.js +50 -0
  192. package/dist/src/runtime/effectiveAgentRunOptions.js.map +1 -0
  193. package/dist/src/runtime/externalAgentRunner.d.ts +25 -0
  194. package/dist/src/runtime/externalAgentRunner.d.ts.map +1 -0
  195. package/dist/src/runtime/externalAgentRunner.js +57 -0
  196. package/dist/src/runtime/externalAgentRunner.js.map +1 -0
  197. package/dist/src/runtime/hadamardAgentDefinitions.d.ts +15 -0
  198. package/dist/src/runtime/hadamardAgentDefinitions.d.ts.map +1 -1
  199. package/dist/src/runtime/hadamardAgentDefinitions.js +42 -2
  200. package/dist/src/runtime/hadamardAgentDefinitions.js.map +1 -1
  201. package/dist/src/runtime/hadamardAgents.d.ts +6 -0
  202. package/dist/src/runtime/hadamardAgents.d.ts.map +1 -1
  203. package/dist/src/runtime/hadamardAgents.js +52 -1
  204. package/dist/src/runtime/hadamardAgents.js.map +1 -1
  205. package/dist/src/runtime/hadamardCompact.d.ts +12 -1
  206. package/dist/src/runtime/hadamardCompact.d.ts.map +1 -1
  207. package/dist/src/runtime/hadamardCompact.js +91 -35
  208. package/dist/src/runtime/hadamardCompact.js.map +1 -1
  209. package/dist/src/runtime/hadamardPermissions.d.ts +1 -0
  210. package/dist/src/runtime/hadamardPermissions.d.ts.map +1 -1
  211. package/dist/src/runtime/hadamardPermissions.js +6 -0
  212. package/dist/src/runtime/hadamardPermissions.js.map +1 -1
  213. package/dist/src/runtime/hadamardSlashCommands.d.ts +7 -1
  214. package/dist/src/runtime/hadamardSlashCommands.d.ts.map +1 -1
  215. package/dist/src/runtime/hadamardSlashCommands.js +47 -5
  216. package/dist/src/runtime/hadamardSlashCommands.js.map +1 -1
  217. package/dist/src/runtime/tools.d.ts.map +1 -1
  218. package/dist/src/runtime/tools.js +2 -0
  219. package/dist/src/runtime/tools.js.map +1 -1
  220. package/dist/src/team/executorTarget.d.ts +26 -0
  221. package/dist/src/team/executorTarget.d.ts.map +1 -0
  222. package/dist/src/team/executorTarget.js +15 -0
  223. package/dist/src/team/executorTarget.js.map +1 -0
  224. package/dist/src/team/modelTeam.d.ts +5 -1
  225. package/dist/src/team/modelTeam.d.ts.map +1 -1
  226. package/dist/src/team/modelTeam.js +157 -41
  227. package/dist/src/team/modelTeam.js.map +1 -1
  228. package/dist/src/team/teamComposition.d.ts +20 -0
  229. package/dist/src/team/teamComposition.d.ts.map +1 -0
  230. package/dist/src/team/teamComposition.js +78 -0
  231. package/dist/src/team/teamComposition.js.map +1 -0
  232. package/dist/src/team/teamDefinitions.d.ts +10 -1
  233. package/dist/src/team/teamDefinitions.d.ts.map +1 -1
  234. package/dist/src/team/teamDefinitions.js +123 -16
  235. package/dist/src/team/teamDefinitions.js.map +1 -1
  236. package/dist/src/team/teamGraph.d.ts +1 -1
  237. package/dist/src/team/teamGraph.d.ts.map +1 -1
  238. package/dist/src/team/teamGraph.js +1 -1
  239. package/dist/src/team/teamGraph.js.map +1 -1
  240. package/dist/src/team/teamPrompts.d.ts.map +1 -1
  241. package/dist/src/team/teamPrompts.js +0 -4
  242. package/dist/src/team/teamPrompts.js.map +1 -1
  243. package/dist/src/team/teamProposalService.d.ts +9 -1
  244. package/dist/src/team/teamProposalService.d.ts.map +1 -1
  245. package/dist/src/team/teamProposalService.js +66 -6
  246. package/dist/src/team/teamProposalService.js.map +1 -1
  247. package/dist/src/team/teamPropose.d.ts +3 -1
  248. package/dist/src/team/teamPropose.d.ts.map +1 -1
  249. package/dist/src/team/teamPropose.js +16 -10
  250. package/dist/src/team/teamPropose.js.map +1 -1
  251. package/dist/src/team/teamRuntime.d.ts +5 -0
  252. package/dist/src/team/teamRuntime.d.ts.map +1 -1
  253. package/dist/src/team/teamRuntime.js +23 -5
  254. package/dist/src/team/teamRuntime.js.map +1 -1
  255. package/dist/src/team/workflowSquad.d.ts +10 -2
  256. package/dist/src/team/workflowSquad.d.ts.map +1 -1
  257. package/dist/src/team/workflowSquad.js +250 -12
  258. package/dist/src/team/workflowSquad.js.map +1 -1
  259. package/dist/src/tools/enterWorktree.d.ts.map +1 -1
  260. package/dist/src/tools/enterWorktree.js +3 -0
  261. package/dist/src/tools/enterWorktree.js.map +1 -1
  262. package/dist/src/tools/exaSearch.d.ts +2 -2
  263. package/dist/src/tools/exitWorktree.d.ts.map +1 -1
  264. package/dist/src/tools/exitWorktree.js +3 -1
  265. package/dist/src/tools/exitWorktree.js.map +1 -1
  266. package/dist/src/tools/hadamardCoreTools.d.ts +5 -14
  267. package/dist/src/tools/hadamardCoreTools.d.ts.map +1 -1
  268. package/dist/src/tools/hadamardCoreTools.js +31 -0
  269. package/dist/src/tools/hadamardCoreTools.js.map +1 -1
  270. package/dist/src/tools/hadamardFileTools.d.ts.map +1 -1
  271. package/dist/src/tools/hadamardFileTools.js +116 -38
  272. package/dist/src/tools/hadamardFileTools.js.map +1 -1
  273. package/dist/src/tools/planMode/PlanModeTools.d.ts +22 -0
  274. package/dist/src/tools/planMode/PlanModeTools.d.ts.map +1 -1
  275. package/dist/src/tools/planMode/PlanModeTools.js +102 -5
  276. package/dist/src/tools/planMode/PlanModeTools.js.map +1 -1
  277. package/dist/src/tools/todo/TodoWriteTool.d.ts +1 -1
  278. package/dist/src/tui/hadamardTui.d.ts.map +1 -1
  279. package/dist/src/tui/hadamardTui.js +387 -168
  280. package/dist/src/tui/hadamardTui.js.map +1 -1
  281. package/dist/src/tui/pendingInput.d.ts +17 -0
  282. package/dist/src/tui/pendingInput.d.ts.map +1 -0
  283. package/dist/src/tui/pendingInput.js +23 -0
  284. package/dist/src/tui/pendingInput.js.map +1 -0
  285. package/dist/src/tui/reasoningDisplay.d.ts +13 -0
  286. package/dist/src/tui/reasoningDisplay.d.ts.map +1 -0
  287. package/dist/src/tui/reasoningDisplay.js +45 -0
  288. package/dist/src/tui/reasoningDisplay.js.map +1 -0
  289. package/dist/src/tui/screen.d.ts +11 -1
  290. package/dist/src/tui/screen.d.ts.map +1 -1
  291. package/dist/src/tui/screen.js +64 -34
  292. package/dist/src/tui/screen.js.map +1 -1
  293. package/dist/src/types.d.ts +210 -77
  294. package/dist/src/types.d.ts.map +1 -1
  295. package/dist/src/types.js.map +1 -1
  296. package/dist/src/ui/commandSurface.d.ts +8 -0
  297. package/dist/src/ui/commandSurface.d.ts.map +1 -1
  298. package/dist/src/ui/commandSurface.js +78 -13
  299. package/dist/src/ui/commandSurface.js.map +1 -1
  300. package/dist/src/update/appUpdateService.d.ts +34 -1
  301. package/dist/src/update/appUpdateService.d.ts.map +1 -1
  302. package/dist/src/update/appUpdateService.js +114 -9
  303. package/dist/src/update/appUpdateService.js.map +1 -1
  304. package/package.json +3 -2
  305. package/dist/src/prompts/outputStyles.d.ts +0 -20
  306. package/dist/src/prompts/outputStyles.d.ts.map +0 -1
  307. package/dist/src/prompts/outputStyles.js +0 -35
  308. package/dist/src/prompts/outputStyles.js.map +0 -1
@@ -10,7 +10,7 @@ import { createHash, randomUUID } from 'node:crypto';
10
10
  import fs from 'node:fs';
11
11
  import path from 'node:path';
12
12
  import * as readline from 'node:readline';
13
- import { createHadamardCoreTools, createAgentSdk, createHadamardBridgeSdk, detectBridgeProviders, loadDefaultHadamardSettings, loadJsonConfigFile, listWorkflows, loadWorkflow, listTeamDefinitions, loadTeamDefinition, cloneTeamDefinition, instantiateTeamDefinition, listTeamAgentLabels, countTeamAgents, createModelTeam, createTeamTool, readTeamPreferences, createManagerTools, createAssistantTeamTools, buildAssistantTeamSystemPrompt, TeamProposalStore, SessionCatalog, createAssistantGlobalTools, buildAssistantGlobalSystemPrompt, readAssistantConfig, writeAssistantConfig, buildManagerSystemPrompt, buildUpdateProgressPrompt, formatManagerUpdatePreview, resolveGitHubDigestForUpdate, readManagerConfig, writeManagerConfig, readProjectPlanFile, readProgressFile, managerProgressPath, createProjectIssue, executeProjectIssue, isIssueStatus, isIssueStorageMode, listProjectIssues, listScheduledAutomationTasks, upsertScheduledAutomationTask, resolveHadamardHome, externalSkillPreferencesToRuntimeOptions, readHadamardExternalSkillPreferences, createManagedPluginRuntime, patchManagedPluginSettings, readManagedPluginCatalog, listRouterProfiles, loadRouterProfile, resolveRoutedRun, transitionProjectIssue, WorktreeService, GoalService, GOAL_METADATA_KEY, normalizeGoal, } from '../index.js';
13
+ import { createHadamardCoreTools, createAgentSdk, createHadamardBridgeSdk, detectBridgeProviders, loadDefaultHadamardSettings, loadJsonConfigFile, listWorkflows, loadWorkflow, saveWorkflow, deleteWorkflow, listTeamDefinitions, loadTeamDefinition, cloneTeamDefinition, deleteTeamDefinition, getBuiltInTeamDefinition, instantiateTeamDefinition, listTeamAgentLabels, countTeamAgents, askTeamDefinition, createTeamTool, readTeamPreferences, createManagerTools, createAssistantTeamTools, buildAssistantTeamSystemPrompt, TeamProposalStore, SessionCatalog, createAssistantGlobalTools, buildAssistantGlobalSystemPrompt, readAssistantConfig, writeAssistantConfig, buildManagerSystemPrompt, buildUpdateProgressPrompt, formatManagerUpdatePreview, resolveGitHubDigestForUpdate, readManagerConfig, writeManagerConfig, readProjectPlanFile, readProgressFile, managerProgressPath, createProjectIssue, executeProjectIssue, isIssueStatus, isIssueStorageMode, listProjectIssues, listScheduledAutomationTasks, upsertScheduledAutomationTask, resolveHadamardHome, externalSkillPreferencesToRuntimeOptions, readHadamardExternalSkillPreferences, createManagedPluginRuntime, patchManagedPluginSettings, readManagedPluginCatalog, listRouterProfiles, loadRouterProfile, resolveRoutedRun, transitionProjectIssue, WorktreeService, } from '../index.js';
14
14
  import { adaptBridgeRun } from '../parity/bridgeEventAdapter.js';
15
15
  import { ExternalCliRuntimeManager } from '../parity/externalCliRuntimeManager.js';
16
16
  import { externalCliSessionMatchesConfig, listExternalCliSessions, readExternalCliSession, } from '../parity/externalCliSessions.js';
@@ -22,25 +22,27 @@ import { createPreToolUseHookClassifier, readPreToolUseHooks, readPostToolUseHoo
22
22
  import { parseTypedHooks } from '../hooks/hookConfig.js';
23
23
  import { isRecord } from '../runtime/helpers.js';
24
24
  import { getLoadedJsonConfig } from '../config/loadJsonConfigFile.js';
25
+ import { buildModelConfigurationCatalog, findModelConfiguration, resolveHadamardConfigurationModel, } from '../config/modelConfigurationCatalog.js';
25
26
  import { findBridgeConfig, maskApiKey, readBridgeConfigs, addBridgeConfig, removeBridgeConfig, isManagedExternalCliRuntime, } from '../parity/bridgeConfigs.js';
26
27
  import { buildRouteModelApi } from '../router/modelRouter.js';
27
28
  import { addMcpServer, readMcpServerConfig, removeMcpServer } from '../mcp/mcpServerConfig.js';
28
29
  import { isReadOnlyBashCommand } from '../runtime/bashClassification.js';
29
30
  import { estimateCost } from '../team/pricing.js';
30
31
  import { applyTeamRunEvent, createTeamRunViewState, formatTeamRunTreeLines } from '../team/teamRunView.js';
31
- import { applyOutputStyle, OUTPUT_STYLES } from '../prompts/outputStyles.js';
32
32
  import { planFilePath, readPlanFile } from '../tools/planMode/PlanModeTools.js';
33
33
  import { loadProjectContext } from '../memory/projectContext.js';
34
34
  import { LegacySurfaceEventPipeline, } from '../surfaces/index.js';
35
35
  import { pathToFileURL } from 'node:url';
36
- import { HADAMARD_INTERACTIVE_COMMANDS, SUBCOMMAND_DESCRIPTIONS, filterInteractiveCommands, interactiveCommandUsage, selectInteractiveCommand, } from '../ui/commandSurface.js';
36
+ import { HADAMARD_INTERACTIVE_COMMANDS, SUBCOMMAND_DESCRIPTIONS, canRunInteractiveCommand, filterInteractiveCommands, interactiveCommandRunPolicy, interactiveCommandUsage, parseTeamAskArguments, selectInteractiveCommand, } from '../ui/commandSurface.js';
37
37
  import { createAgentExecutionProjectView, createAgentExecutionRootView, formatAgentExecutionTreeLines, } from '../ui/agentExecutionView.js';
38
38
  import { A, stringWidth, truncateToWidth, wrapToWidth } from './ansi.js';
39
39
  import { InputEditor } from './editor.js';
40
40
  import { discoverHadamardPlugins } from './pluginCatalog.js';
41
41
  import { TuiScreen } from './screen.js';
42
+ import { ReasoningDisplayState } from './reasoningDisplay.js';
43
+ import { recallLatestFollowUp, restoreAbandonedFollowUp, submitActiveInput, } from './pendingInput.js';
42
44
  import { filterTuiSelectionItems, moveTuiSelection, } from './selection.js';
43
- import { StreamFlusher, formatBanner, formatCompactNotice, formatDivider, formatEditCall, formatErrorLine, formatInfoLine, formatQueuedPrompt, formatThinking, formatToolCall, formatToolResult, formatUserPrompt, } from './transcript.js';
45
+ import { StreamFlusher, formatBanner, formatCompactNotice, formatDivider, formatEditCall, formatErrorLine, formatInfoLine, formatQueuedPrompt, formatToolCall, formatToolResult, formatUserPrompt, } from './transcript.js';
44
46
  const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
45
47
  const CTRL_C_EXIT_WINDOW_MS = 600;
46
48
  const DYNAMIC_FRAME_MS = 33;
@@ -356,6 +358,8 @@ export async function runHadamardTui(options = {}) {
356
358
  let running = false;
357
359
  let commandBusy = false;
358
360
  let shuttingDown = false;
361
+ /** Follow-up pulled out of the queue into the editor; restored if abandoned. */
362
+ let recalledFollowUp = null;
359
363
  // Active team tool the main agent may call (toggled via /team). null = no team.
360
364
  // The tool is only injected into runs when preferences.team.autoInvoke is on;
361
365
  // otherwise attach is a selection and /team ask stays the manual path.
@@ -682,13 +686,10 @@ export async function runHadamardTui(options = {}) {
682
686
  return cost !== null ? `$${cost.toFixed(4)}` : null;
683
687
  }
684
688
  let statusNote = '';
685
- // /output-style prompt prefix swap (gap #19). Applied to the base system
686
- // prompt per turn; 'default' is a no-op.
687
- let outputStyle = 'default';
688
689
  let ctrlCCount = 0;
689
690
  let ctrlCTimer = null;
690
691
  let streamedTextSeen = false;
691
- let streamedThinkingSeen = false;
692
+ const reasoningDisplay = new ReasoningDisplayState();
692
693
  // Track tool names by callId for PostToolUse hook context.
693
694
  const toolCallNames = new Map();
694
695
  // Live todo list (captured from TodoWrite tool calls). Rendered as a
@@ -888,6 +889,16 @@ export async function runHadamardTui(options = {}) {
888
889
  lines.push(promptDivider());
889
890
  return lines;
890
891
  }
892
+ function promptCursorPosition(promptStartLine) {
893
+ if (editor.isEmpty()) {
894
+ return { line: promptStartLine + 1, column: 2 };
895
+ }
896
+ const visual = editor.visualLines(Math.max(screen.width - 5, 7));
897
+ return {
898
+ line: promptStartLine + 1 + visual.cursorRow,
899
+ column: 2 + visual.cursorCol,
900
+ };
901
+ }
891
902
  // ── @-mention file completion ──────────────────────────────────────
892
903
  // Prefer git's view (tracked + untracked, honoring .gitignore) so the list
893
904
  // matches what the agent actually sees; fall back to a bounded fs walk for
@@ -1038,7 +1049,10 @@ export async function runHadamardTui(options = {}) {
1038
1049
  const selected = index === menuSelected;
1039
1050
  const command = `/${name}`.padEnd(commandWidth);
1040
1051
  const label = selected ? `${A.inverse}${command}${A.reset}` : command;
1041
- const description = truncateToWidth(TUI_SLASH_COMMANDS[name] ?? SUBCOMMAND_DESCRIPTIONS[name] ?? '', descriptionWidth);
1052
+ const commandName = name.split(/\s/u, 1)[0] ?? name;
1053
+ const unavailable = running && interactiveCommandRunPolicy(commandName) === 'idle-only';
1054
+ const summary = TUI_SLASH_COMMANDS[name] ?? SUBCOMMAND_DESCRIPTIONS[name] ?? '';
1055
+ const description = truncateToWidth(unavailable ? `${summary} · after current run` : summary, descriptionWidth);
1042
1056
  return `${label} ${A.dim}${description}${A.reset}`;
1043
1057
  });
1044
1058
  const hiddenAbove = windowStart;
@@ -1051,6 +1065,14 @@ export async function runHadamardTui(options = {}) {
1051
1065
  parts.push(`↓${hiddenBelow}`);
1052
1066
  lines.push(`${A.dim} ${parts.join(' ')} more · ${menuSelected + 1}/${matches.length} (↑/↓ to scroll)${A.reset}`);
1053
1067
  }
1068
+ const selected = matches[Math.min(menuSelected, matches.length - 1)];
1069
+ if (selected) {
1070
+ const commandName = selected.split(/\s/u, 1)[0] ?? selected;
1071
+ const availability = interactiveCommandRunPolicy(commandName) === 'during-run'
1072
+ ? 'available while working'
1073
+ : 'idle session';
1074
+ lines.push(`${A.dim} ${interactiveCommandUsage(commandName)} · ${availability}${A.reset}`);
1075
+ }
1054
1076
  return lines;
1055
1077
  }
1056
1078
  function buildDialog() {
@@ -1146,7 +1168,9 @@ export async function runHadamardTui(options = {}) {
1146
1168
  const ctxColor = pct >= 90 ? A.red : pct >= 70 ? A.yellow : A.dim;
1147
1169
  const modelLabel = activeRouter
1148
1170
  ? `router:${activeRouter.name}${routedModelLabel ? ` → ${routedModelLabel}` : ''}`
1149
- : session.model;
1171
+ : !bridgeMode && activeBridgeConfig?.runtime === 'hadamard' && bridgeModelLabel
1172
+ ? `${activeBridgeConfig.name}:${bridgeModelLabel}`
1173
+ : session.model;
1150
1174
  const bridgeTag = bridgeMode && activeBridgeConfig
1151
1175
  ? ` · bridge:${activeBridgeConfig.name}${bridgeModelLabel ? ` · ${bridgeModelLabel}` : ''}`
1152
1176
  : '';
@@ -1171,10 +1195,39 @@ export async function runHadamardTui(options = {}) {
1171
1195
  }
1172
1196
  function buildHintLine() {
1173
1197
  if (running) {
1174
- return [`${A.dim} enter to queue a steering message · esc interrupt · ctrl+c twice to exit${A.reset}`];
1198
+ return [`${A.dim} enter queue follow-up · shift+enter steer now · ↑ recall queued · esc interrupt${A.reset}`];
1175
1199
  }
1176
1200
  return [`${A.dim} ? shortcuts · / commands · @ files · \\↵ newline · ↑↓ history · ctrl+c clear/exit${A.reset}`];
1177
1201
  }
1202
+ function buildPendingInputPanel() {
1203
+ const pending = session.pendingInputs;
1204
+ const visible = pending.followUps.slice(-3);
1205
+ const hidden = pending.followUps.length - visible.length;
1206
+ const lines = visible.map((text, index) => (`${A.dim} queued ${hidden + index + 1}: ${truncateToWidth(text.replace(/\s+/g, ' '), Math.max(screen.width - 15, 12))}${A.reset}`));
1207
+ if (pending.steering.length > 0) {
1208
+ lines.push(`${A.dim} ${pending.steering.length} immediate steering message${pending.steering.length === 1 ? '' : 's'} pending${A.reset}`);
1209
+ }
1210
+ return lines;
1211
+ }
1212
+ function selectionDialogCursorPosition(startLine) {
1213
+ if (!selectionDialog?.searchable)
1214
+ return undefined;
1215
+ return {
1216
+ line: startLine + 2 + (selectionDialog.subtitle ? 1 : 0),
1217
+ column: 4 + stringWidth(selectionDialog.query),
1218
+ };
1219
+ }
1220
+ function textInputDialogCursorPosition(startLine) {
1221
+ if (!textInputDialog)
1222
+ return undefined;
1223
+ const valueBeforeCursor = textInputDialog.secret
1224
+ ? '•'.repeat(textInputDialog.editor.cursor)
1225
+ : textInputDialog.editor.text.slice(0, textInputDialog.editor.cursor);
1226
+ return {
1227
+ line: startLine + 2 + (textInputDialog.description ? 1 : 0),
1228
+ column: 2 + stringWidth(`${textInputDialog.label}: `) + stringWidth(valueBeforeCursor),
1229
+ };
1230
+ }
1178
1231
  function buildTodoPanel() {
1179
1232
  if (currentTodos.length === 0)
1180
1233
  return [];
@@ -1210,7 +1263,14 @@ export async function runHadamardTui(options = {}) {
1210
1263
  }
1211
1264
  function renderDynamic() {
1212
1265
  const lines = [];
1266
+ let promptCursor;
1213
1267
  lines.push(...buildStatusLine());
1268
+ if (running && session.pendingInputCount > 0) {
1269
+ lines.push(...buildPendingInputPanel());
1270
+ }
1271
+ if (reasoningDisplay.hasActive) {
1272
+ lines.push(...reasoningDisplay.liveLines(screen.width));
1273
+ }
1214
1274
  const tail = flusher.tail();
1215
1275
  if (running && tail) {
1216
1276
  lines.push(tail);
@@ -1224,13 +1284,19 @@ export async function runHadamardTui(options = {}) {
1224
1284
  lines.push(...buildDialog());
1225
1285
  }
1226
1286
  else if (selectionDialog) {
1287
+ const dialogStartLine = lines.length;
1227
1288
  lines.push(...buildSelectionDialog());
1289
+ promptCursor = selectionDialogCursorPosition(dialogStartLine);
1228
1290
  }
1229
1291
  else if (textInputDialog) {
1292
+ const dialogStartLine = lines.length;
1230
1293
  lines.push(...buildTextInputDialog());
1294
+ promptCursor = textInputDialogCursorPosition(dialogStartLine);
1231
1295
  }
1232
1296
  else {
1297
+ const promptStartLine = lines.length;
1233
1298
  lines.push(...buildPromptBar());
1299
+ promptCursor = promptCursorPosition(promptStartLine);
1234
1300
  const atMenu = buildAtMenu();
1235
1301
  if (atMenu.length > 0) {
1236
1302
  lines.push(...atMenu);
@@ -1240,7 +1306,7 @@ export async function runHadamardTui(options = {}) {
1240
1306
  lines.push(...(menu.length > 0 ? menu : buildHintLine()));
1241
1307
  }
1242
1308
  }
1243
- screen.setDynamic(lines);
1309
+ screen.setDynamic(lines, promptCursor);
1244
1310
  }
1245
1311
  function scheduleDynamicRender() {
1246
1312
  if (dynamicRenderTimer)
@@ -1259,6 +1325,11 @@ export async function runHadamardTui(options = {}) {
1259
1325
  function appendStatic(lines) {
1260
1326
  screen.appendStatic(lines);
1261
1327
  }
1328
+ function collapseReasoning() {
1329
+ const lines = reasoningDisplay.complete();
1330
+ if (lines.length > 0)
1331
+ appendStatic(lines);
1332
+ }
1262
1333
  // Inline CLI spinner for long async operations (probe, reload).
1263
1334
  // Writes directly to stdout with \r — independent of the redrawable region.
1264
1335
  const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
@@ -1419,7 +1490,7 @@ export async function runHadamardTui(options = {}) {
1419
1490
  if (activeRouter && !bridgeMode) {
1420
1491
  try {
1421
1492
  const decision = await resolveRoutedRun(activeRouter, text, abortCtrl.signal);
1422
- routed = { model: decision.model, modelApi: decision.modelApi };
1493
+ routed = { model: decision.model, modelApi: decision.modelApi, effort: decision.effort };
1423
1494
  routedModelLabel = `${decision.label} (${decision.model})`;
1424
1495
  appendStatic(formatInfoLine(`router → ${routedModelLabel}`));
1425
1496
  }
@@ -1455,7 +1526,7 @@ export async function runHadamardTui(options = {}) {
1455
1526
  // session → context intact; switching bridge↔hadamard is seamless.
1456
1527
  statusNote = `bridge:${activeBridgeConfig?.name ?? 'bridge'}`;
1457
1528
  const stream = session.stream(expandImageRefs(text), {
1458
- systemPrompt: applyOutputStyle(systemPrompt + buildAgentContext(), outputStyle),
1529
+ systemPrompt: systemPrompt + buildAgentContext(),
1459
1530
  signal: abortCtrl.signal,
1460
1531
  permissionMode: currentPermissionMode(),
1461
1532
  effort: currentEffort(),
@@ -1470,14 +1541,18 @@ export async function runHadamardTui(options = {}) {
1470
1541
  resultPromise = stream.result;
1471
1542
  }
1472
1543
  else {
1544
+ const selectedConfigModel = resolveHadamardConfigurationModel(activeBridgeConfig, bridgeMode, bridgeModelLabel);
1473
1545
  const stream = session.stream(expandImageRefs(text), {
1474
- systemPrompt: applyOutputStyle(systemPrompt + buildAgentContext(), outputStyle),
1546
+ systemPrompt: systemPrompt + buildAgentContext(),
1475
1547
  signal: abortCtrl.signal,
1476
1548
  permissionMode: currentPermissionMode(),
1477
- effort: currentEffort(),
1549
+ // A matched route's effort overrides the session effort for this turn.
1550
+ effort: routed?.effort ?? currentEffort(),
1478
1551
  approver,
1479
1552
  classifier: preToolUseHookClassifier,
1480
- ...(routed ? { model: routed.model, modelApi: routed.modelApi } : {}),
1553
+ ...(routed
1554
+ ? { model: routed.model, modelApi: routed.modelApi }
1555
+ : selectedConfigModel ? { model: selectedConfigModel } : {}),
1481
1556
  ...(activeTeamTool && teamPrefs.autoInvoke ? { tools: [...tools, activeTeamTool] } : {}),
1482
1557
  ...(canUseTool ? { canUseTool } : {}),
1483
1558
  });
@@ -1491,6 +1566,7 @@ export async function runHadamardTui(options = {}) {
1491
1566
  }
1492
1567
  }
1493
1568
  const result = await resultPromise;
1569
+ collapseReasoning();
1494
1570
  // Accumulate token + USD usage for /cost and /usage. The model is the
1495
1571
  // routed model (if a router is active) or the session model. Bridge runs
1496
1572
  recordUsage(routed?.model ?? activeBridgeModelApi?.model ?? bridgeModelLabel ?? session.model, result.usage);
@@ -1521,6 +1597,7 @@ export async function runHadamardTui(options = {}) {
1521
1597
  appendStatic(['']);
1522
1598
  }
1523
1599
  catch (error) {
1600
+ collapseReasoning();
1524
1601
  const rest = flusher.drain();
1525
1602
  if (rest.length > 0)
1526
1603
  appendStatic(rest);
@@ -1552,7 +1629,7 @@ export async function runHadamardTui(options = {}) {
1552
1629
  switch (event.type) {
1553
1630
  case 'run.started':
1554
1631
  streamedTextSeen = false;
1555
- streamedThinkingSeen = false;
1632
+ reasoningDisplay.reset();
1556
1633
  return;
1557
1634
  case 'request.started':
1558
1635
  lastTokenEstimate = typeof data.requestTokenEstimate === 'number'
@@ -1564,6 +1641,7 @@ export async function runHadamardTui(options = {}) {
1564
1641
  const delta = typeof data.delta === 'string' ? data.delta : '';
1565
1642
  if (!delta)
1566
1643
  return;
1644
+ collapseReasoning();
1567
1645
  streamedTextSeen = true;
1568
1646
  const flushed = flusher.push(delta);
1569
1647
  if (flushed.length > 0)
@@ -1575,10 +1653,7 @@ export async function runHadamardTui(options = {}) {
1575
1653
  const delta = typeof data.delta === 'string' ? data.delta : '';
1576
1654
  if (!delta)
1577
1655
  return;
1578
- const lines = formatThinking(delta, screen.width);
1579
- if (lines.length > 0)
1580
- appendStatic(lines);
1581
- streamedThinkingSeen = true;
1656
+ reasoningDisplay.append(delta);
1582
1657
  scheduleDynamicRender();
1583
1658
  return;
1584
1659
  }
@@ -1594,15 +1669,15 @@ export async function runHadamardTui(options = {}) {
1594
1669
  }
1595
1670
  case 'model.content': {
1596
1671
  const content = isRecord(data.content) ? data.content : undefined;
1597
- if (data.kind === 'content' && content?.type === 'thinking' && !streamedThinkingSeen) {
1672
+ if (data.kind === 'content' && content?.type === 'thinking' && !reasoningDisplay.hasStreamedContent) {
1598
1673
  const thinking = typeof content.thinking === 'string' ? content.thinking : '';
1599
- const lines = formatThinking(thinking, screen.width);
1600
- if (lines.length > 0)
1601
- appendStatic(lines);
1674
+ reasoningDisplay.setCompleteContent(thinking);
1675
+ scheduleDynamicRender();
1602
1676
  }
1603
1677
  return;
1604
1678
  }
1605
1679
  case 'tool.started': {
1680
+ collapseReasoning();
1606
1681
  const callId = typeof data.callId === 'string' ? data.callId : '';
1607
1682
  const publicName = typeof data.publicName === 'string'
1608
1683
  ? data.publicName
@@ -1739,35 +1814,37 @@ export async function runHadamardTui(options = {}) {
1739
1814
  await resumeSession(selected);
1740
1815
  }
1741
1816
  async function chooseModel() {
1817
+ const catalog = buildModelConfigurationCatalog({
1818
+ model: sdk.config.model,
1819
+ provider: sdk.config.provider,
1820
+ baseURL: sdk.config.baseURL,
1821
+ }, readBridgeConfigs().configs);
1742
1822
  const items = [
1743
- {
1744
- id: 'default',
1745
- label: 'Configured default',
1746
- description: sdk.config.model,
1747
- },
1748
- ...['min', 'medium', 'max']
1749
- .filter(tier => Boolean(sdk.config.modelTiers[tier]))
1750
- .map(tier => ({
1751
- id: `tier:${tier}`,
1752
- label: tier,
1753
- description: sdk.config.modelTiers[tier],
1823
+ ...catalog.map(config => ({
1824
+ id: config.id,
1825
+ label: config.source === 'default' ? 'default' : config.name,
1826
+ description: [
1827
+ config.execution === 'cli' ? `${config.runtime} CLI` : config.runtime,
1828
+ config.model || 'runtime default',
1829
+ config.models.length > 1 ? `${config.models.length} models` : '',
1830
+ ].filter(Boolean).join(' · '),
1754
1831
  })),
1755
1832
  {
1756
1833
  id: 'custom',
1757
1834
  label: 'Enter a model ID',
1758
- description: 'Session override',
1835
+ description: 'Use the default provider for this session',
1759
1836
  },
1760
1837
  {
1761
1838
  id: 'configure',
1762
- label: 'Configure provider, API key, and models',
1763
- description: 'Updates the active Hadamard Agent settings file',
1839
+ label: 'Manage configurations',
1840
+ description: 'Default plus named provider configurations',
1764
1841
  },
1765
1842
  ];
1766
1843
  const selected = await selectItem({
1767
1844
  title: 'Select model',
1768
1845
  subtitle: `Current: ${session.model}`,
1769
1846
  items,
1770
- searchable: false,
1847
+ searchable: true,
1771
1848
  });
1772
1849
  if (!selected)
1773
1850
  return;
@@ -1783,15 +1860,38 @@ export async function runHadamardTui(options = {}) {
1783
1860
  });
1784
1861
  if (!model?.trim())
1785
1862
  return;
1863
+ await disableBridge();
1786
1864
  await session.setModel(model.trim());
1787
1865
  }
1788
- else if (selected === 'default') {
1789
- await session.setModel(sdk.config.model);
1790
- }
1791
1866
  else {
1792
- await session.setModel(selected.slice('tier:'.length));
1867
+ const config = findModelConfiguration(catalog, selected);
1868
+ if (!config)
1869
+ return;
1870
+ if (config.source === 'default') {
1871
+ await disableBridge();
1872
+ await session.setModel(sdk.config.model);
1873
+ }
1874
+ else if (config.config) {
1875
+ let selectedConfig = { ...config.config, model: config.model };
1876
+ if (config.models.length > 1) {
1877
+ const model = await selectItem({
1878
+ title: config.name,
1879
+ subtitle: 'Select a model from this configuration',
1880
+ searchable: true,
1881
+ items: config.models.map(item => ({
1882
+ id: item.name,
1883
+ label: item.name,
1884
+ description: item.modality ?? 'text',
1885
+ })),
1886
+ });
1887
+ if (!model)
1888
+ return;
1889
+ selectedConfig = { ...selectedConfig, model };
1890
+ }
1891
+ await activateBridgeConfig(selectedConfig);
1892
+ }
1793
1893
  }
1794
- appendStatic([...formatInfoLine(`model set to: ${session.model}`), '']);
1894
+ appendStatic([...formatInfoLine(`model configuration: ${activeBridgeConfig?.name ?? 'default'} · ${bridgeModelLabel ?? session.model}`), '']);
1795
1895
  }
1796
1896
  async function configureModelSettings() {
1797
1897
  const store = await resolveHadamardSettingsStore({ configPath: options.configPath });
@@ -1830,11 +1930,16 @@ export async function runHadamardTui(options = {}) {
1830
1930
  label: 'Base URL',
1831
1931
  description: env.HADAMARD_BASE_URL || 'provider default',
1832
1932
  },
1833
- ...['min', 'medium', 'max'].map(tier => ({
1834
- id: `tier:${tier}`,
1835
- label: `${tier} model`,
1836
- description: env[`HADAMARD_DEFAULT_${tier.toUpperCase()}_MODEL`] || 'not configured',
1837
- })),
1933
+ {
1934
+ id: 'model',
1935
+ label: 'Default model',
1936
+ description: env.HADAMARD_MODEL || sdk.config.model || 'not configured',
1937
+ },
1938
+ {
1939
+ id: 'named',
1940
+ label: 'Named configurations',
1941
+ description: `${readBridgeConfigs().configs.length} configured`,
1942
+ },
1838
1943
  {
1839
1944
  id: 'save',
1840
1945
  label: 'Save and apply',
@@ -1904,21 +2009,23 @@ export async function runHadamardTui(options = {}) {
1904
2009
  }
1905
2010
  continue;
1906
2011
  }
1907
- if (selected.startsWith('tier:')) {
1908
- const tier = selected.slice('tier:'.length).toUpperCase();
1909
- const key = `HADAMARD_DEFAULT_${tier}_MODEL`;
2012
+ if (selected === 'model') {
1910
2013
  const model = await promptText({
1911
- title: `Configure ${tier.toLowerCase()} model`,
2014
+ title: 'Configure default model',
1912
2015
  label: 'Model ID',
1913
- initial: env[key] ?? '',
2016
+ initial: env.HADAMARD_MODEL ?? sdk.config.model,
1914
2017
  });
1915
2018
  if (model !== undefined) {
1916
2019
  if (model.trim())
1917
- env[key] = model.trim();
2020
+ env.HADAMARD_MODEL = model.trim();
1918
2021
  else
1919
- delete env[key];
2022
+ delete env.HADAMARD_MODEL;
1920
2023
  dirty = true;
1921
2024
  }
2025
+ continue;
2026
+ }
2027
+ if (selected === 'named') {
2028
+ await manageBridgeConfigs();
1922
2029
  }
1923
2030
  }
1924
2031
  }
@@ -2986,23 +3093,14 @@ export async function runHadamardTui(options = {}) {
2986
3093
  });
2987
3094
  appendStatic([...formatInfoLine(`effort set to: ${currentEffort() ?? 'auto'}`), '']);
2988
3095
  }
2989
- // ── /goal: session-scoped goal managed by the shared GoalService ─────
3096
+ // ── /goal: project-scoped goal managed by the shared GoalService ─────
2990
3097
  // The service is the single authority over goal lifecycle (see plan/13
2991
3098
  // P0.2); the TUI only reads and steers it (create/clear/pause/resume).
2992
3099
  // Complete/blocked are runtime-only transitions, set via the Goal tools.
2993
- function goalService() {
2994
- return GoalService.forSession(session);
2995
- }
2996
- async function setGoal(objective) {
2997
- await goalService().create({ objective });
2998
- }
2999
- async function clearGoal() {
3000
- await goalService().clear();
3001
- }
3002
3100
  function goalContextLine() {
3003
3101
  // Normalize through the shared Goal schema while keeping status rendering
3004
3102
  // synchronous over the already-cached Session metadata.
3005
- const goal = normalizeGoal(session.metadata[GOAL_METADATA_KEY], new Date().toISOString());
3103
+ const goal = sdk.goals.peek(session.id);
3006
3104
  if (!goal)
3007
3105
  return '';
3008
3106
  const marks = {
@@ -3014,28 +3112,6 @@ export async function runHadamardTui(options = {}) {
3014
3112
  const mark = marks[goal.status ?? 'active'] ?? '';
3015
3113
  return ` · goal:${mark}${A.dim}${truncateToWidth(goal.objective, 30)}${A.reset}`;
3016
3114
  }
3017
- async function chooseOutputStyle(arg) {
3018
- const valid = OUTPUT_STYLES.map(s => s.id);
3019
- if (arg) {
3020
- if (!valid.includes(arg)) {
3021
- appendStatic([...formatErrorLine(`unknown output style: ${arg}. Valid: ${valid.join(', ')}`), '']);
3022
- return;
3023
- }
3024
- outputStyle = arg;
3025
- appendStatic([...formatInfoLine(`output style → ${outputStyle}`), '']);
3026
- return;
3027
- }
3028
- const selected = await selectItem({
3029
- title: 'Select output style',
3030
- subtitle: `Current: ${outputStyle}`,
3031
- searchable: false,
3032
- items: OUTPUT_STYLES.map(s => ({ id: s.id, label: s.label, description: s.description })),
3033
- });
3034
- if (selected) {
3035
- outputStyle = selected;
3036
- appendStatic([...formatInfoLine(`output style → ${outputStyle}`), '']);
3037
- }
3038
- }
3039
3115
  async function showSkills() {
3040
3116
  const skills = sdk.skills.listMetadata();
3041
3117
  if (skills.length === 0) {
@@ -3441,6 +3517,10 @@ export async function runHadamardTui(options = {}) {
3441
3517
  const spaceIndex = raw.indexOf(' ');
3442
3518
  const name = (spaceIndex === -1 ? raw.slice(1) : raw.slice(1, spaceIndex)).toLowerCase();
3443
3519
  const args = spaceIndex === -1 ? '' : raw.slice(spaceIndex + 1).trim();
3520
+ if (!canRunInteractiveCommand(name, running)) {
3521
+ appendStatic(formatInfoLine(`/${name} requires an idle session; it will be available after this run`));
3522
+ return;
3523
+ }
3444
3524
  appendStatic(formatUserPrompt(raw));
3445
3525
  commandBusy = true;
3446
3526
  renderDynamic();
@@ -3467,6 +3547,7 @@ export async function runHadamardTui(options = {}) {
3467
3547
  }
3468
3548
  case 'clear':
3469
3549
  process.stdout.write('\x1b[2J\x1b[H');
3550
+ screen.setDynamic([]);
3470
3551
  renderDynamic();
3471
3552
  return;
3472
3553
  case 'init': {
@@ -3493,8 +3574,71 @@ export async function runHadamardTui(options = {}) {
3493
3574
  await chooseRouter(args.slice('router'.length).trim());
3494
3575
  return;
3495
3576
  }
3496
- await session.setModel(args === 'default' ? sdk.config.model : args);
3497
- appendStatic([...formatInfoLine(`model set to: ${session.model}`), '']);
3577
+ if (args === 'custom' || args.startsWith('custom ')) {
3578
+ const customModel = args.slice('custom'.length).trim();
3579
+ if (!customModel) {
3580
+ appendStatic([...formatErrorLine('usage: /model custom <model-id>'), '']);
3581
+ return;
3582
+ }
3583
+ await disableBridge();
3584
+ await session.setModel(customModel);
3585
+ appendStatic([...formatInfoLine(`custom model: ${session.model}`), '']);
3586
+ return;
3587
+ }
3588
+ const catalog = buildModelConfigurationCatalog({ model: sdk.config.model, provider: sdk.config.provider, baseURL: sdk.config.baseURL }, readBridgeConfigs().configs);
3589
+ // `/model <config>` or `/model <config> <model-id>` — match the longest
3590
+ // known configuration name so model ids with spaces still work.
3591
+ let config = findModelConfiguration(catalog, args);
3592
+ let explicitModel;
3593
+ if (!config) {
3594
+ const tokens = args.split(/\s+/u).filter(Boolean);
3595
+ for (let i = tokens.length - 1; i >= 1; i -= 1) {
3596
+ const candidate = tokens.slice(0, i).join(' ');
3597
+ const match = findModelConfiguration(catalog, candidate);
3598
+ if (match) {
3599
+ config = match;
3600
+ explicitModel = tokens.slice(i).join(' ');
3601
+ break;
3602
+ }
3603
+ }
3604
+ }
3605
+ if (!config) {
3606
+ appendStatic([
3607
+ ...formatErrorLine(`unknown model configuration: ${args}`),
3608
+ ...formatInfoLine('Use /model to choose one, or /model custom <model-id>.'),
3609
+ '',
3610
+ ]);
3611
+ return;
3612
+ }
3613
+ if (config.source === 'default') {
3614
+ await disableBridge();
3615
+ await session.setModel(explicitModel || sdk.config.model);
3616
+ appendStatic([...formatInfoLine(`model configuration: default · ${session.model}`), '']);
3617
+ return;
3618
+ }
3619
+ if (!config.config)
3620
+ return;
3621
+ let selectedConfig = { ...config.config, model: config.model };
3622
+ if (explicitModel) {
3623
+ selectedConfig = { ...selectedConfig, model: explicitModel };
3624
+ }
3625
+ else if (config.models.length > 1) {
3626
+ const model = await selectItem({
3627
+ title: config.name,
3628
+ subtitle: 'Select a model from this configuration',
3629
+ searchable: true,
3630
+ items: config.models.map(item => ({
3631
+ id: item.name,
3632
+ label: item.name,
3633
+ description: item.modality ?? 'text',
3634
+ })),
3635
+ });
3636
+ if (!model)
3637
+ return;
3638
+ selectedConfig = { ...selectedConfig, model };
3639
+ }
3640
+ await activateBridgeConfig(selectedConfig);
3641
+ appendStatic([...formatInfoLine(`model configuration: ${activeBridgeConfig?.name ?? config.name} · ${bridgeModelLabel ?? session.model}`), '']);
3498
3642
  return;
3499
3643
  }
3500
3644
  case 'effort':
@@ -3503,9 +3647,6 @@ export async function runHadamardTui(options = {}) {
3503
3647
  else
3504
3648
  await setEffort(args.toLowerCase());
3505
3649
  return;
3506
- case 'output-style':
3507
- await chooseOutputStyle(args.toLowerCase());
3508
- return;
3509
3650
  case 'permissions': {
3510
3651
  // Three presets, selectable or named directly:
3511
3652
  // read-only → deny mutating tools (read / search / web only)
@@ -3761,27 +3902,27 @@ export async function runHadamardTui(options = {}) {
3761
3902
  ]);
3762
3903
  return;
3763
3904
  case 'memory': {
3764
- if (/^(proposals|apply|reject)\b/u.test(args)) {
3765
- try {
3766
- const { MemoryProposalCommandService } = await import('../memory/memoryProposalCommandService.js');
3767
- const result = await new MemoryProposalCommandService(sdk.config.homeDir, sdk.config.workDir).execute(args);
3768
- appendStatic([
3769
- ...formatInfoLine(result.message),
3770
- ...(result.items ?? []).map(item => ` ${A.bold}${item.label}${A.reset}${item.description ? ` ${A.dim}· ${item.description}${A.reset}` : ''}`),
3771
- '',
3772
- ]);
3773
- }
3774
- catch (error) {
3775
- appendStatic([...formatErrorLine(error instanceof Error ? error.message : String(error)), '']);
3776
- }
3777
- return;
3778
- }
3779
3905
  try {
3780
- const state = await session.compactState();
3781
- appendStatic([`${A.dim}${JSON.stringify(state, null, 2)}${A.reset}`, '']);
3906
+ const { HadamardMemoryCommandService } = await import('../memory/memoryCommandService.js');
3907
+ const result = await new HadamardMemoryCommandService({
3908
+ memory: sdk.memory,
3909
+ proposals: sdk.memoryProposals,
3910
+ compactConfig: sdk.config.compact,
3911
+ getState: () => session.compactState(),
3912
+ }).execute(args || 'status');
3913
+ appendStatic([
3914
+ `${A.bold}${result.title}${A.reset}`,
3915
+ ...formatInfoLine(result.message),
3916
+ ...(result.text ? result.text.split('\n').map(line => `${A.dim}${line}${A.reset}`) : []),
3917
+ ...(result.items ?? []).flatMap(item => [
3918
+ ` ${A.bold}${item.label}${A.reset}${item.description ? ` ${A.dim}· ${item.description}${A.reset}` : ''}`,
3919
+ ...(item.detail ? [` ${A.dim}${item.detail}${A.reset}`] : []),
3920
+ ]),
3921
+ '',
3922
+ ]);
3782
3923
  }
3783
3924
  catch (error) {
3784
- appendStatic([...formatErrorLine(error.message), '']);
3925
+ appendStatic([...formatErrorLine(error instanceof Error ? error.message : String(error)), '']);
3785
3926
  }
3786
3927
  return;
3787
3928
  }
@@ -3789,7 +3930,9 @@ export async function runHadamardTui(options = {}) {
3789
3930
  // Break down what is consuming the context window (gap #9 vs
3790
3931
  // claude-code's /context) — usage, messages, system prompt, tools,
3791
3932
  // the loaded CLAUDE.md sources, and the active config.
3792
- const window = sdk.config.compact?.contextWindowTokens ?? 200_000;
3933
+ const { resolveHadamardCompactBudget } = await import('../runtime/hadamardCompact.js');
3934
+ const compactBudget = resolveHadamardCompactBudget(sdk.config.compact);
3935
+ const window = compactBudget.effectiveContextWindowTokens;
3793
3936
  const used = lastTokenEstimate ?? 0;
3794
3937
  const pct = window > 0 ? Math.min(100, Math.round((used / window) * 100)) : 0;
3795
3938
  const usedK = used >= 1000 ? `${(used / 1000).toFixed(1)}k` : `${used}`;
@@ -3805,6 +3948,8 @@ export async function runHadamardTui(options = {}) {
3805
3948
  appendStatic([
3806
3949
  `${A.bold}Context window${A.reset}`,
3807
3950
  ` ${ctxColor}${pct}% used (${usedK} / ${windowK} tokens)${A.reset}`,
3951
+ ` ${A.dim}raw window${A.reset} ${compactBudget.rawContextWindowTokens}`,
3952
+ ` ${A.dim}compact limit${A.reset} ${compactBudget.autoCompactTokenLimit} (${compactBudget.source})`,
3808
3953
  ` ${A.dim}messages${A.reset} ${messages}`,
3809
3954
  ` ${A.dim}system prompt${A.reset} ~${sysChars} chars`,
3810
3955
  ` ${A.dim}tools${A.reset} ${toolMetadata.length}${mcpCount > 0 ? ` (${mcpCount} MCP)` : ''}`,
@@ -3911,43 +4056,8 @@ export async function runHadamardTui(options = {}) {
3911
4056
  return;
3912
4057
  }
3913
4058
  case 'goal': {
3914
- const arg = args.trim();
3915
- const svc = goalService();
3916
- if (!arg) {
3917
- const g = await svc.read();
3918
- if (!g) {
3919
- appendStatic([...formatInfoLine('no goal set — use /goal <objective> to set one'), '']);
3920
- }
3921
- else {
3922
- const marks = { active: `${A.green}▶ active${A.reset}`, paused: `${A.yellow}‖ paused${A.reset}`, complete: `${A.dim}✓ complete${A.reset}`, blocked: `${A.red}⊘ blocked${A.reset}` };
3923
- appendStatic([`${A.bold}Goal${A.reset} ${marks[g.status] ?? ''} ${A.dim}${g.createdAt.slice(0, 10)}${A.reset}`, ` ${g.objective}`, '']);
3924
- }
3925
- return;
3926
- }
3927
- if (arg === 'clear') {
3928
- await clearGoal();
3929
- appendStatic([...formatInfoLine('goal cleared'), '']);
3930
- return;
3931
- }
3932
- if (arg === 'pause') {
3933
- const r = await svc.transition('paused');
3934
- appendStatic(r.ok ? [...formatInfoLine('goal paused'), ''] : [...formatInfoLine(r.message), '']);
3935
- return;
3936
- }
3937
- if (arg === 'resume') {
3938
- const r = await svc.transition('active');
3939
- appendStatic(r.ok ? [...formatInfoLine('goal resumed'), ''] : [...formatInfoLine(r.message), '']);
3940
- return;
3941
- }
3942
- if (arg === 'complete' || arg === 'done') {
3943
- // Complete is a runtime-only transition requiring evidence; the UI
3944
- // cannot mark a goal complete without evidence. Steer the user to
3945
- // let the agent complete it via UpdateGoal, or clear it instead.
3946
- appendStatic([...formatInfoLine('goal completion requires runtime evidence — ask the agent to call UpdateGoal with status "complete", or use /goal clear'), '']);
3947
- return;
3948
- }
3949
- await setGoal(arg);
3950
- appendStatic([...formatInfoLine(`goal set — ${arg}`), '']);
4059
+ const commandResult = await sdk.goals.command(session, args);
4060
+ appendStatic([...formatInfoLine(commandResult.message), '']);
3951
4061
  return;
3952
4062
  }
3953
4063
  case 'review': {
@@ -3974,7 +4084,6 @@ export async function runHadamardTui(options = {}) {
3974
4084
  ` ${A.dim}tokens${A.reset} ${fmtTok(totalInputTokens)} in · ${fmtTok(totalOutputTokens)} out`,
3975
4085
  ` ${A.dim}tools${A.reset} ${toolMetadata.length}${toolMetadata.filter(t => t.provider === 'mcp').length ? ` (${toolMetadata.filter(t => t.provider === 'mcp').length} MCP)` : ''}`,
3976
4086
  ` ${A.dim}model${A.reset} ${session.model}${bridgeMode && activeBridgeConfig ? ` · bridge:${activeBridgeConfig.name}` : ''}`,
3977
- ` ${A.dim}output style${A.reset} ${outputStyle}`,
3978
4087
  ` ${A.dim}plan mode${A.reset} ${session.permissionContext.mode === 'plan' ? 'on' : 'off'}`,
3979
4088
  '',
3980
4089
  ]);
@@ -4304,8 +4413,40 @@ export async function runHadamardTui(options = {}) {
4304
4413
  await runSavedWorkflow(runSpace === -1 ? runRest : runRest.slice(0, runSpace), runSpace === -1 ? undefined : runRest.slice(runSpace + 1).trim());
4305
4414
  return;
4306
4415
  }
4416
+ if (args.startsWith('delete ')) {
4417
+ const name = args.slice(7).trim();
4418
+ if (!name) {
4419
+ appendStatic([...formatErrorLine('usage: /workflows delete <name>'), '']);
4420
+ return;
4421
+ }
4422
+ const removed = await deleteWorkflow(name, sdk.config.workDir);
4423
+ appendStatic([...formatInfoLine(removed ? `deleted workflow: ${name}` : `workflow not found: ${name}`), '']);
4424
+ return;
4425
+ }
4426
+ if (args.startsWith('save ')) {
4427
+ const rest = args.slice(5).trim();
4428
+ const split = rest.indexOf(' ');
4429
+ if (split === -1) {
4430
+ appendStatic([...formatErrorLine('usage: /workflows save <name> <script-path> [--overwrite]'), '']);
4431
+ return;
4432
+ }
4433
+ const name = rest.slice(0, split).trim();
4434
+ const pathParts = rest.slice(split + 1).trim().split(/\s+/);
4435
+ const overwrite = pathParts.includes('--overwrite');
4436
+ const scriptPath = pathParts.filter((part) => part !== '--overwrite').join(' ');
4437
+ try {
4438
+ const resolved = path.isAbsolute(scriptPath) ? scriptPath : path.resolve(sdk.config.workDir, scriptPath);
4439
+ const script = fs.readFileSync(resolved, 'utf8');
4440
+ const filePath = await saveWorkflow(name, script, { projectDir: sdk.config.workDir, overwrite });
4441
+ appendStatic([...formatInfoLine(`saved workflow: ${name} -> ${filePath}`), '']);
4442
+ }
4443
+ catch (error) {
4444
+ appendStatic([...formatErrorLine(error.message || String(error)), '']);
4445
+ }
4446
+ return;
4447
+ }
4307
4448
  if (args && args !== 'list') {
4308
- appendStatic([...formatErrorLine('usage: /workflows [list|run <name> [task]]'), '']);
4449
+ appendStatic([...formatErrorLine('usage: /workflows [list|run <name> [task]|save <name> <script-path>|delete <name>]'), '']);
4309
4450
  return;
4310
4451
  }
4311
4452
  // No sub-command → selection picker.
@@ -4525,15 +4666,36 @@ export async function runHadamardTui(options = {}) {
4525
4666
  }
4526
4667
  return;
4527
4668
  }
4669
+ if (args.startsWith('delete ')) {
4670
+ const teamName = args.slice(7).trim();
4671
+ if (!teamName) {
4672
+ appendStatic([...formatErrorLine('usage: /team delete <name>'), '']);
4673
+ return;
4674
+ }
4675
+ if (getBuiltInTeamDefinition(teamName)) {
4676
+ appendStatic([...formatErrorLine(`cannot delete built-in team: ${teamName}`), '']);
4677
+ return;
4678
+ }
4679
+ const removed = await deleteTeamDefinition(teamName, sdk.config.workDir);
4680
+ if (!removed) {
4681
+ appendStatic([...formatErrorLine(`team not found: ${teamName}`), '']);
4682
+ return;
4683
+ }
4684
+ if (activeTeamName === teamName) {
4685
+ activeTeamTool = null;
4686
+ activeTeamName = null;
4687
+ }
4688
+ appendStatic([...formatInfoLine(`deleted team: ${teamName}`), '']);
4689
+ return;
4690
+ }
4528
4691
  if (args.startsWith('ask ')) {
4529
4692
  const rest = args.slice(4).trim();
4530
- const spaceIdx = rest.indexOf(' ');
4531
- if (spaceIdx === -1) {
4693
+ const parsed = parseTeamAskArguments(rest);
4694
+ if (!parsed) {
4532
4695
  appendStatic([...formatErrorLine('usage: /team ask <name> <prompt>'), '']);
4533
4696
  return;
4534
4697
  }
4535
- const teamName = rest.slice(0, spaceIdx);
4536
- const prompt = rest.slice(spaceIdx + 1).trim();
4698
+ const { name: teamName, prompt } = parsed;
4537
4699
  const loaded = loadTeamDefinition(teamName, sdk.config.workDir);
4538
4700
  if (!loaded) {
4539
4701
  appendStatic([...formatErrorLine(`team not found: ${teamName}`), '']);
@@ -4559,7 +4721,6 @@ export async function runHadamardTui(options = {}) {
4559
4721
  '',
4560
4722
  ]);
4561
4723
  try {
4562
- const team = createModelTeam(definition);
4563
4724
  const teamRunView = createTeamRunViewState(definition.name);
4564
4725
  const printTeamRunTree = () => {
4565
4726
  const lines = formatTeamRunTreeLines(teamRunView);
@@ -4567,7 +4728,7 @@ export async function runHadamardTui(options = {}) {
4567
4728
  return;
4568
4729
  appendStatic([...lines.map((line) => `${A.dim}${line}${A.reset}`), '']);
4569
4730
  };
4570
- const result = await team.ask(prompt, undefined, {
4731
+ const result = await askTeamDefinition(definition, prompt, undefined, {
4571
4732
  workDir: sdk.config.workDir,
4572
4733
  onEvent: (e) => {
4573
4734
  applyTeamRunEvent(teamRunView, e);
@@ -5235,17 +5396,31 @@ export async function runHadamardTui(options = {}) {
5235
5396
  }
5236
5397
  }
5237
5398
  // ── Submit / key handling ──────────────────────────────────────────
5238
- async function submit() {
5239
- if (!running && applyAtCompletion()) {
5399
+ /** If the editor was emptied after ↑-recall, put the follow-up back in queue. */
5400
+ function restoreRecalledFollowUpIfAbandoned() {
5401
+ if (!recalledFollowUp || !editor.isEmpty())
5402
+ return;
5403
+ restoreAbandonedFollowUp(session, recalledFollowUp);
5404
+ recalledFollowUp = null;
5405
+ }
5406
+ async function submit(mode = 'follow-up') {
5407
+ if (applyAtCompletion()) {
5240
5408
  renderDynamic();
5241
5409
  return;
5242
5410
  }
5243
- const selectedCommand = !running
5244
- ? selectInteractiveCommand(editor.text, menuSelected)
5245
- : undefined;
5411
+ const selectedCommand = selectInteractiveCommand(editor.text, menuSelected);
5246
5412
  if (selectedCommand) {
5413
+ const selectedName = selectedCommand.slice(1).split(/\s/u, 1)[0] ?? '';
5414
+ // Slash completions are not a resubmit of the recalled follow-up.
5415
+ restoreAbandonedFollowUp(session, recalledFollowUp);
5416
+ recalledFollowUp = null;
5247
5417
  editor.clear();
5248
5418
  menuSelected = 0;
5419
+ if (running && !canRunInteractiveCommand(selectedName, true)) {
5420
+ appendStatic(formatInfoLine(`/${selectedName} requires an idle session; it will be available after this run`));
5421
+ renderDynamic();
5422
+ return;
5423
+ }
5249
5424
  // A selected completion already contains its subcommand. Keeping the
5250
5425
  // partially typed second word would turn `/agents runs` into
5251
5426
  // `/agents runs runs` on Enter.
@@ -5260,17 +5435,31 @@ export async function runHadamardTui(options = {}) {
5260
5435
  const text = value.trim();
5261
5436
  menuSelected = 0;
5262
5437
  if (!text) {
5438
+ restoreRecalledFollowUpIfAbandoned();
5263
5439
  renderDynamic();
5264
5440
  return;
5265
5441
  }
5266
5442
  if (running) {
5267
5443
  if (text.startsWith('/')) {
5268
- appendStatic(formatInfoLine('slash commands are unavailable while the agent is working'));
5444
+ // Slash commands are not a resubmit of the recalled follow-up.
5445
+ restoreAbandonedFollowUp(session, recalledFollowUp);
5446
+ recalledFollowUp = null;
5447
+ const command = text.slice(1).trim().split(/\s/u, 1)[0]?.toLowerCase() ?? '';
5448
+ if (!canRunInteractiveCommand(command, true)) {
5449
+ appendStatic(formatInfoLine(`/${command} requires an idle session; it will be available after this run`));
5450
+ renderDynamic();
5451
+ return;
5452
+ }
5453
+ await runSlashCommand(text);
5269
5454
  renderDynamic();
5270
5455
  return;
5271
5456
  }
5272
- session.steer(text);
5273
- appendStatic(formatQueuedPrompt(text));
5457
+ // Resubmit consumes the recalled draft (re-queued below).
5458
+ recalledFollowUp = null;
5459
+ submitActiveInput(session, text, mode);
5460
+ appendStatic(mode === 'steer'
5461
+ ? formatInfoLine(`steering now: ${text.replace(/\s+/g, ' ')}`)
5462
+ : formatQueuedPrompt(text));
5274
5463
  renderDynamic();
5275
5464
  return;
5276
5465
  }
@@ -5279,9 +5468,12 @@ export async function runHadamardTui(options = {}) {
5279
5468
  return;
5280
5469
  }
5281
5470
  if (text.startsWith('/')) {
5471
+ restoreAbandonedFollowUp(session, recalledFollowUp);
5472
+ recalledFollowUp = null;
5282
5473
  await runSlashCommand(text);
5283
5474
  return;
5284
5475
  }
5476
+ recalledFollowUp = null;
5285
5477
  void startRun(text);
5286
5478
  }
5287
5479
  function handleDialogKey(key) {
@@ -5462,6 +5654,7 @@ export async function runHadamardTui(options = {}) {
5462
5654
  else if (!editor.isEmpty()) {
5463
5655
  editor.clear();
5464
5656
  menuSelected = 0;
5657
+ restoreRecalledFollowUpIfAbandoned();
5465
5658
  }
5466
5659
  renderDynamic();
5467
5660
  return;
@@ -5472,6 +5665,7 @@ export async function runHadamardTui(options = {}) {
5472
5665
  return;
5473
5666
  }
5474
5667
  editor.deleteForward();
5668
+ restoreRecalledFollowUpIfAbandoned();
5475
5669
  break;
5476
5670
  case 'a':
5477
5671
  editor.moveHome();
@@ -5481,12 +5675,15 @@ export async function runHadamardTui(options = {}) {
5481
5675
  break;
5482
5676
  case 'k':
5483
5677
  editor.killToEnd();
5678
+ restoreRecalledFollowUpIfAbandoned();
5484
5679
  break;
5485
5680
  case 'u':
5486
5681
  editor.killToStart();
5682
+ restoreRecalledFollowUpIfAbandoned();
5487
5683
  break;
5488
5684
  case 'w':
5489
5685
  editor.deleteWordLeft();
5686
+ restoreRecalledFollowUpIfAbandoned();
5490
5687
  break;
5491
5688
  case 'left':
5492
5689
  editor.moveWordLeft();
@@ -5508,6 +5705,13 @@ export async function runHadamardTui(options = {}) {
5508
5705
  }
5509
5706
  switch (name) {
5510
5707
  case 'return': {
5708
+ if (key.shift) {
5709
+ if (running)
5710
+ void submit('steer');
5711
+ else
5712
+ editor.insert('\n');
5713
+ return;
5714
+ }
5511
5715
  if (key.meta) {
5512
5716
  editor.insert('\n');
5513
5717
  break;
@@ -5516,6 +5720,10 @@ export async function runHadamardTui(options = {}) {
5516
5720
  return;
5517
5721
  }
5518
5722
  case 'enter':
5723
+ if (key.shift && running) {
5724
+ void submit('steer');
5725
+ return;
5726
+ }
5519
5727
  editor.insert('\n');
5520
5728
  break;
5521
5729
  case 'escape': {
@@ -5525,6 +5733,7 @@ export async function runHadamardTui(options = {}) {
5525
5733
  else if (!editor.isEmpty()) {
5526
5734
  editor.clear();
5527
5735
  menuSelected = 0;
5736
+ restoreRecalledFollowUpIfAbandoned();
5528
5737
  }
5529
5738
  break;
5530
5739
  }
@@ -5532,9 +5741,11 @@ export async function runHadamardTui(options = {}) {
5532
5741
  editor.backspace();
5533
5742
  menuSelected = 0;
5534
5743
  atSelected = 0;
5744
+ restoreRecalledFollowUpIfAbandoned();
5535
5745
  break;
5536
5746
  case 'delete':
5537
5747
  editor.deleteForward();
5748
+ restoreRecalledFollowUpIfAbandoned();
5538
5749
  break;
5539
5750
  case 'left':
5540
5751
  if (key.meta)
@@ -5555,6 +5766,14 @@ export async function runHadamardTui(options = {}) {
5555
5766
  editor.moveEnd();
5556
5767
  break;
5557
5768
  case 'up': {
5769
+ if (running && editor.isEmpty()) {
5770
+ const recalled = recallLatestFollowUp(session);
5771
+ if (recalled) {
5772
+ recalledFollowUp = recalled;
5773
+ editor.setText(recalled);
5774
+ break;
5775
+ }
5776
+ }
5558
5777
  const atToken = activeAtToken(editor.text, editor.cursor);
5559
5778
  const atCount = atToken ? atCompletions(atToken.token).length : 0;
5560
5779
  const menu = filterSlashCommands(editor.text);