@zergai/ztc 0.4.0-beta.1 → 0.5.0-beta.1

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 (718) hide show
  1. package/README.md +398 -45
  2. package/RUST_THIRD_PARTY_LICENSES.txt +1080 -822
  3. package/THIRD_PARTY_NOTICES.md +273 -12
  4. package/bin/zmux-darwin-arm64 +0 -0
  5. package/bin/ztc-audio-darwin-arm64 +0 -0
  6. package/bin/ztc-capture-darwin-arm64 +0 -0
  7. package/bin/ztc-computer-darwin-arm64 +0 -0
  8. package/bin/ztc-llm-darwin-arm64 +0 -0
  9. package/bin/ztc-tts-darwin-arm64 +0 -0
  10. package/bin/ztc-tui-darwin-arm64 +0 -0
  11. package/bin/ztc-zlicky-overlay-darwin-arm64 +0 -0
  12. package/bin/zterm-darwin-arm64 +0 -0
  13. package/dist/App.js +1206 -209
  14. package/dist/agent/agent.d.ts +74 -8
  15. package/dist/agent/agent.js +1073 -184
  16. package/dist/agent/backends/account_rotation.d.ts +4 -0
  17. package/dist/agent/backends/account_rotation.js +8 -2
  18. package/dist/agent/backends/anthropic.d.ts +13 -0
  19. package/dist/agent/backends/anthropic.js +169 -28
  20. package/dist/agent/backends/claude_cli.js +18 -8
  21. package/dist/agent/backends/codex_app_server.d.ts +1 -0
  22. package/dist/agent/backends/codex_app_server.js +55 -3
  23. package/dist/agent/backends/codex_auth.d.ts +1 -0
  24. package/dist/agent/backends/codex_auth.js +67 -13
  25. package/dist/agent/backends/codex_buffered_recovery.d.ts +27 -0
  26. package/dist/agent/backends/codex_buffered_recovery.js +293 -0
  27. package/dist/agent/backends/gemini.js +3 -1
  28. package/dist/agent/backends/http_error.d.ts +26 -0
  29. package/dist/agent/backends/http_error.js +221 -0
  30. package/dist/agent/backends/index.d.ts +9 -2
  31. package/dist/agent/backends/index.js +22 -2
  32. package/dist/agent/backends/openai.d.ts +0 -6
  33. package/dist/agent/backends/openai.js +5 -2
  34. package/dist/agent/backends/openai_compatible.d.ts +5 -4
  35. package/dist/agent/backends/openai_compatible.js +26 -19
  36. package/dist/agent/backends/openai_responses.d.ts +8 -3
  37. package/dist/agent/backends/openai_responses.js +77 -33
  38. package/dist/agent/backends/provider_ids.d.ts +1 -0
  39. package/dist/agent/backends/provider_ids.js +4 -0
  40. package/dist/agent/backends/request_metrics.js +12 -2
  41. package/dist/agent/backends/types.d.ts +109 -6
  42. package/dist/agent/backends/types.js +5 -1
  43. package/dist/agent/backends/zrouter_stream.d.ts +6 -1
  44. package/dist/agent/backends/zrouter_stream.js +160 -42
  45. package/dist/agent/background_registry.d.ts +2 -0
  46. package/dist/agent/background_registry.js +1 -0
  47. package/dist/agent/claim_observation.d.ts +6 -0
  48. package/dist/agent/claim_observation.js +54 -0
  49. package/dist/agent/commands/accounts.js +46 -177
  50. package/dist/agent/commands/attention.d.ts +2 -0
  51. package/dist/agent/commands/attention.js +67 -0
  52. package/dist/agent/commands/auth.js +18 -2
  53. package/dist/agent/commands/board.d.ts +2 -0
  54. package/dist/agent/commands/board.js +169 -0
  55. package/dist/agent/commands/clipboard.js +50 -1
  56. package/dist/agent/commands/compact.js +21 -13
  57. package/dist/agent/commands/config.js +4 -1
  58. package/dist/agent/commands/context.js +6 -1
  59. package/dist/agent/commands/desktop.d.ts +2 -0
  60. package/dist/agent/commands/desktop.js +29 -0
  61. package/dist/agent/commands/effort.js +14 -6
  62. package/dist/agent/commands/environment.d.ts +3 -0
  63. package/dist/agent/commands/environment.js +109 -0
  64. package/dist/agent/commands/evidence.d.ts +2 -0
  65. package/dist/agent/commands/evidence.js +9 -0
  66. package/dist/agent/commands/fork.js +4 -3
  67. package/dist/agent/commands/goal.d.ts +33 -1
  68. package/dist/agent/commands/goal.js +174 -14
  69. package/dist/agent/commands/goal_exit.d.ts +36 -0
  70. package/dist/agent/commands/goal_exit.js +120 -0
  71. package/dist/agent/commands/help.js +3 -1
  72. package/dist/agent/commands/history.js +3 -1
  73. package/dist/agent/commands/inbox.d.ts +2 -0
  74. package/dist/agent/commands/inbox.js +148 -0
  75. package/dist/agent/commands/index.js +16 -1
  76. package/dist/agent/commands/input_mode.js +8 -6
  77. package/dist/agent/commands/job.d.ts +2 -0
  78. package/dist/agent/commands/job.js +93 -0
  79. package/dist/agent/commands/keybindings.js +4 -1
  80. package/dist/agent/commands/label.d.ts +2 -0
  81. package/dist/agent/commands/label.js +77 -0
  82. package/dist/agent/commands/layout.d.ts +2 -0
  83. package/dist/agent/commands/layout.js +20 -0
  84. package/dist/agent/commands/mirror.js +16 -1
  85. package/dist/agent/commands/model.js +12 -2
  86. package/dist/agent/commands/permissions.d.ts +3 -1
  87. package/dist/agent/commands/permissions.js +79 -89
  88. package/dist/agent/commands/plan.js +5 -15
  89. package/dist/agent/commands/profile.js +21 -2
  90. package/dist/agent/commands/provider_selection.js +20 -1
  91. package/dist/agent/commands/rename.js +1 -1
  92. package/dist/agent/commands/resources.d.ts +2 -0
  93. package/dist/agent/commands/resources.js +67 -0
  94. package/dist/agent/commands/rewind.d.ts +3 -0
  95. package/dist/agent/commands/rewind.js +16 -0
  96. package/dist/agent/commands/runbooks.d.ts +2 -0
  97. package/dist/agent/commands/runbooks.js +73 -0
  98. package/dist/agent/commands/runbooks_lazy.d.ts +2 -0
  99. package/dist/agent/commands/runbooks_lazy.js +8 -0
  100. package/dist/agent/commands/sessions.d.ts +1 -1
  101. package/dist/agent/commands/sessions.js +33 -5
  102. package/dist/agent/commands/settings.js +185 -39
  103. package/dist/agent/commands/setup.d.ts +2 -0
  104. package/dist/agent/commands/setup.js +78 -0
  105. package/dist/agent/commands/share.d.ts +2 -0
  106. package/dist/agent/commands/share.js +64 -0
  107. package/dist/agent/commands/status.js +14 -2
  108. package/dist/agent/commands/types.d.ts +99 -6
  109. package/dist/agent/commands/update.js +8 -0
  110. package/dist/agent/commands/usage.d.ts +2 -0
  111. package/dist/agent/commands/usage.js +8 -0
  112. package/dist/agent/factory.d.ts +11 -0
  113. package/dist/agent/factory.js +113 -88
  114. package/dist/agent/loop_policy.d.ts +6 -3
  115. package/dist/agent/loop_policy.js +12 -3
  116. package/dist/agent/measurement_guidance.d.ts +2 -0
  117. package/dist/agent/measurement_guidance.js +5 -0
  118. package/dist/agent/request_encoder.d.ts +8 -1
  119. package/dist/agent/request_encoder.js +95 -23
  120. package/dist/agent/runtime/benchmark_comparison.d.ts +30 -0
  121. package/dist/agent/runtime/benchmark_comparison.js +94 -0
  122. package/dist/agent/runtime/benchmark_contract.d.ts +48 -0
  123. package/dist/agent/runtime/benchmark_contract.js +90 -0
  124. package/dist/agent/runtime/checked_commands.d.ts +32 -0
  125. package/dist/agent/runtime/checked_commands.js +119 -0
  126. package/dist/agent/runtime/command_execution_policy.d.ts +26 -0
  127. package/dist/agent/runtime/command_execution_policy.js +91 -0
  128. package/dist/agent/runtime/command_jobs.d.ts +36 -18
  129. package/dist/agent/runtime/command_jobs.js +151 -13
  130. package/dist/agent/runtime/command_panes.d.ts +59 -0
  131. package/dist/agent/runtime/command_panes.js +398 -0
  132. package/dist/agent/runtime/command_wait.d.ts +19 -0
  133. package/dist/agent/runtime/command_wait.js +167 -0
  134. package/dist/agent/runtime/cpu_benchmark.d.ts +30 -0
  135. package/dist/agent/runtime/cpu_benchmark.js +126 -0
  136. package/dist/agent/runtime/delegated_evidence.d.ts +26 -0
  137. package/dist/agent/runtime/delegated_evidence.js +80 -0
  138. package/dist/agent/runtime/evidence.d.ts +127 -0
  139. package/dist/agent/runtime/evidence.js +468 -0
  140. package/dist/agent/runtime/execution_context.d.ts +7 -0
  141. package/dist/agent/runtime/execution_context.js +39 -3
  142. package/dist/agent/runtime/json_verifier_recipe.d.ts +8 -0
  143. package/dist/agent/runtime/json_verifier_recipe.js +66 -0
  144. package/dist/agent/runtime/measurements.d.ts +110 -0
  145. package/dist/agent/runtime/measurements.js +317 -0
  146. package/dist/agent/runtime/policy.d.ts +4 -0
  147. package/dist/agent/runtime/policy.js +31 -20
  148. package/dist/agent/runtime/provider_native_evidence.d.ts +20 -0
  149. package/dist/agent/runtime/provider_native_evidence.js +86 -0
  150. package/dist/agent/runtime/recovery.d.ts +2 -0
  151. package/dist/agent/runtime/recovery.js +45 -2
  152. package/dist/agent/runtime/remote_broker_transport.d.ts +10 -0
  153. package/dist/agent/runtime/remote_broker_transport.js +84 -0
  154. package/dist/agent/runtime/remote_deletion_receipts.d.ts +3 -0
  155. package/dist/agent/runtime/remote_deletion_receipts.js +43 -0
  156. package/dist/agent/runtime/remote_journal_replay.d.ts +13 -0
  157. package/dist/agent/runtime/remote_journal_replay.js +233 -0
  158. package/dist/agent/runtime/remote_mutation_journal.d.ts +25 -0
  159. package/dist/agent/runtime/remote_mutation_journal.js +208 -0
  160. package/dist/agent/runtime/remote_resource_endpoint.d.ts +7 -0
  161. package/dist/agent/runtime/remote_resource_endpoint.js +6 -0
  162. package/dist/agent/runtime/remote_resource_errors.d.ts +5 -0
  163. package/dist/agent/runtime/remote_resource_errors.js +9 -0
  164. package/dist/agent/runtime/remote_resource_script.d.ts +2 -0
  165. package/dist/agent/runtime/remote_resource_script.js +1009 -0
  166. package/dist/agent/runtime/remote_resources.d.ts +29 -0
  167. package/dist/agent/runtime/remote_resources.js +160 -0
  168. package/dist/agent/runtime/source_verification.d.ts +12 -0
  169. package/dist/agent/runtime/source_verification.js +210 -0
  170. package/dist/agent/runtime/source_verification_contract.d.ts +75 -0
  171. package/dist/agent/runtime/source_verification_contract.js +203 -0
  172. package/dist/agent/runtime/tool_checkpoints.d.ts +11 -0
  173. package/dist/agent/runtime/tool_checkpoints.js +26 -0
  174. package/dist/agent/runtime/tool_effects.d.ts +5 -0
  175. package/dist/agent/runtime/tool_progress.d.ts +12 -0
  176. package/dist/agent/runtime/tool_progress.js +76 -0
  177. package/dist/agent/runtime/tool_runtime.d.ts +16 -1
  178. package/dist/agent/runtime/tool_runtime.js +204 -10
  179. package/dist/agent/runtime/tracing.d.ts +1 -1
  180. package/dist/agent/runtime/verifier_capture.d.ts +9 -0
  181. package/dist/agent/runtime/verifier_capture.js +100 -0
  182. package/dist/agent/runtime/verifier_contract.d.ts +24 -0
  183. package/dist/agent/runtime/verifier_contract.js +41 -0
  184. package/dist/agent/subagent.d.ts +20 -10
  185. package/dist/agent/subagent.js +266 -29
  186. package/dist/agent/tool_response_directory.d.ts +24 -0
  187. package/dist/agent/tool_response_directory.js +61 -0
  188. package/dist/agent/tool_response_source.d.ts +8 -0
  189. package/dist/agent/tool_response_source.js +49 -0
  190. package/dist/agent/tool_response_store.d.ts +75 -4
  191. package/dist/agent/tool_response_store.js +583 -64
  192. package/dist/agent/tools/ask_user.d.ts +3 -1
  193. package/dist/agent/tools/ask_user.js +30 -11
  194. package/dist/agent/tools/background.js +1 -0
  195. package/dist/agent/tools/benchmark_cpu.d.ts +2 -0
  196. package/dist/agent/tools/benchmark_cpu.js +102 -0
  197. package/dist/agent/tools/compare_benchmarks.d.ts +2 -0
  198. package/dist/agent/tools/compare_benchmarks.js +53 -0
  199. package/dist/agent/tools/deferred.js +1 -0
  200. package/dist/agent/tools/delegate.js +18 -2
  201. package/dist/agent/tools/helper_requirements.d.ts +7 -0
  202. package/dist/agent/tools/helper_requirements.js +19 -0
  203. package/dist/agent/tools/index.d.ts +1 -0
  204. package/dist/agent/tools/index.js +3 -0
  205. package/dist/agent/tools/measurement_report.d.ts +22 -0
  206. package/dist/agent/tools/measurement_report.js +190 -0
  207. package/dist/agent/tools/pane.d.ts +0 -7
  208. package/dist/agent/tools/pane.js +5 -4
  209. package/dist/agent/tools/project_context.d.ts +3 -0
  210. package/dist/agent/tools/project_context.js +45 -0
  211. package/dist/agent/tools/recall_tool_response.d.ts +3 -5
  212. package/dist/agent/tools/recall_tool_response.js +19 -29
  213. package/dist/agent/tools/registry.d.ts +1 -0
  214. package/dist/agent/tools/registry.js +23 -0
  215. package/dist/agent/tools/related_sessions.d.ts +2 -0
  216. package/dist/agent/tools/related_sessions.js +156 -0
  217. package/dist/agent/tools/routine.js +13 -3
  218. package/dist/agent/tools/session_evidence.d.ts +3 -0
  219. package/dist/agent/tools/session_evidence.js +72 -0
  220. package/dist/agent/tools/shell.d.ts +4 -3
  221. package/dist/agent/tools/shell.js +174 -47
  222. package/dist/agent/tools/tool_search.js +3 -2
  223. package/dist/agent/tools/types.d.ts +28 -0
  224. package/dist/agent/tools/verify_artifact.d.ts +2 -0
  225. package/dist/agent/tools/verify_artifact.js +60 -0
  226. package/dist/agent/tools/workflow.js +1 -0
  227. package/dist/app/activity.d.ts +6 -1
  228. package/dist/app/activity.js +81 -12
  229. package/dist/app/agent_runner.d.ts +6 -1
  230. package/dist/app/agent_runner.js +34 -19
  231. package/dist/app/command_context.js +106 -10
  232. package/dist/app/controller.d.ts +15 -4
  233. package/dist/app/controller.js +29 -3
  234. package/dist/app/escape_stop.d.ts +11 -0
  235. package/dist/app/escape_stop.js +15 -0
  236. package/dist/app/liveness.d.ts +6 -4
  237. package/dist/app/liveness.js +13 -3
  238. package/dist/app/model_catalog_cache.d.ts +5 -0
  239. package/dist/app/model_catalog_cache.js +138 -0
  240. package/dist/app/ordered_transcript.js +43 -4
  241. package/dist/app/prompt_turn.d.ts +14 -0
  242. package/dist/app/prompt_turn.js +53 -0
  243. package/dist/app/recap.d.ts +3 -0
  244. package/dist/app/recap.js +9 -3
  245. package/dist/app/restart.d.ts +3 -2
  246. package/dist/app/restart.js +22 -7
  247. package/dist/app/rewind.d.ts +3 -3
  248. package/dist/app/rewind.js +3 -3
  249. package/dist/app/selection_completion.d.ts +17 -2
  250. package/dist/app/selection_completion.js +84 -9
  251. package/dist/app/session_manager.d.ts +38 -6
  252. package/dist/app/session_manager.js +263 -60
  253. package/dist/app/session_messages.d.ts +3 -0
  254. package/dist/app/session_messages.js +60 -0
  255. package/dist/app/task_scope_router.d.ts +20 -0
  256. package/dist/app/task_scope_router.js +173 -0
  257. package/dist/app/transcript_window.d.ts +2 -0
  258. package/dist/app/transcript_window.js +4 -0
  259. package/dist/app/workspace_admission_details.d.ts +3 -0
  260. package/dist/app/workspace_admission_details.js +26 -0
  261. package/dist/app/workspace_layout_controller.d.ts +11 -0
  262. package/dist/app/workspace_layout_controller.js +88 -0
  263. package/dist/app/workspace_monitor.d.ts +1 -0
  264. package/dist/app/workspace_monitor.js +216 -0
  265. package/dist/app/workspace_presets.d.ts +29 -0
  266. package/dist/app/workspace_presets.js +192 -0
  267. package/dist/attention/queue.d.ts +37 -0
  268. package/dist/attention/queue.js +183 -0
  269. package/dist/bench/rf_shared.d.ts +2 -0
  270. package/dist/board/binding.d.ts +42 -0
  271. package/dist/board/binding.js +38 -0
  272. package/dist/board/client.d.ts +42 -0
  273. package/dist/board/client.js +60 -0
  274. package/dist/cli.js +1 -1
  275. package/dist/cli_args.js +19 -17
  276. package/dist/cli_main.js +110 -13
  277. package/dist/cli_ratatui.d.ts +183 -16
  278. package/dist/cli_ratatui.js +3989 -1255
  279. package/dist/components/ActivityLine.d.ts +3 -2
  280. package/dist/components/ActivityLine.js +2 -2
  281. package/dist/components/InputArea.d.ts +19 -1
  282. package/dist/components/InputArea.js +264 -190
  283. package/dist/components/MessageList.d.ts +2 -0
  284. package/dist/components/MessageList.js +3 -1
  285. package/dist/components/SingleMessage.d.ts +2 -0
  286. package/dist/components/SingleMessage.js +4 -2
  287. package/dist/components/StatusBar.d.ts +4 -2
  288. package/dist/components/StatusBar.js +3 -1
  289. package/dist/conductor_branch.js +3 -0
  290. package/dist/config/api_retry.d.ts +6 -1
  291. package/dist/config/api_retry.js +19 -1
  292. package/dist/config/defaults.d.ts +2 -1
  293. package/dist/config/defaults.js +9 -2
  294. package/dist/config/message_display.d.ts +1 -0
  295. package/dist/config/message_display.js +17 -2
  296. package/dist/config/permissions.d.ts +21 -0
  297. package/dist/config/permissions.js +89 -0
  298. package/dist/config/resolved.d.ts +2 -2
  299. package/dist/config/types.d.ts +32 -5
  300. package/dist/config.d.ts +38 -4
  301. package/dist/config.js +290 -42
  302. package/dist/context/project_intelligence.d.ts +44 -0
  303. package/dist/context/project_intelligence.js +246 -0
  304. package/dist/desktop/cli_launch.d.ts +8 -0
  305. package/dist/desktop/cli_launch.js +6 -0
  306. package/dist/desktop/cli_shim.d.ts +8 -0
  307. package/dist/desktop/cli_shim.js +120 -0
  308. package/dist/desktop/runtime.d.ts +16 -0
  309. package/dist/desktop/runtime.js +161 -0
  310. package/dist/desktop/worker_launch.d.ts +7 -0
  311. package/dist/desktop/worker_launch.js +7 -0
  312. package/dist/diagnostics/cargo_mutants_selection.d.ts +27 -0
  313. package/dist/diagnostics/cargo_mutants_selection.js +48 -0
  314. package/dist/diagnostics/native_terminal_benchmark.d.ts +58 -0
  315. package/dist/diagnostics/native_terminal_benchmark.js +1247 -0
  316. package/dist/diagnostics/session_evidence_package.d.ts +40 -0
  317. package/dist/diagnostics/session_evidence_package.js +234 -0
  318. package/dist/diagnostics/session_export.d.ts +93 -0
  319. package/dist/diagnostics/session_export.js +634 -0
  320. package/dist/emulation/tool_adapters.js +48 -18
  321. package/dist/emulation/ztc_addendum.d.ts +1 -0
  322. package/dist/emulation/ztc_addendum.js +13 -12
  323. package/dist/environment/service_runner.d.ts +1 -0
  324. package/dist/environment/service_runner.js +68 -0
  325. package/dist/environment/store.d.ts +120 -0
  326. package/dist/environment/store.js +387 -0
  327. package/dist/events.d.ts +40 -1
  328. package/dist/events.js +10 -17
  329. package/dist/goal_contract.d.ts +25 -0
  330. package/dist/goal_contract.js +44 -0
  331. package/dist/headless.d.ts +31 -0
  332. package/dist/headless.js +578 -70
  333. package/dist/headless_cli.js +8 -0
  334. package/dist/headless_cli_args.d.ts +1 -0
  335. package/dist/headless_cli_args.js +1 -0
  336. package/dist/intake/cli.d.ts +1 -0
  337. package/dist/intake/cli.js +33 -0
  338. package/dist/intake/format.d.ts +2 -0
  339. package/dist/intake/format.js +14 -0
  340. package/dist/intake/notifications.d.ts +16 -0
  341. package/dist/intake/notifications.js +132 -0
  342. package/dist/intake/store.d.ts +56 -0
  343. package/dist/intake/store.js +238 -0
  344. package/dist/library.d.ts +8 -0
  345. package/dist/library.js +19 -10
  346. package/dist/mcp/client.d.ts +1 -0
  347. package/dist/mcp/client.js +20 -0
  348. package/dist/modes/builtin/plan.yaml +2 -2
  349. package/dist/modes/handlers.js +1 -1
  350. package/dist/onboarding/first_run.d.ts +3 -0
  351. package/dist/onboarding/first_run.js +33 -25
  352. package/dist/onboarding/setup.d.ts +32 -0
  353. package/dist/onboarding/setup.js +133 -0
  354. package/dist/onboarding/setup_agent_health.d.ts +17 -0
  355. package/dist/onboarding/setup_agent_health.js +79 -0
  356. package/dist/onboarding/setup_environment.d.ts +66 -0
  357. package/dist/onboarding/setup_environment.js +219 -0
  358. package/dist/onboarding/setup_workspace.d.ts +17 -0
  359. package/dist/onboarding/setup_workspace.js +129 -0
  360. package/dist/optimize/adapters/index.d.ts +4 -1
  361. package/dist/optimize/adapters/index.js +7 -0
  362. package/dist/optimize/adapters/kernelbench.js +14 -5
  363. package/dist/optimize/adapters/popcorn.js +3 -2
  364. package/dist/optimize/adapters/profile_benchmark.js +13 -7
  365. package/dist/optimize/adapters/rfbench.js +25 -14
  366. package/dist/optimize/adapters/types.d.ts +3 -0
  367. package/dist/optimize/adapters/zergpod.js +5 -2
  368. package/dist/optimize/baseline.js +10 -21
  369. package/dist/optimize/branch.js +9 -12
  370. package/dist/optimize/combine.js +2 -3
  371. package/dist/optimize/executor.js +34 -48
  372. package/dist/optimize/measurements.d.ts +55 -0
  373. package/dist/optimize/measurements.js +173 -0
  374. package/dist/optimize/results.d.ts +1 -0
  375. package/dist/optimize/results.js +11 -33
  376. package/dist/optimize/tge.js +2 -1
  377. package/dist/panes/initial_input.d.ts +19 -0
  378. package/dist/panes/initial_input.js +206 -0
  379. package/dist/panes/labels.d.ts +16 -0
  380. package/dist/panes/labels.js +42 -0
  381. package/dist/panes/model_catalog.d.ts +3 -3
  382. package/dist/panes/model_catalog.js +28 -4
  383. package/dist/panes/pane_registry.js +6 -2
  384. package/dist/panes/types.d.ts +3 -0
  385. package/dist/panes/ztc_launch.js +19 -0
  386. package/dist/plugin/optimization.d.ts +2 -0
  387. package/dist/plugin/optimization.js +1 -0
  388. package/dist/plugin/runtime.d.ts +2 -0
  389. package/dist/plugin/simulation.d.ts +6 -1
  390. package/dist/plugin/simulation_contract.d.ts +836 -1
  391. package/dist/plugin/simulation_documents.d.ts +22 -0
  392. package/dist/plugin/simulation_documents.js +1 -0
  393. package/dist/plugin/simulation_interventions.d.ts +103 -0
  394. package/dist/plugin/simulation_interventions.js +1 -0
  395. package/dist/plugin/simulation_living.d.ts +1 -0
  396. package/dist/plugin/simulation_living.js +3 -0
  397. package/dist/plugin/simulation_meetings.d.ts +121 -0
  398. package/dist/plugin/simulation_meetings.js +1 -0
  399. package/dist/plugin/simulation_story_review.d.ts +111 -0
  400. package/dist/plugin/simulation_story_review.js +1 -0
  401. package/dist/plugin/simulation_storyteller.d.ts +91 -0
  402. package/dist/plugin/simulation_storyteller.js +1 -0
  403. package/dist/plugin/simulation_structured.d.ts +2 -0
  404. package/dist/plugin/simulation_structured.js +2 -0
  405. package/dist/plugin/simulation_toolkit.d.ts +21 -0
  406. package/dist/plugin/simulation_toolkit.js +24 -0
  407. package/dist/prompts/tool_delegate_task.j2 +1 -1
  408. package/dist/remote-broker/Dockerfile +10 -0
  409. package/dist/remote-broker/archive.py +1227 -0
  410. package/dist/remote-broker/cutover.py +761 -0
  411. package/dist/remote-broker/requirements.txt +5 -0
  412. package/dist/remote-broker/resources.json +6 -0
  413. package/dist/remote-broker/resources.write-staging.example.json +18 -0
  414. package/dist/remote-broker/retained.py +686 -0
  415. package/dist/remote-broker/retained_cleanup.py +550 -0
  416. package/dist/remote-broker/server.py +648 -0
  417. package/dist/remote-broker/worker.py +1007 -0
  418. package/dist/router_auth.d.ts +31 -0
  419. package/dist/router_auth.js +155 -9
  420. package/dist/runbooks/catalog.d.ts +3 -0
  421. package/dist/runbooks/catalog.js +26 -0
  422. package/dist/runbooks/library.d.ts +11 -0
  423. package/dist/runbooks/library.js +95 -0
  424. package/dist/runbooks/package.d.ts +43 -0
  425. package/dist/runbooks/package.js +102 -0
  426. package/dist/session.d.ts +65 -13
  427. package/dist/session.js +548 -43
  428. package/dist/session_handoff.d.ts +25 -0
  429. package/dist/session_handoff.js +322 -0
  430. package/dist/session_handoff_contract.d.ts +89 -0
  431. package/dist/session_handoff_contract.js +179 -0
  432. package/dist/session_media.d.ts +41 -0
  433. package/dist/session_media.js +357 -0
  434. package/dist/session_ownership.d.ts +16 -0
  435. package/dist/session_ownership.js +221 -0
  436. package/dist/session_public.d.ts +10 -2
  437. package/dist/session_public.js +6 -1
  438. package/dist/simulation/dynamic_history.d.ts +42 -0
  439. package/dist/simulation/dynamic_history.js +173 -0
  440. package/dist/simulation/engines/swarm/pane_manager.js +7 -6
  441. package/dist/simulation/inference.d.ts +3 -1
  442. package/dist/simulation/inference.js +19 -1
  443. package/dist/simulation/intervention_generation.d.ts +27 -0
  444. package/dist/simulation/intervention_generation.js +246 -0
  445. package/dist/simulation/intervention_material.d.ts +4 -0
  446. package/dist/simulation/intervention_material.js +81 -0
  447. package/dist/simulation/living/context.d.ts +3130 -0
  448. package/dist/simulation/living/context.js +944 -0
  449. package/dist/simulation/living/definer.d.ts +12 -0
  450. package/dist/simulation/living/definer.js +223 -0
  451. package/dist/simulation/living/definition.d.ts +257 -0
  452. package/dist/simulation/living/definition.js +341 -0
  453. package/dist/simulation/living/frame.d.ts +259 -0
  454. package/dist/simulation/living/frame.js +223 -0
  455. package/dist/simulation/living/identifiers.d.ts +2 -0
  456. package/dist/simulation/living/identifiers.js +12 -0
  457. package/dist/simulation/living/index.d.ts +15 -0
  458. package/dist/simulation/living/index.js +15 -0
  459. package/dist/simulation/living/inference.d.ts +219 -0
  460. package/dist/simulation/living/inference.js +576 -0
  461. package/dist/simulation/living/mechanics.d.ts +54 -0
  462. package/dist/simulation/living/mechanics.js +588 -0
  463. package/dist/simulation/living/model-input.d.ts +25 -0
  464. package/dist/simulation/living/model-input.js +215 -0
  465. package/dist/simulation/living/open-world.d.ts +257 -0
  466. package/dist/simulation/living/open-world.js +102 -0
  467. package/dist/simulation/living/projection.d.ts +7 -0
  468. package/dist/simulation/living/projection.js +31 -0
  469. package/dist/simulation/living/recall.d.ts +17 -0
  470. package/dist/simulation/living/recall.js +25 -0
  471. package/dist/simulation/living/reduce.d.ts +15 -0
  472. package/dist/simulation/living/reduce.js +1853 -0
  473. package/dist/simulation/living/replay.d.ts +6 -0
  474. package/dist/simulation/living/replay.js +22 -0
  475. package/dist/simulation/living/runtime.d.ts +140 -0
  476. package/dist/simulation/living/runtime.js +3020 -0
  477. package/dist/simulation/living/shared-kinds.d.ts +24 -0
  478. package/dist/simulation/living/shared-kinds.js +96 -0
  479. package/dist/simulation/living/types.d.ts +2785 -0
  480. package/dist/simulation/living/types.js +669 -0
  481. package/dist/simulation/llm.d.ts +3 -1
  482. package/dist/simulation/llm.js +90 -30
  483. package/dist/simulation/memory/embedding.d.ts +1 -1
  484. package/dist/simulation/memory/embedding.js +9 -3
  485. package/dist/simulation/memory/filesystem.d.ts +2 -0
  486. package/dist/simulation/memory/filesystem.js +5 -1
  487. package/dist/simulation/memory/filesystem_file_tree.d.ts +4 -1
  488. package/dist/simulation/memory/filesystem_file_tree.js +41 -10
  489. package/dist/simulation/memory/filesystem_invariants.js +1 -2
  490. package/dist/simulation/memory/filesystem_types.d.ts +1 -0
  491. package/dist/simulation/memory/memory.d.ts +2 -0
  492. package/dist/simulation/memory/memory.js +5 -1
  493. package/dist/simulation/orchestration/intent.js +0 -2
  494. package/dist/simulation/orchestration/lifecycle.js +0 -2
  495. package/dist/simulation/plugin_host.d.ts +25 -1
  496. package/dist/simulation/plugin_host.js +334 -17
  497. package/dist/simulation/session_documents.d.ts +20 -0
  498. package/dist/simulation/session_documents.js +62 -0
  499. package/dist/simulation/session_input_validation.d.ts +43 -0
  500. package/dist/simulation/session_input_validation.js +364 -0
  501. package/dist/simulation/session_inputs.d.ts +32 -0
  502. package/dist/simulation/session_inputs.js +154 -0
  503. package/dist/simulation/telemetry.d.ts +11 -14
  504. package/dist/simulation/telemetry.js +177 -54
  505. package/dist/simulation/toolkit/bounded_turns.d.ts +13 -0
  506. package/dist/simulation/toolkit/bounded_turns.js +80 -0
  507. package/dist/simulation/toolkit/dynamic_agents.d.ts +25 -0
  508. package/dist/simulation/toolkit/dynamic_agents.js +218 -0
  509. package/dist/simulation/toolkit/dynamic_digest.d.ts +1 -0
  510. package/dist/simulation/toolkit/dynamic_digest.js +8 -0
  511. package/dist/simulation/toolkit/dynamic_documents.d.ts +7 -0
  512. package/dist/simulation/toolkit/dynamic_documents.js +71 -0
  513. package/dist/simulation/toolkit/dynamic_driver.d.ts +8 -0
  514. package/dist/simulation/toolkit/dynamic_driver.js +269 -0
  515. package/dist/simulation/toolkit/dynamic_game.d.ts +53 -0
  516. package/dist/simulation/toolkit/dynamic_game.js +805 -0
  517. package/dist/simulation/toolkit/dynamic_knowledge.d.ts +15 -0
  518. package/dist/simulation/toolkit/dynamic_knowledge.js +91 -0
  519. package/dist/simulation/toolkit/dynamic_mechanics.d.ts +22 -0
  520. package/dist/simulation/toolkit/dynamic_mechanics.js +910 -0
  521. package/dist/simulation/toolkit/dynamic_meetings.d.ts +239 -0
  522. package/dist/simulation/toolkit/dynamic_meetings.js +350 -0
  523. package/dist/simulation/toolkit/dynamic_progress.d.ts +131 -0
  524. package/dist/simulation/toolkit/dynamic_progress.js +214 -0
  525. package/dist/simulation/toolkit/dynamic_projection.d.ts +9 -0
  526. package/dist/simulation/toolkit/dynamic_projection.js +219 -0
  527. package/dist/simulation/toolkit/dynamic_runtime.d.ts +412 -0
  528. package/dist/simulation/toolkit/dynamic_runtime.js +6536 -0
  529. package/dist/simulation/toolkit/dynamic_state.d.ts +111 -0
  530. package/dist/simulation/toolkit/dynamic_state.js +1967 -0
  531. package/dist/simulation/toolkit/dynamic_story_review.d.ts +508 -0
  532. package/dist/simulation/toolkit/dynamic_story_review.js +333 -0
  533. package/dist/simulation/toolkit/dynamic_storyteller.d.ts +77 -0
  534. package/dist/simulation/toolkit/dynamic_storyteller.js +114 -0
  535. package/dist/simulation/toolkit/dynamic_storyteller_runtime.d.ts +26 -0
  536. package/dist/simulation/toolkit/dynamic_storyteller_runtime.js +126 -0
  537. package/dist/simulation/toolkit/dynamic_world.d.ts +22 -0
  538. package/dist/simulation/toolkit/dynamic_world.js +377 -0
  539. package/dist/simulation/toolkit/structured_output.d.ts +5 -0
  540. package/dist/simulation/toolkit/structured_output.js +321 -0
  541. package/dist/simulation/types/world.d.ts +2 -0
  542. package/dist/simulation/world/artifact_store.js +1 -1
  543. package/dist/simulation/world/memory_manager.d.ts +5 -0
  544. package/dist/simulation/world/memory_manager.js +17 -2
  545. package/dist/simulation/world/state.d.ts +3 -2
  546. package/dist/simulation/world/state.js +15 -5
  547. package/dist/skills/bundled/measurement-validation/SKILL.md +144 -0
  548. package/dist/terminal_attach.d.ts +1 -0
  549. package/dist/terminal_attach.js +113 -0
  550. package/dist/tool_progress.d.ts +3 -0
  551. package/dist/tool_progress.js +27 -0
  552. package/dist/types.d.ts +145 -10
  553. package/dist/types.js +3 -0
  554. package/dist/ui/core/input.d.ts +3 -0
  555. package/dist/ui/core/input_segments.d.ts +18 -1
  556. package/dist/ui/core/input_segments.js +131 -47
  557. package/dist/ui/core/input_state.d.ts +12 -0
  558. package/dist/ui/core/layout_yoga.d.ts +1 -1
  559. package/dist/ui/core/turn_metadata.d.ts +5 -0
  560. package/dist/ui/core/turn_metadata.js +22 -0
  561. package/dist/ui/ink/render.d.ts +2 -1
  562. package/dist/ui/ink/render.js +6 -5
  563. package/dist/ui/ratatui/headless_pane_manager.d.ts +14 -1
  564. package/dist/ui/ratatui/headless_pane_manager.js +293 -104
  565. package/dist/ui/ratatui/index.d.ts +1 -0
  566. package/dist/ui/ratatui/input.d.ts +4 -0
  567. package/dist/ui/ratatui/input.js +15 -0
  568. package/dist/ui/ratatui/ipc_path.d.ts +2 -0
  569. package/dist/ui/ratatui/ipc_path.js +29 -1
  570. package/dist/ui/ratatui/mux_persistence.d.ts +3 -1
  571. package/dist/ui/ratatui/mux_persistence.js +139 -1
  572. package/dist/ui/ratatui/pane_relay.d.ts +1 -1
  573. package/dist/ui/ratatui/pane_relay.js +55 -27
  574. package/dist/ui/ratatui/process.d.ts +17 -1
  575. package/dist/ui/ratatui/process.js +80 -7
  576. package/dist/ui/ratatui/protocol.d.ts +236 -1
  577. package/dist/ui/ratatui/protocol.js +90 -0
  578. package/dist/ui/scrollback.d.ts +2 -1
  579. package/dist/ui/scrollback.js +9 -4
  580. package/dist/ui/stream_typewriter.d.ts +8 -3
  581. package/dist/ui/stream_typewriter.js +30 -1
  582. package/dist/ui/streaming_scrollback.d.ts +1 -0
  583. package/dist/ui/streaming_scrollback.js +1 -1
  584. package/dist/ui/transcript_render_cache.js +10 -0
  585. package/dist/ui/views/activity_line.d.ts +5 -3
  586. package/dist/ui/views/activity_line.js +65 -7
  587. package/dist/ui/views/app.d.ts +6 -3
  588. package/dist/ui/views/app.js +17 -6
  589. package/dist/ui/views/completion_menu.js +10 -5
  590. package/dist/ui/views/input_area.d.ts +7 -3
  591. package/dist/ui/views/input_area.js +15 -9
  592. package/dist/ui/views/message_list.d.ts +4 -1
  593. package/dist/ui/views/message_list.js +89 -19
  594. package/dist/ui/views/queue_panel.d.ts +3 -1
  595. package/dist/ui/views/queue_panel.js +9 -2
  596. package/dist/ui/views/status_bar.d.ts +4 -2
  597. package/dist/ui/views/status_bar.js +28 -13
  598. package/dist/ui/views/theme.d.ts +2 -0
  599. package/dist/ui/views/theme.js +2 -0
  600. package/dist/ui/widgets/color_panel.js +18 -0
  601. package/dist/ui/widgets/form.js +8 -0
  602. package/dist/ui/widgets/log_view.js +7 -0
  603. package/dist/ui/widgets/navigator_panel.js +8 -0
  604. package/dist/ui/widgets/prompt_viewer.js +2 -0
  605. package/dist/ui/widgets/questionnaire.d.ts +2 -0
  606. package/dist/ui/widgets/questionnaire.js +79 -16
  607. package/dist/ui/widgets/resume_panel.d.ts +2 -0
  608. package/dist/ui/widgets/resume_panel.js +26 -15
  609. package/dist/ui/widgets/rewind_panel.d.ts +2 -2
  610. package/dist/ui/widgets/rewind_panel.js +4 -2
  611. package/dist/ui/widgets/select.js +16 -27
  612. package/dist/ui/widgets/settings_panel.js +16 -0
  613. package/dist/ui/widgets/status_line_panel.d.ts +1 -0
  614. package/dist/ui/widgets/status_line_panel.js +30 -4
  615. package/dist/ui/widgets/text_input.d.ts +2 -0
  616. package/dist/ui/widgets/text_input.js +15 -1
  617. package/dist/ui/widgets/types.d.ts +2 -0
  618. package/dist/usage_projection.d.ts +24 -0
  619. package/dist/usage_projection.js +130 -0
  620. package/dist/usage_snapshot.d.ts +36 -0
  621. package/dist/usage_snapshot.js +131 -0
  622. package/dist/utils/assistant_message.d.ts +2 -0
  623. package/dist/utils/assistant_message.js +5 -0
  624. package/dist/utils/atomic_write.d.ts +6 -2
  625. package/dist/utils/atomic_write.js +29 -4
  626. package/dist/utils/checked_shell.d.ts +25 -0
  627. package/dist/utils/checked_shell.js +107 -0
  628. package/dist/utils/compaction.d.ts +4 -1
  629. package/dist/utils/compaction.js +31 -7
  630. package/dist/utils/context.d.ts +4 -4
  631. package/dist/utils/context.js +14 -12
  632. package/dist/utils/deepseek.d.ts +2 -2
  633. package/dist/utils/deepseek.js +13 -5
  634. package/dist/utils/ephemeral.js +4 -0
  635. package/dist/utils/ephemeral_tools.d.ts +18 -0
  636. package/dist/utils/ephemeral_tools.js +97 -0
  637. package/dist/utils/jev_client.d.ts +4 -0
  638. package/dist/utils/jev_client.js +15 -0
  639. package/dist/utils/model_cache.d.ts +15 -0
  640. package/dist/utils/model_cache.js +143 -0
  641. package/dist/utils/models.d.ts +4 -2
  642. package/dist/utils/models.js +12 -16
  643. package/dist/utils/reasoning.d.ts +8 -1
  644. package/dist/utils/reasoning.js +31 -11
  645. package/dist/utils/router_catalog.d.ts +49 -0
  646. package/dist/utils/router_catalog.js +271 -0
  647. package/dist/utils/sandbox.d.ts +9 -0
  648. package/dist/utils/sandbox.js +118 -15
  649. package/dist/utils/shell.d.ts +7 -0
  650. package/dist/utils/shell.js +98 -34
  651. package/dist/utils/subprocess_env.d.ts +3 -0
  652. package/dist/utils/subprocess_env.js +27 -1
  653. package/dist/web/accounts_mirror.d.ts +3 -1
  654. package/dist/web/accounts_mirror.js +13 -9
  655. package/dist/web/child_publication.d.ts +17 -0
  656. package/dist/web/child_publication.js +45 -0
  657. package/dist/web/mirror_authenticator.d.ts +31 -11
  658. package/dist/web/mirror_authenticator.js +67 -17
  659. package/dist/web/mirror_client.d.ts +31 -4
  660. package/dist/web/mirror_client.js +189 -23
  661. package/dist/web/mirror_stream.d.ts +84 -22
  662. package/dist/web/mirror_stream.js +676 -95
  663. package/dist/web/observer_query_redaction.d.ts +6 -0
  664. package/dist/web/observer_query_redaction.js +58 -0
  665. package/dist/web/pane_registry.d.ts +2 -0
  666. package/dist/web/pane_registry.js +86 -22
  667. package/dist/web/tool_image.d.ts +6 -0
  668. package/dist/web/tool_image.js +17 -0
  669. package/dist/web/view_plugin.d.ts +14 -0
  670. package/dist/web/view_plugin.js +23 -1
  671. package/dist/worker/executor.js +31 -12
  672. package/dist/worker/index.d.ts +1 -0
  673. package/dist/worker/toolsets.js +3 -1
  674. package/dist/worker/types.d.ts +12 -1
  675. package/dist/workflows/registry.js +1 -0
  676. package/dist/workflows/registry_handle.d.ts +2 -0
  677. package/dist/workflows/runtime.d.ts +2 -0
  678. package/dist/workflows/runtime.js +1 -0
  679. package/dist/workspace_agent.d.ts +7 -0
  680. package/dist/workspace_agent.js +36 -0
  681. package/dist/workspace_collision.d.ts +76 -0
  682. package/dist/workspace_collision.js +688 -0
  683. package/dist/workspace_layout.d.ts +18 -0
  684. package/dist/workspace_layout.js +121 -0
  685. package/dist/workspace_runtime.d.ts +44 -0
  686. package/dist/workspace_runtime.js +82 -0
  687. package/dist/workspace_session_context.d.ts +62 -0
  688. package/dist/workspace_session_context.js +160 -0
  689. package/dist/zlm-embed.mjs +32518 -9615
  690. package/dist/zlm.js +1 -0
  691. package/node_modules/@zerg/model-contracts/LICENSE.md +375 -0
  692. package/node_modules/@zerg/model-contracts/README.md +90 -0
  693. package/node_modules/@zerg/model-contracts/index.d.ts +20 -0
  694. package/node_modules/@zerg/model-contracts/index.js +223 -0
  695. package/node_modules/@zerg/model-contracts/package.json +8 -0
  696. package/node_modules/glob/node_modules/brace-expansion/index.js +78 -22
  697. package/node_modules/glob/node_modules/brace-expansion/package.json +1 -1
  698. package/node_modules/readdir-glob/node_modules/brace-expansion/index.js +78 -22
  699. package/node_modules/readdir-glob/node_modules/brace-expansion/package.json +1 -1
  700. package/package.json +45 -24
  701. package/packages/model-contracts/LICENSE.md +375 -0
  702. package/packages/model-contracts/README.md +90 -0
  703. package/packages/model-contracts/index.d.ts +20 -0
  704. package/packages/model-contracts/index.js +223 -0
  705. package/packages/model-contracts/package.json +8 -0
  706. package/remote-broker/Dockerfile +10 -0
  707. package/remote-broker/README.md +308 -0
  708. package/remote-broker/archive.py +1227 -0
  709. package/remote-broker/cutover.py +761 -0
  710. package/remote-broker/requirements.txt +5 -0
  711. package/remote-broker/resources.example.json +6 -0
  712. package/remote-broker/resources.write-staging.example.json +18 -0
  713. package/remote-broker/retained.py +686 -0
  714. package/remote-broker/retained_cleanup.py +550 -0
  715. package/remote-broker/server.py +648 -0
  716. package/scripts/build-remote-broker.mjs +13 -0
  717. package/scripts/ensure-local-packages.mjs +6 -0
  718. package/scripts/node-pty-helpers.mjs +103 -0
