@dsh-cc/tui 0.5.0

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 (363) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +144 -0
  3. package/lib/bash-history.d.ts +31 -0
  4. package/lib/bash-history.d.ts.map +1 -0
  5. package/lib/bash-history.js +94 -0
  6. package/lib/bash-history.js.map +1 -0
  7. package/lib/compact-fold.d.ts +35 -0
  8. package/lib/compact-fold.d.ts.map +1 -0
  9. package/lib/compact-fold.js +60 -0
  10. package/lib/compact-fold.js.map +1 -0
  11. package/lib/components/arg-completers.d.ts +22 -0
  12. package/lib/components/arg-completers.d.ts.map +1 -0
  13. package/lib/components/arg-completers.js +75 -0
  14. package/lib/components/arg-completers.js.map +1 -0
  15. package/lib/components/code-theme.d.ts +25 -0
  16. package/lib/components/code-theme.d.ts.map +1 -0
  17. package/lib/components/code-theme.js +137 -0
  18. package/lib/components/code-theme.js.map +1 -0
  19. package/lib/components/completion.d.ts +50 -0
  20. package/lib/components/completion.d.ts.map +1 -0
  21. package/lib/components/completion.js +279 -0
  22. package/lib/components/completion.js.map +1 -0
  23. package/lib/components/diff-card.d.ts +29 -0
  24. package/lib/components/diff-card.d.ts.map +1 -0
  25. package/lib/components/diff-card.js +175 -0
  26. package/lib/components/diff-card.js.map +1 -0
  27. package/lib/components/diff-hunks.d.ts +44 -0
  28. package/lib/components/diff-hunks.d.ts.map +1 -0
  29. package/lib/components/diff-hunks.js +188 -0
  30. package/lib/components/diff-hunks.js.map +1 -0
  31. package/lib/components/editor-history.d.ts +23 -0
  32. package/lib/components/editor-history.d.ts.map +1 -0
  33. package/lib/components/editor-history.js +29 -0
  34. package/lib/components/editor-history.js.map +1 -0
  35. package/lib/components/markdown-theme.d.ts +11 -0
  36. package/lib/components/markdown-theme.d.ts.map +1 -0
  37. package/lib/components/markdown-theme.js +29 -0
  38. package/lib/components/markdown-theme.js.map +1 -0
  39. package/lib/components/masked-input.d.ts +24 -0
  40. package/lib/components/masked-input.d.ts.map +1 -0
  41. package/lib/components/masked-input.js +168 -0
  42. package/lib/components/masked-input.js.map +1 -0
  43. package/lib/components/overlay-host.d.ts +31 -0
  44. package/lib/components/overlay-host.d.ts.map +1 -0
  45. package/lib/components/overlay-host.js +128 -0
  46. package/lib/components/overlay-host.js.map +1 -0
  47. package/lib/components/overlays.d.ts +94 -0
  48. package/lib/components/overlays.d.ts.map +1 -0
  49. package/lib/components/overlays.js +450 -0
  50. package/lib/components/overlays.js.map +1 -0
  51. package/lib/components/provider-box.d.ts +23 -0
  52. package/lib/components/provider-box.d.ts.map +1 -0
  53. package/lib/components/provider-box.js +164 -0
  54. package/lib/components/provider-box.js.map +1 -0
  55. package/lib/components/read-group.d.ts +51 -0
  56. package/lib/components/read-group.d.ts.map +1 -0
  57. package/lib/components/read-group.js +105 -0
  58. package/lib/components/read-group.js.map +1 -0
  59. package/lib/components/root-bash.d.ts +36 -0
  60. package/lib/components/root-bash.d.ts.map +1 -0
  61. package/lib/components/root-bash.js +112 -0
  62. package/lib/components/root-bash.js.map +1 -0
  63. package/lib/components/root-types.d.ts +42 -0
  64. package/lib/components/root-types.d.ts.map +1 -0
  65. package/lib/components/root-types.js +8 -0
  66. package/lib/components/root-types.js.map +1 -0
  67. package/lib/components/root-utils.d.ts +22 -0
  68. package/lib/components/root-utils.d.ts.map +1 -0
  69. package/lib/components/root-utils.js +45 -0
  70. package/lib/components/root-utils.js.map +1 -0
  71. package/lib/components/root.d.ts +25 -0
  72. package/lib/components/root.d.ts.map +1 -0
  73. package/lib/components/root.js +357 -0
  74. package/lib/components/root.js.map +1 -0
  75. package/lib/components/theme.d.ts +61 -0
  76. package/lib/components/theme.d.ts.map +1 -0
  77. package/lib/components/theme.js +99 -0
  78. package/lib/components/theme.js.map +1 -0
  79. package/lib/components/transcript.d.ts +43 -0
  80. package/lib/components/transcript.d.ts.map +1 -0
  81. package/lib/components/transcript.js +203 -0
  82. package/lib/components/transcript.js.map +1 -0
  83. package/lib/components/working-line.d.ts +35 -0
  84. package/lib/components/working-line.d.ts.map +1 -0
  85. package/lib/components/working-line.js +66 -0
  86. package/lib/components/working-line.js.map +1 -0
  87. package/lib/effort-catalog.d.ts +27 -0
  88. package/lib/effort-catalog.d.ts.map +1 -0
  89. package/lib/effort-catalog.js +36 -0
  90. package/lib/effort-catalog.js.map +1 -0
  91. package/lib/export-markdown.d.ts +18 -0
  92. package/lib/export-markdown.d.ts.map +1 -0
  93. package/lib/export-markdown.js +124 -0
  94. package/lib/export-markdown.js.map +1 -0
  95. package/lib/harness/approval-preview.d.ts +33 -0
  96. package/lib/harness/approval-preview.d.ts.map +1 -0
  97. package/lib/harness/approval-preview.js +179 -0
  98. package/lib/harness/approval-preview.js.map +1 -0
  99. package/lib/harness/boot-banner.d.ts +14 -0
  100. package/lib/harness/boot-banner.d.ts.map +1 -0
  101. package/lib/harness/boot-banner.js +89 -0
  102. package/lib/harness/boot-banner.js.map +1 -0
  103. package/lib/harness/driver-agent.d.ts +78 -0
  104. package/lib/harness/driver-agent.d.ts.map +1 -0
  105. package/lib/harness/driver-agent.js +314 -0
  106. package/lib/harness/driver-agent.js.map +1 -0
  107. package/lib/harness/driver-agents.d.ts +20 -0
  108. package/lib/harness/driver-agents.d.ts.map +1 -0
  109. package/lib/harness/driver-agents.js +123 -0
  110. package/lib/harness/driver-agents.js.map +1 -0
  111. package/lib/harness/driver-approvals.d.ts +38 -0
  112. package/lib/harness/driver-approvals.d.ts.map +1 -0
  113. package/lib/harness/driver-approvals.js +277 -0
  114. package/lib/harness/driver-approvals.js.map +1 -0
  115. package/lib/harness/driver-bash.d.ts +15 -0
  116. package/lib/harness/driver-bash.d.ts.map +1 -0
  117. package/lib/harness/driver-bash.js +73 -0
  118. package/lib/harness/driver-bash.js.map +1 -0
  119. package/lib/harness/driver-catalog.d.ts +84 -0
  120. package/lib/harness/driver-catalog.d.ts.map +1 -0
  121. package/lib/harness/driver-catalog.js +256 -0
  122. package/lib/harness/driver-catalog.js.map +1 -0
  123. package/lib/harness/driver-ctx.d.ts +462 -0
  124. package/lib/harness/driver-ctx.d.ts.map +1 -0
  125. package/lib/harness/driver-ctx.js +11 -0
  126. package/lib/harness/driver-ctx.js.map +1 -0
  127. package/lib/harness/driver-hud.d.ts +31 -0
  128. package/lib/harness/driver-hud.d.ts.map +1 -0
  129. package/lib/harness/driver-hud.js +204 -0
  130. package/lib/harness/driver-hud.js.map +1 -0
  131. package/lib/harness/driver-live.d.ts +36 -0
  132. package/lib/harness/driver-live.d.ts.map +1 -0
  133. package/lib/harness/driver-live.js +44 -0
  134. package/lib/harness/driver-live.js.map +1 -0
  135. package/lib/harness/driver-modal.d.ts +52 -0
  136. package/lib/harness/driver-modal.d.ts.map +1 -0
  137. package/lib/harness/driver-modal.js +78 -0
  138. package/lib/harness/driver-modal.js.map +1 -0
  139. package/lib/harness/driver-mode.d.ts +35 -0
  140. package/lib/harness/driver-mode.d.ts.map +1 -0
  141. package/lib/harness/driver-mode.js +84 -0
  142. package/lib/harness/driver-mode.js.map +1 -0
  143. package/lib/harness/driver-pickers.d.ts +48 -0
  144. package/lib/harness/driver-pickers.d.ts.map +1 -0
  145. package/lib/harness/driver-pickers.js +249 -0
  146. package/lib/harness/driver-pickers.js.map +1 -0
  147. package/lib/harness/driver-promote.d.ts +29 -0
  148. package/lib/harness/driver-promote.d.ts.map +1 -0
  149. package/lib/harness/driver-promote.js +36 -0
  150. package/lib/harness/driver-promote.js.map +1 -0
  151. package/lib/harness/driver-queue.d.ts +19 -0
  152. package/lib/harness/driver-queue.d.ts.map +1 -0
  153. package/lib/harness/driver-queue.js +281 -0
  154. package/lib/harness/driver-queue.js.map +1 -0
  155. package/lib/harness/driver-run-local.d.ts +32 -0
  156. package/lib/harness/driver-run-local.d.ts.map +1 -0
  157. package/lib/harness/driver-run-local.js +366 -0
  158. package/lib/harness/driver-run-local.js.map +1 -0
  159. package/lib/harness/driver-sessions.d.ts +28 -0
  160. package/lib/harness/driver-sessions.d.ts.map +1 -0
  161. package/lib/harness/driver-sessions.js +386 -0
  162. package/lib/harness/driver-sessions.js.map +1 -0
  163. package/lib/harness/driver.d.ts +22 -0
  164. package/lib/harness/driver.d.ts.map +1 -0
  165. package/lib/harness/driver.js +459 -0
  166. package/lib/harness/driver.js.map +1 -0
  167. package/lib/harness/preset.d.ts +32 -0
  168. package/lib/harness/preset.d.ts.map +1 -0
  169. package/lib/harness/preset.js +34 -0
  170. package/lib/harness/preset.js.map +1 -0
  171. package/lib/harness/session-list.d.ts +65 -0
  172. package/lib/harness/session-list.d.ts.map +1 -0
  173. package/lib/harness/session-list.js +91 -0
  174. package/lib/harness/session-list.js.map +1 -0
  175. package/lib/harness/shell-output.d.ts +44 -0
  176. package/lib/harness/shell-output.d.ts.map +1 -0
  177. package/lib/harness/shell-output.js +90 -0
  178. package/lib/harness/shell-output.js.map +1 -0
  179. package/lib/harness/statusline-cc-transcript.d.ts +51 -0
  180. package/lib/harness/statusline-cc-transcript.d.ts.map +1 -0
  181. package/lib/harness/statusline-cc-transcript.js +191 -0
  182. package/lib/harness/statusline-cc-transcript.js.map +1 -0
  183. package/lib/harness/statusline-command.d.ts +54 -0
  184. package/lib/harness/statusline-command.d.ts.map +1 -0
  185. package/lib/harness/statusline-command.js +161 -0
  186. package/lib/harness/statusline-command.js.map +1 -0
  187. package/lib/harness/statusline-payload.d.ts +79 -0
  188. package/lib/harness/statusline-payload.d.ts.map +1 -0
  189. package/lib/harness/statusline-payload.js +93 -0
  190. package/lib/harness/statusline-payload.js.map +1 -0
  191. package/lib/harness/statusline-settings.d.ts +47 -0
  192. package/lib/harness/statusline-settings.d.ts.map +1 -0
  193. package/lib/harness/statusline-settings.js +57 -0
  194. package/lib/harness/statusline-settings.js.map +1 -0
  195. package/lib/harness/statusline-wiring.d.ts +45 -0
  196. package/lib/harness/statusline-wiring.d.ts.map +1 -0
  197. package/lib/harness/statusline-wiring.js +247 -0
  198. package/lib/harness/statusline-wiring.js.map +1 -0
  199. package/lib/harness/usage-view.d.ts +84 -0
  200. package/lib/harness/usage-view.d.ts.map +1 -0
  201. package/lib/harness/usage-view.js +199 -0
  202. package/lib/harness/usage-view.js.map +1 -0
  203. package/lib/harness/worktree-exit.d.ts +105 -0
  204. package/lib/harness/worktree-exit.d.ts.map +1 -0
  205. package/lib/harness/worktree-exit.js +168 -0
  206. package/lib/harness/worktree-exit.js.map +1 -0
  207. package/lib/history.d.ts +50 -0
  208. package/lib/history.d.ts.map +1 -0
  209. package/lib/history.js +139 -0
  210. package/lib/history.js.map +1 -0
  211. package/lib/index.d.ts +63 -0
  212. package/lib/index.d.ts.map +1 -0
  213. package/lib/index.js +54 -0
  214. package/lib/index.js.map +1 -0
  215. package/lib/input.d.ts +158 -0
  216. package/lib/input.d.ts.map +1 -0
  217. package/lib/input.js +400 -0
  218. package/lib/input.js.map +1 -0
  219. package/lib/invariant.d.ts +14 -0
  220. package/lib/invariant.d.ts.map +1 -0
  221. package/lib/invariant.js +20 -0
  222. package/lib/invariant.js.map +1 -0
  223. package/lib/mode-cycle.d.ts +22 -0
  224. package/lib/mode-cycle.d.ts.map +1 -0
  225. package/lib/mode-cycle.js +32 -0
  226. package/lib/mode-cycle.js.map +1 -0
  227. package/lib/model-catalog.d.ts +22 -0
  228. package/lib/model-catalog.d.ts.map +1 -0
  229. package/lib/model-catalog.js +45 -0
  230. package/lib/model-catalog.js.map +1 -0
  231. package/lib/packaged-preset.d.ts +32 -0
  232. package/lib/packaged-preset.d.ts.map +1 -0
  233. package/lib/packaged-preset.js +94 -0
  234. package/lib/packaged-preset.js.map +1 -0
  235. package/lib/plugin.d.ts +11 -0
  236. package/lib/plugin.d.ts.map +1 -0
  237. package/lib/plugin.js +88 -0
  238. package/lib/plugin.js.map +1 -0
  239. package/lib/profile.d.ts +4 -0
  240. package/lib/profile.d.ts.map +1 -0
  241. package/lib/profile.js +7 -0
  242. package/lib/profile.js.map +1 -0
  243. package/lib/project-sessions.d.ts +29 -0
  244. package/lib/project-sessions.d.ts.map +1 -0
  245. package/lib/project-sessions.js +60 -0
  246. package/lib/project-sessions.js.map +1 -0
  247. package/lib/project.d.ts +75 -0
  248. package/lib/project.d.ts.map +1 -0
  249. package/lib/project.js +196 -0
  250. package/lib/project.js.map +1 -0
  251. package/lib/provider-actions.d.ts +37 -0
  252. package/lib/provider-actions.d.ts.map +1 -0
  253. package/lib/provider-actions.js +417 -0
  254. package/lib/provider-actions.js.map +1 -0
  255. package/lib/provider-command.d.ts +109 -0
  256. package/lib/provider-command.d.ts.map +1 -0
  257. package/lib/provider-command.js +220 -0
  258. package/lib/provider-command.js.map +1 -0
  259. package/lib/provider-detail.d.ts +15 -0
  260. package/lib/provider-detail.d.ts.map +1 -0
  261. package/lib/provider-detail.js +201 -0
  262. package/lib/provider-detail.js.map +1 -0
  263. package/lib/provider-flow.d.ts +191 -0
  264. package/lib/provider-flow.d.ts.map +1 -0
  265. package/lib/provider-flow.js +192 -0
  266. package/lib/provider-flow.js.map +1 -0
  267. package/lib/provider-presets.d.ts +32 -0
  268. package/lib/provider-presets.d.ts.map +1 -0
  269. package/lib/provider-presets.js +105 -0
  270. package/lib/provider-presets.js.map +1 -0
  271. package/lib/provider-read.d.ts +63 -0
  272. package/lib/provider-read.d.ts.map +1 -0
  273. package/lib/provider-read.js +115 -0
  274. package/lib/provider-read.js.map +1 -0
  275. package/lib/provider-settings.d.ts +30 -0
  276. package/lib/provider-settings.d.ts.map +1 -0
  277. package/lib/provider-settings.js +65 -0
  278. package/lib/provider-settings.js.map +1 -0
  279. package/lib/resume-target.d.ts +59 -0
  280. package/lib/resume-target.d.ts.map +1 -0
  281. package/lib/resume-target.js +143 -0
  282. package/lib/resume-target.js.map +1 -0
  283. package/lib/slash.d.ts +44 -0
  284. package/lib/slash.d.ts.map +1 -0
  285. package/lib/slash.js +81 -0
  286. package/lib/slash.js.map +1 -0
  287. package/lib/state/driver-types.d.ts +505 -0
  288. package/lib/state/driver-types.d.ts.map +1 -0
  289. package/lib/state/driver-types.js +8 -0
  290. package/lib/state/driver-types.js.map +1 -0
  291. package/lib/state/driver-usage-types.d.ts +59 -0
  292. package/lib/state/driver-usage-types.d.ts.map +1 -0
  293. package/lib/state/driver-usage-types.js +8 -0
  294. package/lib/state/driver-usage-types.js.map +1 -0
  295. package/lib/statusline.d.ts +63 -0
  296. package/lib/statusline.d.ts.map +1 -0
  297. package/lib/statusline.js +97 -0
  298. package/lib/statusline.js.map +1 -0
  299. package/lib/store/pickers.d.ts +34 -0
  300. package/lib/store/pickers.d.ts.map +1 -0
  301. package/lib/store/pickers.js +111 -0
  302. package/lib/store/pickers.js.map +1 -0
  303. package/lib/store/provider-panel.d.ts +107 -0
  304. package/lib/store/provider-panel.d.ts.map +1 -0
  305. package/lib/store/provider-panel.js +151 -0
  306. package/lib/store/provider-panel.js.map +1 -0
  307. package/lib/store/question.d.ts +21 -0
  308. package/lib/store/question.d.ts.map +1 -0
  309. package/lib/store/question.js +59 -0
  310. package/lib/store/question.js.map +1 -0
  311. package/lib/store/queue.d.ts +27 -0
  312. package/lib/store/queue.d.ts.map +1 -0
  313. package/lib/store/queue.js +34 -0
  314. package/lib/store/queue.js.map +1 -0
  315. package/lib/store/session.d.ts +73 -0
  316. package/lib/store/session.d.ts.map +1 -0
  317. package/lib/store/session.js +172 -0
  318. package/lib/store/session.js.map +1 -0
  319. package/lib/store/subagents.d.ts +16 -0
  320. package/lib/store/subagents.d.ts.map +1 -0
  321. package/lib/store/subagents.js +44 -0
  322. package/lib/store/subagents.js.map +1 -0
  323. package/lib/store/todos.d.ts +35 -0
  324. package/lib/store/todos.d.ts.map +1 -0
  325. package/lib/store/todos.js +68 -0
  326. package/lib/store/todos.js.map +1 -0
  327. package/lib/store/usage.d.ts +15 -0
  328. package/lib/store/usage.d.ts.map +1 -0
  329. package/lib/store/usage.js +27 -0
  330. package/lib/store/usage.js.map +1 -0
  331. package/lib/store/views.d.ts +419 -0
  332. package/lib/store/views.d.ts.map +1 -0
  333. package/lib/store/views.js +3 -0
  334. package/lib/store/views.js.map +1 -0
  335. package/lib/store.d.ts +19 -0
  336. package/lib/store.d.ts.map +1 -0
  337. package/lib/store.js +10 -0
  338. package/lib/store.js.map +1 -0
  339. package/lib/terminal/lease.d.ts +33 -0
  340. package/lib/terminal/lease.d.ts.map +1 -0
  341. package/lib/terminal/lease.js +99 -0
  342. package/lib/terminal/lease.js.map +1 -0
  343. package/lib/tool-card.d.ts +75 -0
  344. package/lib/tool-card.d.ts.map +1 -0
  345. package/lib/tool-card.js +109 -0
  346. package/lib/tool-card.js.map +1 -0
  347. package/lib/tool-verbs.d.ts +12 -0
  348. package/lib/tool-verbs.d.ts.map +1 -0
  349. package/lib/tool-verbs.js +29 -0
  350. package/lib/tool-verbs.js.map +1 -0
  351. package/lib/transcript.d.ts +40 -0
  352. package/lib/transcript.d.ts.map +1 -0
  353. package/lib/transcript.js +413 -0
  354. package/lib/transcript.js.map +1 -0
  355. package/lib/working-line.d.ts +44 -0
  356. package/lib/working-line.d.ts.map +1 -0
  357. package/lib/working-line.js +56 -0
  358. package/lib/working-line.js.map +1 -0
  359. package/package.json +72 -0
  360. package/presets/cc/.dsh-cc-managed.json +5 -0
  361. package/presets/cc/agent.cordis.yml +524 -0
  362. package/presets/cc/preset.yml +3 -0
  363. package/scripts/stage-preset.mjs +26 -0
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Model / effort / permission pickers for the in-process driver. Verbatim
3
+ * extraction from harness/driver.ts (openModelPicker / applyModelSwitch /
4
+ * openEffortPicker / openPermissionPicker plus the return-literal picker
5
+ * bodies), reading shared state through a DriverPickersCtx instead of
6
+ * createDriver's closures.
7
+ *
8
+ * The pickers are stateless overlays: each open parks picker state and each
9
+ * submit reads-then-closes before validating. The bare model switch writes
10
+ * synchronously; a carried effort validates detached via resolveEfforts /
11
+ * stalePair, kept in createDriver and passed in.
12
+ * @module @dsh-cc/tui/harness/driver-pickers
13
+ */
14
+ import { ReasoningEffortId } from '@deepseek-ai/dsh-llm';
15
+ import { BYPASS_MODE, PERMISSION_MODE_OPTIONS } from '@dsh-cc/command-permissions';
16
+ import { moveEffortPickerFocus, moveModelPickerFocus, movePermissionPickerFocus, setEffortPicker, setModelPicker, setPermissionPicker, upsertRow, } from "../store.js";
17
+ import { formatModelCatalog } from "../model-catalog.js";
18
+ /**
19
+ * Free-function collaborator implementing the three pickers. Reads all shared
20
+ * state/functions off `rt` (DriverPickersCtx) so it never imports the
21
+ * createDriver factory. `selection` is passed by reference so writes land on
22
+ * createDriver's live ref across switchSession; resolveEfforts / stalePair /
23
+ * loadCatalog stay in createDriver and arrive on the ctx.
24
+ */
25
+ export function createPickersSection(rt) {
26
+ // `/model` (no args) opens a modal picker instead of dumping a text catalog.
27
+ // The arg path (`/model <n|provider/id>`) stays text-based for scripts.
28
+ const openModelPicker = async () => {
29
+ const catalog = await rt.loadCatalog();
30
+ const currentRoute = rt.selection.current === undefined
31
+ ? undefined
32
+ : { provider: rt.selection.current.provider, model: rt.selection.current.model };
33
+ if (catalog.length === 0) {
34
+ rt.emit(upsertRow(rt.state(), { kind: 'status', text: formatModelCatalog(catalog, currentRoute) }));
35
+ return;
36
+ }
37
+ const entries = catalog.map(entry => ({
38
+ provider: entry.provider,
39
+ id: entry.id,
40
+ name: entry.name,
41
+ }));
42
+ let focused = 0;
43
+ if (currentRoute !== undefined) {
44
+ const index = entries.findIndex(entry => entry.provider === currentRoute.provider && entry.id === currentRoute.model);
45
+ if (index >= 0)
46
+ focused = index;
47
+ }
48
+ rt.emit(setModelPicker(rt.state(), {
49
+ entries,
50
+ focused,
51
+ ...currentRoute === undefined ? {} : { current: currentRoute },
52
+ }));
53
+ };
54
+ /**
55
+ * Apply a `/model` switch to `provider`/`model`, carrying the live effort
56
+ * when the new model still supports it. The bare pair needs no validation,
57
+ * so the no-carried-effort path writes synchronously in the caller's tick;
58
+ * the carried path validates via rt.resolveEfforts and degrades to a
59
+ * bare pair + reset notice when the effort is unsupported OR unresolvable —
60
+ * the switch itself never fails on validation. The stale-pair guard covers
61
+ * the detached continuation: a selection that moved while validation was in
62
+ * flight is never clobbered.
63
+ */
64
+ const applyModelSwitch = async (provider, model) => {
65
+ const captured = rt.selection.current;
66
+ const carried = captured?.reasoningEffort;
67
+ if (captured === undefined || carried === undefined) {
68
+ rt.selection.current = { provider, model };
69
+ rt.emit(upsertRow(rt.state(), { kind: 'status', text: `Model is now ${provider}/${model}.` }));
70
+ return;
71
+ }
72
+ const efforts = await rt.resolveEfforts(provider, model);
73
+ if (rt.stalePair(captured))
74
+ return;
75
+ const supported = efforts?.some(level => level.id === carried) === true;
76
+ rt.selection.current = supported
77
+ ? { provider, model, reasoningEffort: ReasoningEffortId(carried) }
78
+ : { provider, model };
79
+ rt.emit(upsertRow(rt.state(), { kind: 'status', text: `Model is now ${provider}/${model}.` }));
80
+ if (!supported) {
81
+ rt.emit(upsertRow(rt.state(), {
82
+ kind: 'status',
83
+ text: `Effort "${carried}" not supported by ${model}; reset to default.`,
84
+ }));
85
+ }
86
+ };
87
+ /**
88
+ * Open the `/effort` picker: resolve the live model's advertised efforts
89
+ * and park `effortPicker` state — entries are the effort ids plus the
90
+ * trailing reserved `default` entry, focus on the live effort (the
91
+ * `default` entry when none is set or it is no longer in the list). Fail
92
+ * closed: an unresolved model emits the no-model notice and unresolvable
93
+ * levels emit a notice — never a fabricated list.
94
+ */
95
+ const openEffortPicker = async () => {
96
+ const route = rt.selection.current;
97
+ if (route === undefined) {
98
+ rt.emit(upsertRow(rt.state(), { kind: 'status', text: 'No model configured. Use /model first.' }));
99
+ return;
100
+ }
101
+ const efforts = await rt.resolveEfforts(route.provider, route.model);
102
+ if (efforts === undefined || efforts.length === 0) {
103
+ rt.emit(upsertRow(rt.state(), { kind: 'status', text: `Cannot resolve effort levels for ${route.model}.` }));
104
+ return;
105
+ }
106
+ const entries = [...efforts.map(level => level.id), 'default'];
107
+ const index = route.reasoningEffort === undefined ? -1 : entries.indexOf(route.reasoningEffort);
108
+ rt.emit(setEffortPicker(rt.state(), {
109
+ entries,
110
+ focused: index >= 0 ? index : entries.length - 1,
111
+ current: route.reasoningEffort,
112
+ }));
113
+ };
114
+ /**
115
+ * Open the `/permissions` picker: park the five CC rule-engine modes,
116
+ * focused on the live mode (row 0 when the live mode is not in the list).
117
+ * The overlay always opens — an unmounted engine surfaces as a host-command
118
+ * error on submit, matching the argued `/permissions <mode>` path.
119
+ */
120
+ const openPermissionPicker = () => {
121
+ const currentMode = rt.liveMode(rt.current.agent);
122
+ const index = PERMISSION_MODE_OPTIONS.findIndex(option => option.id === currentMode);
123
+ rt.emit(setPermissionPicker(rt.state(), {
124
+ entries: PERMISSION_MODE_OPTIONS,
125
+ focused: index >= 0 ? index : 0,
126
+ current: currentMode,
127
+ }));
128
+ };
129
+ return {
130
+ openModelPicker,
131
+ applyModelSwitch,
132
+ openEffortPicker,
133
+ openPermissionPicker,
134
+ modelPickerMove(delta) {
135
+ rt.emit(moveModelPickerFocus(rt.state(), delta));
136
+ },
137
+ modelPickerSubmit() {
138
+ const picker = rt.state().modelPicker;
139
+ if (picker === undefined)
140
+ return Promise.resolve();
141
+ const entry = picker.entries[picker.focused];
142
+ // Read-then-close: capture the focused entry BEFORE the synchronous
143
+ // close-emit so the overlay never lingers while validation runs.
144
+ rt.emit(setModelPicker(rt.state(), undefined));
145
+ if (entry === undefined)
146
+ return Promise.resolve();
147
+ // Effort-preserving switch with the stale-pair guard inside; the bare
148
+ // fast path writes synchronously, a carried effort continues detached.
149
+ return applyModelSwitch(entry.provider, entry.id);
150
+ },
151
+ modelPickerCancel() {
152
+ rt.emit(setModelPicker(rt.state(), undefined));
153
+ },
154
+ effortPickerMove(delta) {
155
+ rt.emit(moveEffortPickerFocus(rt.state(), delta));
156
+ },
157
+ async effortPickerSubmit() {
158
+ const picker = rt.state().effortPicker;
159
+ if (picker === undefined)
160
+ return;
161
+ const entry = picker.entries[picker.focused];
162
+ // Read-then-close (mirror modelPickerSubmit): capture the focused entry
163
+ // BEFORE the synchronous close-emit, then validate+write detached.
164
+ rt.emit(setEffortPicker(rt.state(), undefined));
165
+ if (entry === undefined)
166
+ return;
167
+ const captured = rt.selection.current;
168
+ if (captured === undefined) {
169
+ rt.emit(upsertRow(rt.state(), { kind: 'status', text: 'No model configured. Use /model first.' }));
170
+ return;
171
+ }
172
+ // The reserved `default` entry resets to the bare pair with zero
173
+ // validation (the provider default is always legal); the stale-pair
174
+ // guard still applies.
175
+ if (entry === 'default') {
176
+ if (rt.stalePair(captured))
177
+ return;
178
+ rt.selection.current = { provider: captured.provider, model: captured.model };
179
+ rt.emit(upsertRow(rt.state(), { kind: 'status', text: 'Reasoning effort reset to the provider default.' }));
180
+ return;
181
+ }
182
+ const efforts = await rt.resolveEfforts(captured.provider, captured.model);
183
+ // Stale-pair guard: the captured model must still be the live selection
184
+ // when the validation continuation resumes — a concurrent /model or
185
+ // switchSession re-seed in between refuses the write.
186
+ if (rt.stalePair(captured))
187
+ return;
188
+ const level = efforts?.find(candidate => candidate.id === entry);
189
+ if (level === undefined) {
190
+ rt.emit(upsertRow(rt.state(), { kind: 'status', text: `Cannot resolve effort levels for ${captured.model}.` }));
191
+ return;
192
+ }
193
+ rt.selection.current = {
194
+ provider: captured.provider,
195
+ model: captured.model,
196
+ reasoningEffort: ReasoningEffortId(level.id),
197
+ };
198
+ rt.emit(upsertRow(rt.state(), { kind: 'status', text: `Reasoning effort is now ${level.name}.` }));
199
+ },
200
+ effortPickerCancel() {
201
+ rt.emit(setEffortPicker(rt.state(), undefined));
202
+ },
203
+ permissionPickerMove(delta) {
204
+ rt.emit(movePermissionPickerFocus(rt.state(), delta));
205
+ },
206
+ async permissionPickerSubmit() {
207
+ const picker = rt.state().permissionPicker;
208
+ if (picker === undefined)
209
+ return;
210
+ const entry = picker.entries[picker.focused];
211
+ if (entry === undefined) {
212
+ rt.emit(setPermissionPicker(rt.state(), undefined));
213
+ return;
214
+ }
215
+ // bypassPermissions parks an in-overlay confirmation first; a second
216
+ // enter (or any other mode) closes then writes through the host command.
217
+ if (entry.id === BYPASS_MODE && picker.confirmingBypass !== true) {
218
+ rt.emit(setPermissionPicker(rt.state(), { ...picker, confirmingBypass: true }));
219
+ return;
220
+ }
221
+ rt.emit(setPermissionPicker(rt.state(), undefined));
222
+ await rt.runHarness(`/permissions ${entry.id}`);
223
+ },
224
+ permissionPickerCancel() {
225
+ const picker = rt.state().permissionPicker;
226
+ if (picker === undefined)
227
+ return;
228
+ if (picker.confirmingBypass === true) {
229
+ const { confirmingBypass: _dropped, ...rest } = picker;
230
+ rt.emit(setPermissionPicker(rt.state(), rest));
231
+ return;
232
+ }
233
+ rt.emit(setPermissionPicker(rt.state(), undefined));
234
+ },
235
+ async loadModelCatalog() {
236
+ return rt.loadCatalog();
237
+ },
238
+ async loadModelEfforts() {
239
+ const route = rt.selection.current;
240
+ if (route === undefined)
241
+ return [];
242
+ const efforts = await rt.resolveEfforts(route.provider, route.model);
243
+ if (efforts === undefined)
244
+ return [];
245
+ return [...efforts.map(level => level.id), 'default'];
246
+ },
247
+ };
248
+ }
249
+ //# sourceMappingURL=driver-pickers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver-pickers.js","sourceRoot":"","sources":["../../src/harness/driver-pickers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Ctrl+B promotion section (UX plan §3.4): promote every armed foreground
3
+ * subagent collect of the CURRENT session to background. Structural split of
4
+ * the same logic from `./driver.ts`.
5
+ * @module @dsh-cc/tui/harness/driver-promote
6
+ */
7
+ import type { Context } from '@deepseek-ai/cordis';
8
+ /**
9
+ * Create the promote handler for one driver instance. The collect path (Task
10
+ * tool) registers each in-flight foreground collect under parentSessionId +
11
+ * toolCallToken in the root-realm `ccCollectorRegistry` (published by the
12
+ * cc-subagent-task plugin, CcPlugins pattern); `promote()` on a handle
13
+ * releases the wait (`async_launched` + `backgroundedByUser: true` tool
14
+ * result) and leaves the child untouched — its settlement notice later
15
+ * delivers normally. Gated by the env kill switch; an absent registry or
16
+ * session change degrades to 0 (nothing promotable).
17
+ *
18
+ * The session filter compares against the LIVE agent's session id via the
19
+ * `current` holder, so a switch after construction promotes the new session's
20
+ * collects, exactly like the previous inline closure.
21
+ */
22
+ export declare function createPromoteSection(ctx: Context, current: {
23
+ agent: {
24
+ session: {
25
+ id: unknown;
26
+ };
27
+ };
28
+ }): () => number;
29
+ //# sourceMappingURL=driver-promote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver-promote.d.ts","sourceRoot":"","sources":["../../src/harness/driver-promote.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE;IAAE,KAAK,EAAE;QAAE,OAAO,EAAE;YAAE,EAAE,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE,CAAA;CAAE,GAC/C,MAAM,MAAM,CAYd"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Ctrl+B promotion section (UX plan §3.4): promote every armed foreground
3
+ * subagent collect of the CURRENT session to background. Structural split of
4
+ * the same logic from `./driver.ts`.
5
+ * @module @dsh-cc/tui/harness/driver-promote
6
+ */
7
+ import { backgroundTasksDisabled } from '@dsh-cc/subagent-task';
8
+ /**
9
+ * Create the promote handler for one driver instance. The collect path (Task
10
+ * tool) registers each in-flight foreground collect under parentSessionId +
11
+ * toolCallToken in the root-realm `ccCollectorRegistry` (published by the
12
+ * cc-subagent-task plugin, CcPlugins pattern); `promote()` on a handle
13
+ * releases the wait (`async_launched` + `backgroundedByUser: true` tool
14
+ * result) and leaves the child untouched — its settlement notice later
15
+ * delivers normally. Gated by the env kill switch; an absent registry or
16
+ * session change degrades to 0 (nothing promotable).
17
+ *
18
+ * The session filter compares against the LIVE agent's session id via the
19
+ * `current` holder, so a switch after construction promotes the new session's
20
+ * collects, exactly like the previous inline closure.
21
+ */
22
+ export function createPromoteSection(ctx, current) {
23
+ const promoteForegroundCollects = () => {
24
+ if (backgroundTasksDisabled())
25
+ return 0;
26
+ const registry = ctx.get('ccCollectorRegistry');
27
+ if (registry?.collectorsForSession === undefined)
28
+ return 0;
29
+ const armed = registry.collectorsForSession(String(current.agent.session.id));
30
+ for (const handle of armed)
31
+ handle.promote();
32
+ return armed.length;
33
+ };
34
+ return promoteForegroundCollects;
35
+ }
36
+ //# sourceMappingURL=driver-promote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver-promote.js","sourceRoot":"","sources":["../../src/harness/driver-promote.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAE/D;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,oBAAoB,CAClC,GAAY,EACZ,OAAgD;IAEhD,MAAM,yBAAyB,GAAG,GAAW,EAAE;QAC7C,IAAI,uBAAuB,EAAE;YAAE,OAAO,CAAC,CAAA;QACvC,MAAM,QAAQ,GAAI,GAAG,CAAC,GAAG,CAAC,qBAAqB,CAEjC,CAAA;QACd,IAAI,QAAQ,EAAE,oBAAoB,KAAK,SAAS;YAAE,OAAO,CAAC,CAAA;QAC1D,MAAM,KAAK,GAAG,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;QAC7E,KAAK,MAAM,MAAM,IAAI,KAAK;YAAE,MAAM,CAAC,OAAO,EAAE,CAAA;QAC5C,OAAO,KAAK,CAAC,MAAM,CAAA;IACrB,CAAC,CAAA;IACD,OAAO,yBAAyB,CAAA;AAClC,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Outbox queue, submit, and interrupt pipeline extracted from harness/driver.ts.
3
+ * Free-function collaborator: takes a {@link DriverQueueCtx} instead of closing
4
+ * over createDriver's locals, so the harness factory stays out of this leaf.
5
+ * @module @dsh-cc/tui/harness/driver-queue
6
+ */
7
+ import type { DriverQueueCtx } from './driver-ctx.ts';
8
+ /**
9
+ * Build the queue/sumit/interrupt section of createDriver. `rt` supplies live
10
+ * state, emit, and neighbor-section seams (bash/local/harness/permission picker).
11
+ */
12
+ export declare function createQueueSection(rt: DriverQueueCtx): {
13
+ flushQueue(): void;
14
+ steerQueued(): void;
15
+ recallQueued(): string | undefined;
16
+ submit(text?: string): Promise<void>;
17
+ interrupt(): void;
18
+ };
19
+ //# sourceMappingURL=driver-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver-queue.d.ts","sourceRoot":"","sources":["../../src/harness/driver-queue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AA2QrD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,GAAG;IACtD,UAAU,IAAI,IAAI,CAAA;IAClB,WAAW,IAAI,IAAI,CAAA;IACnB,YAAY,IAAI,MAAM,GAAG,SAAS,CAAA;IAClC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpC,SAAS,IAAI,IAAI,CAAA;CAClB,CAQA"}
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Outbox queue, submit, and interrupt pipeline extracted from harness/driver.ts.
3
+ * Free-function collaborator: takes a {@link DriverQueueCtx} instead of closing
4
+ * over createDriver's locals, so the harness factory stays out of this leaf.
5
+ * @module @dsh-cc/tui/harness/driver-queue
6
+ */
7
+ import { createUserMessage } from '@deepseek-ai/dsh-llm';
8
+ import { LOCAL_SLASH, parseSlash } from "../slash.js";
9
+ import { saveHistory } from "../history.js";
10
+ import { clearQueue, clearTurn, enqueue, popQueued, setBusy, setDraft, setTurnActive, upsertRow, } from "../store.js";
11
+ const asUserMessage = (text) => createUserMessage({
12
+ content: [{ type: 'text', text }],
13
+ source: { kind: 'user' },
14
+ });
15
+ /**
16
+ * Dispatch one queued outbox entry. A queued plugin command (`/codex:review
17
+ * args`) is re-classified here instead of being forwarded verbatim: flush and
18
+ * steer otherwise leak the raw slash line to the model as prompt text. A local
19
+ * name that is NOT a TUI-owned LOCAL_SLASH entry is a plugin command — it
20
+ * routes through the ccPlugins service. Anything else (plain prose, ordinary
21
+ * local names) sends as the original followup/steer, unchanged.
22
+ *
23
+ * Returns whether the entry was handed off to the agent (raw send or a
24
+ * successful plugin dispatch) — false means dropped with a notice. flushQueue
25
+ * uses this to avoid anchoring a busy turn nothing will arrive for.
26
+ */
27
+ const dispatchQueued = (rt, text, mode) => {
28
+ const send = (raw) => {
29
+ const message = asUserMessage(raw);
30
+ if (mode === 'followup')
31
+ rt.current.agent.followup(message);
32
+ else
33
+ rt.current.agent.steer(message);
34
+ return true;
35
+ };
36
+ const parsed = parseSlash(text);
37
+ if (parsed.kind !== 'local') {
38
+ return send(text);
39
+ }
40
+ if (LOCAL_SLASH.includes(parsed.name)) {
41
+ // Ordinary TUI-local names never enter the outbox while busy, but if one
42
+ // ever does, keep the historical behavior: forward the text verbatim.
43
+ return send(text);
44
+ }
45
+ // Plugin command. The raw-line fallback is reserved for names that are no
46
+ // longer in the plugin command table (uninstalled between enqueue and
47
+ // flush) or a vanished service — the same unknown-slash fall-through
48
+ // philosophy as the submit path. A name that IS still registered but fails
49
+ // ({ok:false} or reject) must never reach the model as raw slash text: it
50
+ // surfaces a notice instead and the queued line is dropped.
51
+ const plugins = rt.ctx.get('ccPlugins');
52
+ if (plugins === undefined || typeof plugins.runPluginCommand !== 'function') {
53
+ return send(text);
54
+ }
55
+ const { name, rawInput } = parsed;
56
+ // Live membership check against the CURRENT table, mirrored at failure
57
+ // time: a name that left the table degrades to the raw line; a name that is
58
+ // still registered keeps the line local.
59
+ const stillListed = () => {
60
+ if (typeof plugins.listPluginCommands !== 'function')
61
+ return true;
62
+ try {
63
+ return plugins.listPluginCommands().some(c => c.name.toLowerCase() === name);
64
+ }
65
+ catch {
66
+ return true; // undecidable → keep the line local, never raw-send
67
+ }
68
+ };
69
+ const failWithNotice = (reason) => {
70
+ rt.showNotice(`Plugin command /${name} failed: ${reason}`);
71
+ return false;
72
+ };
73
+ return plugins.runPluginCommand(name, { agent: rt.current.agent, rawInput })
74
+ .then((result) => {
75
+ if (!(result !== null && typeof result === 'object' && result.ok === false))
76
+ return true;
77
+ if (!stillListed())
78
+ return send(text);
79
+ const reason = result.reason ?? 'unknown error';
80
+ return failWithNotice(reason);
81
+ })
82
+ .catch((error) => {
83
+ if (!stillListed())
84
+ return send(text);
85
+ return failWithNotice(error instanceof Error ? error.message : String(error));
86
+ });
87
+ };
88
+ /**
89
+ * Outbox flush, anchored to the durable `turn/end` event: snapshot the queue,
90
+ * dispatch every entry FIFO through `followup`, and clear the queue in one
91
+ * atomic stroke — so the queue never holds an entry that was already sent and
92
+ * ↑ recall cannot race a flush. Busy is re-asserted optimistically (the
93
+ * flushed followups start a new turn immediately; the fold's `turn/end`
94
+ * handling just set it false), but only when at least one entry was handed
95
+ * off.
96
+ *
97
+ * The stroke is deferred until the agent converges to idle (agent.whenIdle,
98
+ * falling back to one microtask for hosts without it). Two hazards force the
99
+ * wait, both proven by live e2e:
100
+ *
101
+ * 1. The call sites are `session/event` observers running INSIDE the session
102
+ * append publication window; `followup` → `inbox.splice` appends
103
+ * `agent/inbox/spliced` synchronously and hits the session reentrancy
104
+ * guard ("session append cannot reenter while another append is being
105
+ * published").
106
+ * 2. A bare microtask lands in the driver teardown gap: kick()'s loop already
107
+ * made its last inbox claim but setPhase(idle) hasn't run, so wakeDriver
108
+ * takes the non-idle branch — which neither latches the wake (not an
109
+ * abort/maintenance) nor has a live driver to claim the work. The spliced
110
+ * message strands in the inbox and the UI sits on a zombie busy anchor.
111
+ * whenIdle resolves only after kick's finally sets the idle phase, where
112
+ * wakeDriver's idle path reliably starts the next driver.
113
+ */
114
+ const flushQueue = (rt) => {
115
+ const flush = () => {
116
+ const s = rt.state();
117
+ const pending = [...s.queued];
118
+ if (pending.length === 0)
119
+ return;
120
+ rt.emit(clearQueue(s));
121
+ const handedOff = pending.map(text => dispatchQueued(rt, text, 'followup'));
122
+ // Anchor the followup turn only once we know at least one entry was
123
+ // actually handed off: an all-dropped flush (e.g. a plugin command that
124
+ // failed with a notice) must not leave a zombie busy spinner behind.
125
+ void Promise.all(handedOff).then(results => {
126
+ if (!results.some(Boolean))
127
+ return;
128
+ rt.emit(setTurnActive(setBusy(rt.state(), true), { startedAt: Date.now(), outputBase: s.hud?.tokens?.output }));
129
+ });
130
+ };
131
+ // Await the agent that ENDED the turn (captured now); dispatchQueued reads
132
+ // rt.current.agent at fire time, so a session switch still targets the
133
+ // live session. A rejected whenIdle must not strand the queue.
134
+ const endingAgent = rt.current.agent;
135
+ if (typeof endingAgent.whenIdle === 'function') {
136
+ void endingAgent.whenIdle().then(flush, flush);
137
+ }
138
+ else {
139
+ queueMicrotask(flush);
140
+ }
141
+ };
142
+ /**
143
+ * Ctrl+S queue-jump: inject every queued entry into the RUNNING turn
144
+ * immediately — same synchronous snapshot-then-clear discipline as
145
+ * {@link flushQueue}, but via `agent.steer` and without a busy flip (the
146
+ * turn is already running).
147
+ */
148
+ const steerQueued = (rt) => {
149
+ const s = rt.state();
150
+ const pending = [...s.queued];
151
+ if (pending.length === 0)
152
+ return;
153
+ for (const text of pending) {
154
+ // Semantic trade-off: a queued plugin command reaches the running turn as
155
+ // a followup (queued work) rather than a steer — running it through the
156
+ // plugin seam is more important than steering semantics.
157
+ void dispatchQueued(rt, text, 'steer');
158
+ }
159
+ rt.emit(clearQueue(s));
160
+ };
161
+ /**
162
+ * Recall for editing: pop the most recent queued entry back out of the outbox
163
+ * and hand it to the caller (root.ts puts it into the composer). Race-free by
164
+ * construction — flush and steer always clear synchronously, so the queue only
165
+ * ever holds entries that were never sent.
166
+ */
167
+ const recallQueued = (rt) => {
168
+ const popped = popQueued(rt.state());
169
+ if (popped.text === undefined)
170
+ return undefined;
171
+ rt.emit(popped.state);
172
+ return popped.text;
173
+ };
174
+ const submit = async (rt, text) => {
175
+ const draft = text ?? rt.state().draft;
176
+ if (draft.trim().length === 0)
177
+ return;
178
+ rt.emit(setDraft(rt.state(), ''));
179
+ // A leading `!` marks a LOCAL shell command no matter how the text was
180
+ // entered — typed in shell mode or pasted wholesale. It runs even while
181
+ // the agent is busy (a local command never touches the turn) and is neither
182
+ // a prompt nor a slash command.
183
+ if (draft.startsWith('!')) {
184
+ await rt.runShellCommand(draft.slice(1));
185
+ return;
186
+ }
187
+ const parsed = parseSlash(draft);
188
+ if (parsed.kind === 'local') {
189
+ await rt.runLocal(parsed.name, parsed.rawInput);
190
+ return;
191
+ }
192
+ if (parsed.kind === 'harness') {
193
+ // Bare `/permissions` is the TUI analogue of the browser popupSelect
194
+ // decoration: open the overlay instead of dumping the rule listing.
195
+ // `/permissions <mode>` stays scriptable through the host command.
196
+ if (/^\/permissions$/i.test(parsed.line)) {
197
+ rt.openPermissionPicker();
198
+ return;
199
+ }
200
+ // An empty name segment (bare `/` or `/ `) is not a command, a skill,
201
+ // or a prompt worth a model turn.
202
+ const name = parsed.line.slice(1).split(/\s/, 1)[0] ?? '';
203
+ if (name.length === 0) {
204
+ rt.showNotice('Empty slash command.');
205
+ return;
206
+ }
207
+ const result = await rt.runHarness(parsed.line);
208
+ if (result !== undefined) {
209
+ // A result object (success or error) means a known command ran in the
210
+ // command plane; `null` means no command registry is mounted (runHarness
211
+ // already noticed). Neither becomes a prompt.
212
+ return;
213
+ }
214
+ // Unknown name: fall through to the prompt path below. This is the whole
215
+ // user-invocable-skill mechanism — the TUI never decides "is this a
216
+ // skill?"; the host's closed-set matching does. The followup message
217
+ // below MUST keep `source: { kind: 'user' }` because dsh-tool-skill's
218
+ // pre-step gesture boundary only scans `source.kind === 'user'` messages;
219
+ // if the name is a user-invocable skill it injects <skill_content>, and
220
+ // otherwise the line stays ordinary prose.
221
+ }
222
+ // W4 await-late seam: the boot seed (deployment default model) must be
223
+ // SETTLED before this turn is enqueued or dispatched — the harness
224
+ // snapshots `selection.current` at the start of prompt assembly
225
+ // (@deepseek-ai/dsh-agent model-selection.ts `system-prompt/assemble`),
226
+ // so dispatching earlier would run the turn with an undefined model.
227
+ // Local `!` shell lines and slash commands above already returned.
228
+ await rt.waitForModel();
229
+ // Persist the prompt (not slash commands — they are commands, not prompts,
230
+ // and would dilute the recall signal; an unknown slash IS a prompt and
231
+ // persists here via the fall-through above). Consecutive duplicates and the
232
+ // cap are handled inside saveHistory. This is also the first real-content
233
+ // signal: mark the session so the launcher can resume it.
234
+ rt.setHistory(saveHistory([...rt.getHistory(), draft], rt.historyDir));
235
+ rt.setMarkedContent(true);
236
+ rt.persistResumeTarget();
237
+ const s = rt.state();
238
+ if (s.busy) {
239
+ // Outbox: park the text as a pending chip only. It reaches the agent on
240
+ // the next durable `turn/end` (flushQueue) or immediately via Ctrl+S
241
+ // (steerQueued). No injection into the running turn here — that is what
242
+ // makes recall-then-edit meaningful.
243
+ rt.emit(enqueue(s, draft));
244
+ return;
245
+ }
246
+ // Idle sends bypass the outbox entirely — the row surfaces from the durable
247
+ // `user/message` event, and a sent text must not stay recallable.
248
+ rt.current.agent.followup(createUserMessage({
249
+ content: [{ type: 'text', text: draft }],
250
+ source: { kind: 'user' },
251
+ }));
252
+ // Anchor the working line at dispatch: elapsed counts from here, the token
253
+ // delta from the current HUD total (undefined when unseeded — the tokenUsage
254
+ // rebase pins it on the first change).
255
+ rt.emit(setTurnActive(setBusy(rt.state(), true), { startedAt: Date.now(), outputBase: s.hud?.tokens?.output }));
256
+ };
257
+ const interrupt = (rt) => {
258
+ rt.current.agent.cancel({ kind: 'user' });
259
+ // cancel discards queued/steering inbox items; mirror that in UI state.
260
+ // Clearing BEFORE the abort's turn/end lands also guarantees the flush
261
+ // anchor finds an empty queue — an interrupt never resurrects entries.
262
+ // The working-line anchor clears with the turn.
263
+ rt.emit(upsertRow(clearTurn(clearQueue(setBusy(rt.state(), false))), {
264
+ kind: 'status',
265
+ text: 'Interrupted by user.',
266
+ }));
267
+ };
268
+ /**
269
+ * Build the queue/sumit/interrupt section of createDriver. `rt` supplies live
270
+ * state, emit, and neighbor-section seams (bash/local/harness/permission picker).
271
+ */
272
+ export function createQueueSection(rt) {
273
+ return {
274
+ flushQueue: () => flushQueue(rt),
275
+ steerQueued: () => steerQueued(rt),
276
+ recallQueued: () => recallQueued(rt),
277
+ submit: (text) => submit(rt, text),
278
+ interrupt: () => interrupt(rt),
279
+ };
280
+ }
281
+ //# sourceMappingURL=driver-queue.js.map