@@ -3,6 +3,7 @@ import React, { useReducer, useCallback } from 'react';
3
3
  import { useInput, useStdout, useApp } from 'ink';
4
4
  import { InkNode } from '../ui/ink/index.js';
5
5
  import { buildInputAreaView, wrapInputSegments } from '../ui/views/input_area.js';
6
+ import { PLAN_ACCENT } from '../ui/views/theme.js';
6
7
  import { moveVertical } from '../ui/core/input_segments.js';
7
8
  import { debugLog } from '../debug/logger.js';
8
9
  import chalk from 'chalk';
@@ -16,7 +17,7 @@ import { maybeFilterFillers } from '../utils/dictation_filter.js';
16
17
  const useNative = isNativeDictationAvailable();
17
18
  const isRecording = () => useNative ? isNativeRecording() : isLegacyRecording();
18
19
  const isDictationAvailable = () => useNative ? true : isLegacyDictationAvailable();
19
- import { createEmptyState, insertText, insertBadge, backspace, deleteForward, moveLeft, moveRight, moveWordLeft, moveWordRight, getPlainText, serializeSegments, PASTE_BADGE_THRESHOLD } from '../ui/core/input_segments.js';
20
+ import { createEmptyState, historyUp, historyDown, recordInputHistory, killInputDraft, yankInputKill, getInputShortcutHint, insertText, insertBadge, expandPasteBadge, backspace, deleteForward, moveLeft, moveRight, moveWordLeft, moveWordRight, getPlainText, serializeSegments, PASTE_BADGE_THRESHOLD } from '../ui/core/input_segments.js';
20
21
  // Helper to check for Ctrl key combinations
21
22
  // Handles multiple formats:
22
23
  // 1. Ink's key.ctrl + letter
@@ -44,56 +45,34 @@ function reducer(state, action) {
44
45
  switch (action.type) {
45
46
  case 'apply':
46
47
  return { ...state, ...action.state };
47
- case 'submit': {
48
- const history = action.historyEnabled && action.historyEntry
49
- ? [...state.history.slice(-100), action.historyEntry]
50
- : state.history;
51
- return {
52
- ...state,
53
- segments: [],
54
- cursor: { index: 0, offset: 0 },
55
- historyIdx: -1,
56
- history
57
- };
58
- }
59
- case 'history_nav': {
60
- if (!action.historyEnabled || state.history.length === 0)
61
- return state;
62
- let newIdx;
63
- if (action.direction === 'up') {
64
- newIdx = state.historyIdx === -1
65
- ? state.history.length - 1
66
- : Math.max(0, state.historyIdx - 1);
67
- }
68
- else {
69
- newIdx = state.historyIdx === -1 ? -1 : state.historyIdx + 1;
70
- if (newIdx >= state.history.length)
71
- newIdx = -1;
72
- }
73
- if (newIdx === -1) {
74
- return { ...state, historyIdx: -1, segments: [], cursor: { index: 0, offset: 0 } };
75
- }
76
- const historyValue = state.history[newIdx];
77
- const nextSegments = historyValue.length > 0
78
- ? [{ type: 'text', text: historyValue }]
79
- : [];
80
- return {
81
- ...state,
82
- historyIdx: newIdx,
83
- segments: nextSegments,
84
- cursor: { index: nextSegments.length ? 0 : 0, offset: historyValue.length }
85
- };
86
- }
48
+ case 'submit':
49
+ return recordInputHistory(state, action.historyEnabled ? action.historyEntry : undefined, 101);
50
+ case 'history_nav':
51
+ return !action.historyEnabled ? state : action.direction === 'up' ? historyUp(state) : historyDown(state);
87
52
  default:
88
53
  return state;
89
54
  }
90
55
  }
91
- export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, onToast, onDictationStateChange, cols = process.stdout.columns || 80, inputBus, disabled = false, placeholder = 'Type a message...', historyEnabled = true, debug = false, cwd = process.cwd(), spinnerLabel, spinnerFrame, passwordMode = false, onCycleMode, }) => {
56
+ export const InputArea = ({ onSubmit, onCancelRun, isRunning = false, onQueue, onCommand, commands = [], onStateChange, onToast, onDictationStateChange, cols = process.stdout.columns || 80, inputBus, clearDraftVersion = 0, disabled = false, mode = 'normal', inputBlocked, placeholder = 'Type a message...', historyEnabled: requestedHistoryEnabled = true, debug = false, cwd = process.cwd(), spinnerLabel, spinnerFrame, passwordMode = false, promptOwner, allowEmptySubmit = false, onCycleMode, }) => {
57
+ // Password ownership never retains or recalls ordinary composer history.
58
+ const historyEnabled = requestedHistoryEnabled && !passwordMode;
92
59
  const [state, dispatch] = useReducer(reducer, initialState);
60
+ const clearDraftVersionRef = React.useRef(clearDraftVersion);
93
61
  const stateRef = React.useRef(state);
62
+ const draftRevisionRef = React.useRef(0);
63
+ const draftSegmentsRef = React.useRef(state.segments);
64
+ if (process.env.ZTC_INPUT_OVERLAY !== '1' && draftSegmentsRef.current !== state.segments) {
65
+ draftSegmentsRef.current = state.segments;
66
+ draftRevisionRef.current += 1;
67
+ }
94
68
  const [badgePreview, setBadgePreview] = React.useState(null);
95
69
  const killRingRef = React.useRef([]);
96
70
  const killIndexRef = React.useRef(-1);
71
+ const passwordKillRingRef = React.useRef([]);
72
+ const passwordKillIndexRef = React.useRef(-1);
73
+ const activeKillRingRef = passwordMode ? passwordKillRingRef : killRingRef;
74
+ const activeKillIndexRef = passwordMode ? passwordKillIndexRef : killIndexRef;
75
+ const passwordOwnershipRef = React.useRef(null);
97
76
  const dictationBusyRef = React.useRef(false);
98
77
  // Bracketed paste mode support - buffer paste content between \x1b[200~ and \x1b[201~
99
78
  const pasteBufferRef = React.useRef('');
@@ -138,34 +117,34 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
138
117
  const pushKill = useCallback((text, next) => {
139
118
  if (!text)
140
119
  return;
141
- const ring = killRingRef.current;
120
+ const ring = activeKillRingRef.current;
142
121
  ring.unshift(text);
143
122
  if (ring.length > 20)
144
123
  ring.pop();
145
- killIndexRef.current = 0;
124
+ activeKillIndexRef.current = 0;
146
125
  // A kill that empties the composer reads as losing the prompt — Ctrl+U/Ctrl+K
147
126
  // and Ghostty's Cmd+Backspace (encoded as Ctrl+U) all land here. The text is
148
127
  // still in the kill ring, so say which key brings it back. Mirrors the
149
128
  // Ratatui composer's notice.
150
129
  if (next && next.segments.length === 0)
151
130
  onToast?.('Cleared · Ctrl+Y restores');
152
- }, [onToast]);
131
+ }, [activeKillRingRef, activeKillIndexRef, onToast]);
153
132
  const yank = useCallback((current) => {
154
- const ring = killRingRef.current;
133
+ const ring = activeKillRingRef.current;
155
134
  if (ring.length === 0)
156
135
  return current;
157
- const next = insertText(current, ring[0]);
136
+ const next = yankInputKill(current, ring[0]);
158
137
  return { ...next, historyIdx: -1 };
159
- }, []);
138
+ }, [activeKillRingRef]);
160
139
  const yankPop = useCallback((current) => {
161
- const ring = killRingRef.current;
140
+ const ring = activeKillRingRef.current;
162
141
  if (ring.length === 0)
163
142
  return current;
164
- const idx = killIndexRef.current === -1 ? 0 : (killIndexRef.current + 1) % ring.length;
165
- killIndexRef.current = idx;
166
- const next = insertText(current, ring[idx]);
143
+ const idx = activeKillIndexRef.current === -1 ? 0 : (activeKillIndexRef.current + 1) % ring.length;
144
+ activeKillIndexRef.current = idx;
145
+ const next = yankInputKill(current, ring[idx]);
167
146
  return { ...next, historyIdx: -1 };
168
- }, []);
147
+ }, [activeKillRingRef, activeKillIndexRef]);
169
148
  const killToStart = useCallback((current) => {
170
149
  const segments = [...current.segments];
171
150
  const cursor = current.cursor;
@@ -175,7 +154,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
175
154
  const killed = seg.text.slice(0, cursor.offset);
176
155
  seg.text = seg.text.slice(cursor.offset);
177
156
  return {
178
- next: { ...current, segments, cursor: { index: cursor.index, offset: 0 }, historyIdx: -1 },
157
+ next: { ...current, segments, cursor: { index: cursor.index, offset: 0 } },
179
158
  killed
180
159
  };
181
160
  }, []);
@@ -188,7 +167,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
188
167
  const killed = seg.text.slice(cursor.offset);
189
168
  seg.text = seg.text.slice(0, cursor.offset);
190
169
  return {
191
- next: { ...current, segments, cursor: { index: cursor.index, offset: seg.text.length }, historyIdx: -1 },
170
+ next: { ...current, segments, cursor: { index: cursor.index, offset: seg.text.length } },
192
171
  killed
193
172
  };
194
173
  }, []);
@@ -206,7 +185,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
206
185
  const killed = before.slice(killStart);
207
186
  seg.text = before.slice(0, killStart) + after;
208
187
  return {
209
- next: { ...current, segments, cursor: { index: cursor.index, offset: killStart }, historyIdx: -1 },
188
+ next: { ...current, segments, cursor: { index: cursor.index, offset: killStart } },
210
189
  killed
211
190
  };
212
191
  }, []);
@@ -222,7 +201,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
222
201
  const killed = match ? match[1] : '';
223
202
  seg.text = before + after.slice(killed.length);
224
203
  return {
225
- next: { ...current, segments, cursor, historyIdx: -1 },
204
+ next: { ...current, segments, cursor },
226
205
  killed
227
206
  };
228
207
  }, []);
@@ -241,7 +220,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
241
220
  chars[idx] = chars[idx + 1];
242
221
  chars[idx + 1] = a;
243
222
  seg.text = chars.join('');
244
- return { ...current, segments, cursor: { index: cursor.index, offset: Math.min(text.length, cursor.offset + 1) }, historyIdx: -1 };
223
+ return { ...current, segments, cursor: { index: cursor.index, offset: Math.min(text.length, cursor.offset + 1) } };
245
224
  }, []);
246
225
  const transposeWords = useCallback((current) => {
247
226
  const segments = [...current.segments];
@@ -262,7 +241,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
262
241
  const rightEnd = rightMatch[0].length;
263
242
  const replacedLeft = left.slice(0, leftStart) + rightWord;
264
243
  seg.text = replacedLeft + right.slice(rightEnd);
265
- return { ...current, segments, cursor: { index: cursor.index, offset: replacedLeft.length }, historyIdx: -1 };
244
+ return { ...current, segments, cursor: { index: cursor.index, offset: replacedLeft.length } };
266
245
  }, []);
267
246
  // Tab completion state
268
247
  const tabCompletionAlternativesRef = React.useRef([]);
@@ -282,7 +261,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
282
261
  const completed = alts[nextIdx];
283
262
  const newText = `/${completed}`;
284
263
  const newSegments = [{ type: 'text', text: newText }];
285
- return { ...current, segments: newSegments, cursor: { index: 0, offset: newText.length }, historyIdx: -1 };
264
+ return { ...current, segments: newSegments, cursor: { index: 0, offset: newText.length } };
286
265
  }
287
266
  const matches = commands.filter(c => c.name.startsWith(prefix));
288
267
  if (matches.length === 0)
@@ -296,7 +275,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
296
275
  if (matches.length > 1) {
297
276
  onToast?.(formatCommandCompletionPreview(matches));
298
277
  }
299
- return { ...current, segments: newSegments, cursor: { index: 0, offset: newText.length }, historyIdx: -1 };
278
+ return { ...current, segments: newSegments, cursor: { index: 0, offset: newText.length } };
300
279
  }
301
280
  // Only complete if this looks like a shell command with a path
302
281
  // Match: !cd path, !ls path, !cat path, etc.
@@ -337,7 +316,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
337
316
  ...current,
338
317
  segments: newSegments,
339
318
  cursor: { index: 0, offset: newText.length },
340
- historyIdx: -1
319
+ historyIdx: current.historyIdx
341
320
  };
342
321
  }, [cwd, onToast]);
343
322
  const inputRenderCount = React.useRef(0);
@@ -348,7 +327,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
348
327
  });
349
328
  const handleSubmit = useCallback(() => {
350
329
  const serialized = serializeSegments(state.segments).trim();
351
- if (!serialized)
330
+ if (!serialized && !allowEmptySubmit)
352
331
  return;
353
332
  const plainText = getPlainText(state.segments).trim();
354
333
  const isCommand = state.segments.length === 1 && state.segments[0].type === 'text' && plainText.startsWith('/');
@@ -367,24 +346,51 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
367
346
  onCommand?.(cmd, args);
368
347
  }
369
348
  else {
370
- onSubmit(serialized);
349
+ const saved = structuredClone(state);
350
+ const submittedRevision = draftRevisionRef.current;
351
+ onSubmit(serialized, { restore: () => {
352
+ if (draftRevisionRef.current !== submittedRevision + 1)
353
+ return false;
354
+ draftRevisionRef.current += 1;
355
+ dispatch({ type: 'apply', state: { segments: saved.segments, cursor: saved.cursor, historyIdx: -1 } });
356
+ return true;
357
+ } });
371
358
  }
372
359
  dispatch({ type: 'submit', historyEnabled, historyEntry: serialized });
373
- }, [state.segments, onSubmit, onCommand, historyEnabled]);
360
+ }, [state.segments, onSubmit, onCommand, historyEnabled, allowEmptySubmit]);
374
361
  const navigateHistory = useCallback((direction) => {
375
362
  dispatch({ type: 'history_nav', direction, historyEnabled });
376
363
  }, [historyEnabled]);
377
364
  const { stdout } = useStdout();
378
365
  const { exit } = useApp();
366
+ const clearComposer = useCallback((current) => {
367
+ if (onCancelRun?.())
368
+ return null;
369
+ if (current.segments.length === 0) {
370
+ exit();
371
+ return null;
372
+ }
373
+ const kill = killInputDraft(current);
374
+ killRingRef.current.unshift(kill.killed);
375
+ if (killRingRef.current.length > 20)
376
+ killRingRef.current.pop();
377
+ killIndexRef.current = 0;
378
+ onToast?.('Cleared · Ctrl+Y restores');
379
+ return kill.state;
380
+ }, [onCancelRun, onToast, exit]);
379
381
  const overlayEnabled = process.env.ZTC_INPUT_OVERLAY === '1';
380
382
  const overlayStateRef = React.useRef(initialState);
381
383
  const overlayBusyRef = React.useRef(false);
382
384
  const insertTextIntoState = useCallback((text) => {
383
385
  const current = stateRef.current;
384
386
  const next = insertText(current, text);
385
- dispatch({ type: 'apply', state: { ...next, historyIdx: -1 } });
387
+ dispatch({ type: 'apply', state: { ...next } });
386
388
  }, []);
387
389
  const renderOverlay = useCallback((overlayState) => {
390
+ if (draftSegmentsRef.current !== overlayState.segments) {
391
+ draftSegmentsRef.current = overlayState.segments;
392
+ draftRevisionRef.current += 1;
393
+ }
388
394
  if (!stdout || !stdout.isTTY)
389
395
  return;
390
396
  if (overlayBusyRef.current)
@@ -393,12 +399,23 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
393
399
  const rows = stdout.rows || 24;
394
400
  const statusHeight = 1;
395
401
  const cols = stdout.columns || 80;
396
- const wrapped = wrapInputSegments(overlayState.segments, overlayState.cursor, cols);
402
+ let visibleOverlayState = overlayState;
403
+ if (passwordMode) {
404
+ visibleOverlayState = {
405
+ ...overlayState,
406
+ segments: overlayState.segments.map(segment => segment.type === 'text'
407
+ ? { ...segment, text: '•'.repeat(segment.text.length) } : segment),
408
+ };
409
+ }
410
+ const wrapped = wrapInputSegments(visibleOverlayState.segments, visibleOverlayState.cursor, cols);
397
411
  const inputLines = Math.max(1, wrapped.lines.length);
398
- const inputHeight = inputLines + 4;
412
+ const plan = mode === 'plan';
413
+ const inputHeight = inputLines + 4 + (plan ? 1 : 0);
399
414
  const startRow = Math.max(1, rows - (inputHeight + statusHeight) + 1);
400
- const prompt = disabled ? chalk.gray('❯ ') : chalk.blue('❯ ');
415
+ const prompt = plan ? chalk.hex(PLAN_ACCENT).bold('❯ ') : disabled ? chalk.gray('❯ ') : chalk.blue('❯ ');
401
416
  const lines = [];
417
+ if (plan)
418
+ lines.push(chalk.bold(' PLAN ') + chalk.hex(PLAN_ACCENT)('─'.repeat(Math.max(0, cols - 6))));
402
419
  if (disabled) {
403
420
  lines.push(prompt + chalk.gray(placeholder));
404
421
  }
@@ -414,6 +431,9 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
414
431
  if (isCursor) {
415
432
  lineText += chalk.inverse(token.text || ' ');
416
433
  }
434
+ else if (token.style?.bold && token.style?.badge?.type === 'paste') {
435
+ lineText += chalk.yellow.bold(token.text);
436
+ }
417
437
  else if (token.style?.color === 'yellow') {
418
438
  lineText += chalk.gray(token.text);
419
439
  }
@@ -427,18 +447,19 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
427
447
  lines.push(lineText);
428
448
  });
429
449
  }
430
- const plainText = overlayState.segments.length === 1 && overlayState.segments[0].type === 'text'
431
- ? overlayState.segments[0].text
450
+ const plainText = visibleOverlayState.segments.length === 1 && visibleOverlayState.segments[0].type === 'text'
451
+ ? visibleOverlayState.segments[0].text
432
452
  : '';
433
453
  const isCommandMode = plainText.startsWith('/');
434
454
  const commandQuery = isCommandMode ? plainText.slice(1).trim() : '';
435
455
  const commandMatches = isCommandMode
436
456
  ? commands.filter(c => c.name.startsWith(commandQuery)).slice(0, 4)
437
457
  : [];
458
+ const expansionHint = disabled ? null : getInputShortcutHint(overlayState, isRunning, activeKillRingRef.current.length > 0);
438
459
  for (let i = 0; i < 4; i += 1) {
439
460
  const cmd = commandMatches[i];
440
461
  if (!cmd) {
441
- lines.push(' ');
462
+ lines.push(i === 0 && expansionHint ? chalk.gray(expansionHint) : ' ');
442
463
  continue;
443
464
  }
444
465
  const usage = cmd.usage ? ` ${cmd.usage}` : '';
@@ -454,11 +475,11 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
454
475
  }
455
476
  stdout.write('\x1b[u');
456
477
  overlayBusyRef.current = false;
457
- }, [commands, disabled, placeholder, stdout]);
478
+ }, [commands, disabled, placeholder, stdout, passwordMode, cols, mode, isRunning]);
458
479
  const insertTextIntoOverlay = useCallback((text) => {
459
480
  const current = overlayStateRef.current;
460
481
  const next = insertText(current, text);
461
- overlayStateRef.current = { ...next, historyIdx: -1 };
482
+ overlayStateRef.current = { ...next };
462
483
  renderOverlay(overlayStateRef.current);
463
484
  }, [renderOverlay]);
464
485
  // The Ink renderer has no native clipboard bridge. Image pastes are read by
@@ -477,6 +498,16 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
477
498
  const isKittyCtrl = modifier === 5;
478
499
  const isKittyMeta = modifier === 9;
479
500
  const lowerChar = char.toLowerCase();
501
+ if (isKittyCtrl && lowerChar === 'c') {
502
+ const next = clearComposer(state);
503
+ if (next)
504
+ dispatch({ type: 'apply', state: next });
505
+ return;
506
+ }
507
+ if (isKittyCtrl && lowerChar === 'x') {
508
+ dispatch({ type: 'apply', state: expandPasteBadge(state) });
509
+ return;
510
+ }
480
511
  // Navigation
481
512
  if (isKittyCtrl && lowerChar === 'a') {
482
513
  dispatch({ type: 'apply', state: { cursor: { index: 0, offset: 0 } } });
@@ -627,7 +658,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
627
658
  return;
628
659
  }
629
660
  // Unknown Kitty sequence - don't insert as text
630
- }, [disabled, killToEnd, killToStart, killWordBackward, killWordForward, navigateHistory, onDictationStateChange, onToast, pushKill, state, transposeChars, transposeWords, yank, yankPop]);
661
+ }, [clearComposer, disabled, killToEnd, killToStart, killWordBackward, killWordForward, navigateHistory, onDictationStateChange, onToast, pushKill, state, transposeChars, transposeWords, yank, yankPop]);
631
662
  const handleInput = useCallback((input, key) => {
632
663
  // Detect Kitty keyboard protocol CSI u sequences
633
664
  // Format: ESC [ <keycode> ; <modifiers> u
@@ -648,8 +679,8 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
648
679
  const modifier = parseInt(kittyMatch[2], 10);
649
680
  const isKittyCtrl = modifier === 5;
650
681
  const isKittyMeta = modifier === 9;
651
- // Ctrl+C (99;5) or Cmd+C (99;9) - exit the app
652
- if (keycode === 99 && (isKittyCtrl || isKittyMeta)) {
682
+ // Ctrl+C follows the composer lifecycle; retain the existing Cmd+C exit.
683
+ if (keycode === 99 && isKittyMeta) {
653
684
  exit();
654
685
  return;
655
686
  }
@@ -669,6 +700,16 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
669
700
  }
670
701
  if (disabled)
671
702
  return;
703
+ if (key.ctrl && isCtrl(input, key, 'c') && !isPastingRef.current) {
704
+ const next = clearComposer(state);
705
+ if (next)
706
+ dispatch({ type: 'apply', state: next });
707
+ return;
708
+ }
709
+ if (key.ctrl && isCtrl(input, key, 'x') && !isPastingRef.current) {
710
+ dispatch({ type: 'apply', state: expandPasteBadge(state) });
711
+ return;
712
+ }
672
713
  // Handle bracketed paste mode markers
673
714
  // Note: ESC might be stripped or sent separately by Ink, so check both with and without ESC
674
715
  const PASTE_START_FULL = '\x1b[200~';
@@ -695,11 +736,11 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
695
736
  const lineCount = pasteContent.split('\n').length;
696
737
  if (lineCount >= PASTE_BADGE_THRESHOLD) {
697
738
  const next = insertBadge(state, { type: 'paste', text: pasteContent });
698
- dispatch({ type: 'apply', state: { ...next, historyIdx: -1 } });
739
+ dispatch({ type: 'apply', state: { ...next } });
699
740
  }
700
741
  else {
701
742
  const next = insertText(state, pasteContent);
702
- dispatch({ type: 'apply', state: { ...next, historyIdx: -1 } });
743
+ dispatch({ type: 'apply', state: { ...next } });
703
744
  }
704
745
  }
705
746
  else {
@@ -727,11 +768,11 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
727
768
  if (pasteContent.length > 0) {
728
769
  if (lineCount >= PASTE_BADGE_THRESHOLD) {
729
770
  const next = insertBadge(state, { type: 'paste', text: pasteContent });
730
- dispatch({ type: 'apply', state: { ...next, historyIdx: -1 } });
771
+ dispatch({ type: 'apply', state: { ...next } });
731
772
  }
732
773
  else {
733
774
  const next = insertText(state, pasteContent);
734
- dispatch({ type: 'apply', state: { ...next, historyIdx: -1 } });
775
+ dispatch({ type: 'apply', state: { ...next } });
735
776
  }
736
777
  }
737
778
  else {
@@ -780,7 +821,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
780
821
  // kitty keyboard protocol enabled (macOS Terminal.app, default iTerm2).
781
822
  if (key.return && (key.shift || key.meta)) {
782
823
  const next = insertText(state, '\n');
783
- dispatch({ type: 'apply', state: { ...next, historyIdx: -1 } });
824
+ dispatch({ type: 'apply', state: { ...next } });
784
825
  return;
785
826
  }
786
827
  // Line-wise movement first (Codex parity): the arrows walk a multi-line
@@ -789,7 +830,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
789
830
  if (key.upArrow || key.downArrow) {
790
831
  const vertical = moveVertical(state, key.upArrow ? -1 : 1);
791
832
  if (vertical.moved) {
792
- dispatch({ type: 'apply', state: { ...vertical.state, historyIdx: -1 } });
833
+ dispatch({ type: 'apply', state: { ...vertical.state } });
793
834
  return;
794
835
  }
795
836
  navigateHistory(key.upArrow ? 'up' : 'down');
@@ -801,6 +842,14 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
801
842
  return;
802
843
  }
803
844
  if (safeKey.tab || input === '\t') {
845
+ const serialized = serializeSegments(state.segments).trim();
846
+ const plain = getPlainText(state.segments).trim();
847
+ if (onQueue && serialized && !plain.startsWith('/') && !plain.startsWith('!')) {
848
+ if (onQueue(serialized)) {
849
+ dispatch({ type: 'submit', historyEnabled, historyEntry: serialized });
850
+ }
851
+ return;
852
+ }
804
853
  void handleTabComplete(state).then(nextState => {
805
854
  if (nextState !== state) {
806
855
  dispatch({ type: 'apply', state: nextState });
@@ -995,14 +1044,14 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
995
1044
  const lineCount = input.split('\n').length;
996
1045
  if (lineCount >= PASTE_BADGE_THRESHOLD) {
997
1046
  const next = insertBadge(state, { type: 'paste', text: input });
998
- dispatch({ type: 'apply', state: { ...next, historyIdx: -1 } });
1047
+ dispatch({ type: 'apply', state: { ...next } });
999
1048
  return;
1000
1049
  }
1001
1050
  }
1002
1051
  const next = insertText(state, input);
1003
- dispatch({ type: 'apply', state: { ...next, historyIdx: -1 } });
1052
+ dispatch({ type: 'apply', state: { ...next } });
1004
1053
  }
1005
- }, [disabled, exit, handleClipboardImagePaste, handleKittyInput, handleSubmit, navigateHistory, state]);
1054
+ }, [clearComposer, disabled, exit, handleClipboardImagePaste, handleKittyInput, handleSubmit, navigateHistory, state, onQueue, historyEnabled, pushKill, yank, yankPop]);
1006
1055
  // Handle Kitty keyboard protocol sequences for overlay mode
1007
1056
  const handleKittyOverlayInput = useCallback((char, key, keycode, modifier) => {
1008
1057
  if (disabled)
@@ -1011,6 +1060,19 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1011
1060
  const isKittyCtrl = modifier === 5;
1012
1061
  const isKittyMeta = modifier === 9;
1013
1062
  const lowerChar = char.toLowerCase();
1063
+ if (isKittyCtrl && lowerChar === 'c') {
1064
+ const next = clearComposer(current);
1065
+ if (next) {
1066
+ overlayStateRef.current = next;
1067
+ renderOverlay(next);
1068
+ }
1069
+ return;
1070
+ }
1071
+ if (isKittyCtrl && lowerChar === 'x') {
1072
+ overlayStateRef.current = expandPasteBadge(current);
1073
+ renderOverlay(overlayStateRef.current);
1074
+ return;
1075
+ }
1014
1076
  // Navigation
1015
1077
  if (isKittyCtrl && lowerChar === 'a') {
1016
1078
  overlayStateRef.current = { ...current, cursor: { index: 0, offset: 0 } };
@@ -1033,36 +1095,15 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1033
1095
  return;
1034
1096
  }
1035
1097
  if (isKittyCtrl && lowerChar === 'p') {
1036
- if (historyEnabled && current.history.length > 0) {
1037
- const newIdx = current.historyIdx === -1 ? current.history.length - 1 : Math.max(0, current.historyIdx - 1);
1038
- const historyValue = current.history[newIdx] || '';
1039
- overlayStateRef.current = {
1040
- ...current,
1041
- historyIdx: newIdx,
1042
- segments: historyValue.length > 0 ? [{ type: 'text', text: historyValue }] : [],
1043
- cursor: { index: historyValue.length > 0 ? 0 : 0, offset: historyValue.length }
1044
- };
1098
+ if (historyEnabled) {
1099
+ overlayStateRef.current = historyUp(current);
1045
1100
  renderOverlay(overlayStateRef.current);
1046
1101
  }
1047
1102
  return;
1048
1103
  }
1049
1104
  if (isKittyCtrl && lowerChar === 'n') {
1050
- if (historyEnabled && current.history.length > 0) {
1051
- let newIdx = current.historyIdx === -1 ? -1 : current.historyIdx + 1;
1052
- if (newIdx >= current.history.length)
1053
- newIdx = -1;
1054
- if (newIdx === -1) {
1055
- overlayStateRef.current = { ...createEmptyState(), history: current.history, historyIdx: -1 };
1056
- }
1057
- else {
1058
- const historyValue = current.history[newIdx];
1059
- overlayStateRef.current = {
1060
- ...current,
1061
- historyIdx: newIdx,
1062
- segments: historyValue.length > 0 ? [{ type: 'text', text: historyValue }] : [],
1063
- cursor: { index: historyValue.length > 0 ? 0 : 0, offset: historyValue.length }
1064
- };
1065
- }
1105
+ if (historyEnabled) {
1106
+ overlayStateRef.current = historyDown(current);
1066
1107
  renderOverlay(overlayStateRef.current);
1067
1108
  }
1068
1109
  return;
@@ -1071,7 +1112,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1071
1112
  if (isKittyCtrl && lowerChar === 'u') {
1072
1113
  const { next, killed } = killToStart(current);
1073
1114
  pushKill(killed, next);
1074
- overlayStateRef.current = { ...next, history: current.history, historyIdx: -1 };
1115
+ overlayStateRef.current = { ...next, history: current.history };
1075
1116
  renderOverlay(overlayStateRef.current);
1076
1117
  return;
1077
1118
  }
@@ -1119,7 +1160,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1119
1160
  if (text && text.trim()) {
1120
1161
  // Filter filler words and submit the transcribed text
1121
1162
  onSubmit(maybeFilterFillers(text.trim()));
1122
- overlayStateRef.current = { ...overlayStateRef.current, segments: [], cursor: { index: 0, offset: 0 }, historyIdx: -1 };
1163
+ overlayStateRef.current = { ...overlayStateRef.current, segments: [], cursor: { index: 0, offset: 0 } };
1123
1164
  renderOverlay(overlayStateRef.current);
1124
1165
  }
1125
1166
  else {
@@ -1140,7 +1181,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1140
1181
  if (result.text && result.text.trim()) {
1141
1182
  // Filter filler words and submit the transcribed text
1142
1183
  onSubmit(maybeFilterFillers(result.text.trim()));
1143
- overlayStateRef.current = { ...overlayStateRef.current, segments: [], cursor: { index: 0, offset: 0 }, historyIdx: -1 };
1184
+ overlayStateRef.current = { ...overlayStateRef.current, segments: [], cursor: { index: 0, offset: 0 } };
1144
1185
  renderOverlay(overlayStateRef.current);
1145
1186
  }
1146
1187
  else {
@@ -1205,7 +1246,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1205
1246
  return;
1206
1247
  }
1207
1248
  // Unknown Kitty sequence - don't insert as text
1208
- }, [disabled, historyEnabled, killToEnd, killToStart, killWordBackward, killWordForward, onDictationStateChange, onToast, pushKill, renderOverlay, transposeChars, transposeWords, yank, yankPop]);
1249
+ }, [clearComposer, disabled, historyEnabled, killToEnd, killToStart, killWordBackward, killWordForward, onDictationStateChange, onToast, pushKill, renderOverlay, transposeChars, transposeWords, yank, yankPop]);
1209
1250
  const handleOverlayInput = useCallback((input, key) => {
1210
1251
  if (disabled)
1211
1252
  return;
@@ -1237,13 +1278,38 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1237
1278
  return;
1238
1279
  }
1239
1280
  const current = overlayStateRef.current;
1281
+ if (key.ctrl && isCtrl(input, key, 'c')) {
1282
+ const next = clearComposer(current);
1283
+ if (next) {
1284
+ overlayStateRef.current = next;
1285
+ renderOverlay(next);
1286
+ }
1287
+ return;
1288
+ }
1289
+ if (key.ctrl && isCtrl(input, key, 'x')) {
1290
+ overlayStateRef.current = expandPasteBadge(current);
1291
+ renderOverlay(overlayStateRef.current);
1292
+ return;
1293
+ }
1294
+ if ((safeKey.tab || input === '\t') && !safeKey.shift) {
1295
+ const serialized = serializeSegments(current.segments).trim();
1296
+ const plain = getPlainText(current.segments).trim();
1297
+ if (onQueue && serialized && !plain.startsWith('/') && !plain.startsWith('!')) {
1298
+ if (onQueue(serialized)) {
1299
+ const history = historyEnabled ? [...current.history.slice(-100), serialized] : current.history;
1300
+ overlayStateRef.current = { ...createEmptyState(), history, historyIdx: -1 };
1301
+ renderOverlay(overlayStateRef.current);
1302
+ }
1303
+ return;
1304
+ }
1305
+ }
1240
1306
  if ((key.ctrl || key.meta) && (input === 'v' || input === '\u0016')) {
1241
1307
  void handleClipboardImagePaste('overlay');
1242
1308
  return;
1243
1309
  }
1244
1310
  if (key.return && !key.shift && !key.meta) {
1245
1311
  const serialized = serializeSegments(current.segments).trim();
1246
- if (serialized) {
1312
+ if (serialized || allowEmptySubmit) {
1247
1313
  const plainText = getPlainText(current.segments).trim();
1248
1314
  const isCommand = current.segments.length === 1 && current.segments[0].type === 'text' && plainText.startsWith('/');
1249
1315
  if (isCommand) {
@@ -1252,7 +1318,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1252
1318
  const args = parts.slice(1);
1253
1319
  // Handle /pi (paste image) command directly
1254
1320
  if (cmd === 'pi' || cmd === 'paste-image') {
1255
- overlayStateRef.current = { ...createEmptyState(), history: current.history, historyIdx: -1 };
1321
+ overlayStateRef.current = recordInputHistory(current, historyEnabled ? serialized : undefined, 101);
1256
1322
  renderOverlay(overlayStateRef.current);
1257
1323
  void handleClipboardImagePaste('overlay');
1258
1324
  return;
@@ -1260,18 +1326,25 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1260
1326
  onCommand?.(cmd, args);
1261
1327
  }
1262
1328
  else {
1263
- onSubmit(serialized);
1264
- }
1265
- if (historyEnabled) {
1266
- current.history = [...current.history.slice(-100), serialized];
1329
+ const saved = structuredClone(current);
1330
+ const submittedRevision = draftRevisionRef.current;
1331
+ onSubmit(serialized, { restore: () => {
1332
+ if (draftRevisionRef.current !== submittedRevision + 1)
1333
+ return false;
1334
+ draftRevisionRef.current += 1;
1335
+ overlayStateRef.current = { ...overlayStateRef.current,
1336
+ segments: saved.segments, cursor: saved.cursor, historyIdx: -1 };
1337
+ renderOverlay(overlayStateRef.current);
1338
+ return true;
1339
+ } });
1267
1340
  }
1268
1341
  }
1269
- overlayStateRef.current = { ...createEmptyState(), history: current.history, historyIdx: -1 };
1342
+ overlayStateRef.current = recordInputHistory(current, historyEnabled && serialized ? serialized : undefined, 101);
1270
1343
  renderOverlay(overlayStateRef.current);
1271
1344
  return;
1272
1345
  }
1273
1346
  if (key.return && (key.shift || key.meta)) {
1274
- overlayStateRef.current = { ...insertText(current, '\n'), historyIdx: -1 };
1347
+ overlayStateRef.current = { ...insertText(current, '\n') };
1275
1348
  renderOverlay(overlayStateRef.current);
1276
1349
  return;
1277
1350
  }
@@ -1279,32 +1352,12 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1279
1352
  const direction = key.upArrow ? 'up' : 'down';
1280
1353
  const vertical = moveVertical(current, direction === 'up' ? -1 : 1);
1281
1354
  if (vertical.moved) {
1282
- overlayStateRef.current = { ...vertical.state, historyIdx: -1 };
1355
+ overlayStateRef.current = { ...vertical.state };
1283
1356
  renderOverlay(overlayStateRef.current);
1284
1357
  return;
1285
1358
  }
1286
- if (historyEnabled && current.history.length > 0) {
1287
- let newIdx;
1288
- if (direction === 'up') {
1289
- newIdx = current.historyIdx === -1 ? current.history.length - 1 : Math.max(0, current.historyIdx - 1);
1290
- }
1291
- else {
1292
- newIdx = current.historyIdx === -1 ? -1 : current.historyIdx + 1;
1293
- if (newIdx >= current.history.length)
1294
- newIdx = -1;
1295
- }
1296
- if (newIdx === -1) {
1297
- overlayStateRef.current = { ...createEmptyState(), history: current.history, historyIdx: -1 };
1298
- }
1299
- else {
1300
- const historyValue = current.history[newIdx];
1301
- overlayStateRef.current = {
1302
- ...current,
1303
- historyIdx: newIdx,
1304
- segments: historyValue.length > 0 ? [{ type: 'text', text: historyValue }] : [],
1305
- cursor: { index: historyValue.length > 0 ? 0 : 0, offset: historyValue.length }
1306
- };
1307
- }
1359
+ if (historyEnabled) {
1360
+ overlayStateRef.current = key.upArrow ? historyUp(current) : historyDown(current);
1308
1361
  renderOverlay(overlayStateRef.current);
1309
1362
  }
1310
1363
  return;
@@ -1351,36 +1404,15 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1351
1404
  }
1352
1405
  if (isCtrl(input, key, 'p')) {
1353
1406
  const direction = 'up';
1354
- if (historyEnabled && current.history.length > 0) {
1355
- let newIdx = current.historyIdx === -1 ? current.history.length - 1 : Math.max(0, current.historyIdx - 1);
1356
- const historyValue = current.history[newIdx] || '';
1357
- overlayStateRef.current = {
1358
- ...current,
1359
- historyIdx: newIdx,
1360
- segments: historyValue.length > 0 ? [{ type: 'text', text: historyValue }] : [],
1361
- cursor: { index: historyValue.length > 0 ? 0 : 0, offset: historyValue.length }
1362
- };
1407
+ if (historyEnabled) {
1408
+ overlayStateRef.current = historyUp(current);
1363
1409
  renderOverlay(overlayStateRef.current);
1364
1410
  }
1365
1411
  return;
1366
1412
  }
1367
1413
  if (isCtrl(input, key, 'n')) {
1368
- if (historyEnabled && current.history.length > 0) {
1369
- let newIdx = current.historyIdx === -1 ? -1 : current.historyIdx + 1;
1370
- if (newIdx >= current.history.length)
1371
- newIdx = -1;
1372
- if (newIdx === -1) {
1373
- overlayStateRef.current = { ...createEmptyState(), history: current.history, historyIdx: -1 };
1374
- }
1375
- else {
1376
- const historyValue = current.history[newIdx];
1377
- overlayStateRef.current = {
1378
- ...current,
1379
- historyIdx: newIdx,
1380
- segments: historyValue.length > 0 ? [{ type: 'text', text: historyValue }] : [],
1381
- cursor: { index: historyValue.length > 0 ? 0 : 0, offset: historyValue.length }
1382
- };
1383
- }
1414
+ if (historyEnabled) {
1415
+ overlayStateRef.current = historyDown(current);
1384
1416
  renderOverlay(overlayStateRef.current);
1385
1417
  }
1386
1418
  return;
@@ -1391,7 +1423,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1391
1423
  if (key.meta || key.ctrl) {
1392
1424
  const { next, killed } = killWordBackward(current);
1393
1425
  pushKill(killed, next);
1394
- overlayStateRef.current = { ...next, history: current.history, historyIdx: -1 };
1426
+ overlayStateRef.current = { ...next, history: current.history };
1395
1427
  }
1396
1428
  else {
1397
1429
  overlayStateRef.current = backspace(current);
@@ -1402,7 +1434,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1402
1434
  if (isCtrl(input, key, 'u')) {
1403
1435
  const { next, killed } = killToStart(current);
1404
1436
  pushKill(killed, next);
1405
- overlayStateRef.current = { ...next, history: current.history, historyIdx: -1 };
1437
+ overlayStateRef.current = { ...next, history: current.history };
1406
1438
  renderOverlay(overlayStateRef.current);
1407
1439
  return;
1408
1440
  }
@@ -1431,7 +1463,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1431
1463
  if (text && text.trim()) {
1432
1464
  // Filter filler words and submit the transcribed text
1433
1465
  onSubmit(maybeFilterFillers(text.trim()));
1434
- overlayStateRef.current = { ...overlayStateRef.current, segments: [], cursor: { index: 0, offset: 0 }, historyIdx: -1 };
1466
+ overlayStateRef.current = { ...overlayStateRef.current, segments: [], cursor: { index: 0, offset: 0 } };
1435
1467
  renderOverlay(overlayStateRef.current);
1436
1468
  }
1437
1469
  else {
@@ -1452,7 +1484,7 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1452
1484
  if (result.text && result.text.trim()) {
1453
1485
  // Filter filler words and submit the transcribed text
1454
1486
  onSubmit(maybeFilterFillers(result.text.trim()));
1455
- overlayStateRef.current = { ...overlayStateRef.current, segments: [], cursor: { index: 0, offset: 0 }, historyIdx: -1 };
1487
+ overlayStateRef.current = { ...overlayStateRef.current, segments: [], cursor: { index: 0, offset: 0 } };
1456
1488
  renderOverlay(overlayStateRef.current);
1457
1489
  }
1458
1490
  else {
@@ -1552,42 +1584,84 @@ export const InputArea = ({ onSubmit, onCommand, commands = [], onStateChange, o
1552
1584
  else {
1553
1585
  overlayStateRef.current = insertText(current, input);
1554
1586
  }
1555
- overlayStateRef.current = { ...overlayStateRef.current, historyIdx: -1 };
1587
+ overlayStateRef.current = { ...overlayStateRef.current };
1556
1588
  renderOverlay(overlayStateRef.current);
1557
1589
  }
1558
- }, [disabled, handleKittyOverlayInput, historyEnabled, onCommand, onSubmit, renderOverlay]);
1559
- useInput((input, key) => {
1590
+ }, [clearComposer, disabled, handleKittyOverlayInput, historyEnabled, onCommand, onSubmit, onQueue, renderOverlay, pushKill, yank, yankPop, allowEmptySubmit]);
1591
+ const currentInputHandler = (input, key) => {
1592
+ if (inputBlocked?.())
1593
+ return;
1560
1594
  if (overlayEnabled) {
1561
1595
  handleOverlayInput(input, key);
1562
1596
  return;
1563
1597
  }
1564
1598
  handleInput(input, key);
1565
- });
1599
+ };
1600
+ const inputHandlerRef = React.useRef(currentInputHandler);
1601
+ React.useLayoutEffect(() => {
1602
+ inputHandlerRef.current = currentInputHandler;
1603
+ }, [currentInputHandler]);
1604
+ // Ink subscribes in a passive effect. Keep that subscription stable, while
1605
+ // committed draft and prompt ownership reach the listener before the next key.
1606
+ const dispatchInput = useCallback((input, key) => {
1607
+ inputHandlerRef.current(input, key);
1608
+ }, []);
1609
+ useInput(dispatchInput);
1566
1610
  React.useEffect(() => {
1567
1611
  if (!inputBus)
1568
1612
  return;
1569
- return inputBus.subscribe(({ input, key }) => {
1570
- if (overlayEnabled) {
1571
- handleOverlayInput(input, key);
1572
- return;
1573
- }
1574
- handleInput(input, key);
1575
- });
1576
- }, [handleInput, handleOverlayInput, inputBus, overlayEnabled]);
1613
+ return inputBus.subscribe(({ input, key }) => dispatchInput(input, key));
1614
+ }, [dispatchInput, inputBus]);
1577
1615
  React.useEffect(() => {
1578
1616
  if (!overlayEnabled)
1579
1617
  return;
1580
1618
  renderOverlay(overlayStateRef.current);
1581
1619
  }, [overlayEnabled, renderOverlay]);
1620
+ React.useLayoutEffect(() => {
1621
+ const previous = passwordOwnershipRef.current;
1622
+ if (previous && previous.passwordMode === passwordMode && previous.promptOwner === promptOwner)
1623
+ return;
1624
+ passwordOwnershipRef.current = { passwordMode, promptOwner };
1625
+ // Each password owner can recall ordinary cuts without retaining another
1626
+ // owner's private cuts or changing the ordinary ring's order/index.
1627
+ passwordKillRingRef.current = passwordMode ? [...killRingRef.current] : [];
1628
+ passwordKillIndexRef.current = passwordMode ? killIndexRef.current : -1;
1629
+ if (previous?.passwordMode && passwordMode && previous.promptOwner !== promptOwner) {
1630
+ dispatch({ type: 'submit', historyEnabled: false });
1631
+ if (overlayEnabled) {
1632
+ draftRevisionRef.current += 1;
1633
+ overlayStateRef.current = { ...overlayStateRef.current, segments: [], cursor: { index: 0, offset: 0 }, historyIdx: -1 };
1634
+ renderOverlay(overlayStateRef.current);
1635
+ }
1636
+ }
1637
+ }, [passwordMode, promptOwner, overlayEnabled, renderOverlay]);
1638
+ React.useLayoutEffect(() => {
1639
+ if (clearDraftVersionRef.current === clearDraftVersion)
1640
+ return;
1641
+ clearDraftVersionRef.current = clearDraftVersion;
1642
+ passwordKillRingRef.current = [];
1643
+ passwordKillIndexRef.current = -1;
1644
+ dispatch({ type: 'submit', historyEnabled: false });
1645
+ if (overlayEnabled) {
1646
+ draftRevisionRef.current += 1;
1647
+ overlayStateRef.current = { ...overlayStateRef.current, segments: [], cursor: { index: 0, offset: 0 }, historyIdx: -1 };
1648
+ renderOverlay(overlayStateRef.current);
1649
+ }
1650
+ }, [clearDraftVersion, overlayEnabled, renderOverlay]);
1582
1651
  // Create masked state for password mode
1652
+ const visibleState = clearDraftVersionRef.current !== clearDraftVersion
1653
+ ? { ...state, segments: [], cursor: { index: 0, offset: 0 } }
1654
+ : state;
1583
1655
  const displayState = passwordMode ? {
1584
- ...state,
1585
- segments: state.segments.map(seg => seg.type === 'text' ? { ...seg, text: '•'.repeat(seg.text.length) } : seg)
1586
- } : state;
1656
+ ...visibleState,
1657
+ segments: visibleState.segments.map(seg => seg.type === 'text' ? { ...seg, text: '•'.repeat(seg.text.length) } : seg)
1658
+ } : visibleState;
1587
1659
  const node = buildInputAreaView({
1588
1660
  state: displayState,
1589
1661
  placeholder,
1590
1662
  disabled,
1663
+ mode,
1664
+ isRunning,
1591
1665
  commands,
1592
1666
  cols,
1593
1667
  badgePreview,