orynacode-ai 1.16.2

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 (938) hide show
  1. package/AGENTS.md +131 -0
  2. package/BUN_SHELL_MIGRATION_PLAN.md +136 -0
  3. package/Dockerfile +18 -0
  4. package/README.md +15 -0
  5. package/bin/orynacode +199 -0
  6. package/bunfig.toml +7 -0
  7. package/git +0 -0
  8. package/migration/20260511173437_session-metadata/migration.sql +1 -0
  9. package/migration/20260511173437_session-metadata/snapshot.json +1500 -0
  10. package/package.json +155 -0
  11. package/parsers-config.ts +386 -0
  12. package/script/bench-search.ts +115 -0
  13. package/script/bench-test-suite.ts +52 -0
  14. package/script/build.ts +244 -0
  15. package/script/generate.ts +14 -0
  16. package/script/httpapi-exercise.ts +1 -0
  17. package/script/postinstall.mjs +189 -0
  18. package/script/profile-test-files.ts +42 -0
  19. package/script/publish.ts +213 -0
  20. package/script/run-workspace-server +106 -0
  21. package/script/schema.ts +77 -0
  22. package/script/time.ts +6 -0
  23. package/script/trace-imports.ts +153 -0
  24. package/specs/effect/error-boundaries-plan.md +235 -0
  25. package/specs/effect/errors.md +207 -0
  26. package/specs/effect/facades.md +218 -0
  27. package/specs/effect/guide.md +247 -0
  28. package/specs/effect/instance-context.md +13 -0
  29. package/specs/effect/loose-ends.md +30 -0
  30. package/specs/effect/migration.md +62 -0
  31. package/specs/effect/routes.md +61 -0
  32. package/specs/effect/schema.md +88 -0
  33. package/specs/effect/server-package.md +58 -0
  34. package/specs/effect/todo.md +241 -0
  35. package/specs/effect/tools.md +88 -0
  36. package/specs/openapi-translation-cleanup.md +204 -0
  37. package/specs/tui-plugins.md +544 -0
  38. package/specs/v2/api.ts +67 -0
  39. package/specs/v2/message-shape.md +136 -0
  40. package/specs/v2/notifications.md +13 -0
  41. package/specs/v2/tui-command-shim.md +67 -0
  42. package/src/account/account.ts +459 -0
  43. package/src/account/repo.ts +170 -0
  44. package/src/account/schema.ts +99 -0
  45. package/src/account/url.ts +8 -0
  46. package/src/acp/agent.ts +95 -0
  47. package/src/acp/config-option.ts +203 -0
  48. package/src/acp/content.ts +250 -0
  49. package/src/acp/directory.ts +210 -0
  50. package/src/acp/error.ts +90 -0
  51. package/src/acp/event.ts +344 -0
  52. package/src/acp/permission.ts +145 -0
  53. package/src/acp/profile.ts +42 -0
  54. package/src/acp/service.ts +1062 -0
  55. package/src/acp/session.ts +231 -0
  56. package/src/acp/tool.ts +317 -0
  57. package/src/acp/usage.ts +239 -0
  58. package/src/agent/agent.ts +433 -0
  59. package/src/agent/generate.txt +75 -0
  60. package/src/agent/prompt/compaction.txt +9 -0
  61. package/src/agent/prompt/explore.txt +18 -0
  62. package/src/agent/prompt/summary.txt +11 -0
  63. package/src/agent/prompt/title.txt +44 -0
  64. package/src/agent/subagent-permissions.ts +35 -0
  65. package/src/audio.d.ts +14 -0
  66. package/src/auth/index.ts +96 -0
  67. package/src/background/job.ts +36 -0
  68. package/src/bus/global.ts +22 -0
  69. package/src/cli/bootstrap.ts +11 -0
  70. package/src/cli/cmd/account.ts +264 -0
  71. package/src/cli/cmd/acp.ts +76 -0
  72. package/src/cli/cmd/agent.ts +259 -0
  73. package/src/cli/cmd/cmd.ts +7 -0
  74. package/src/cli/cmd/db.ts +62 -0
  75. package/src/cli/cmd/debug/agent.handler.ts +193 -0
  76. package/src/cli/cmd/debug/agent.ts +27 -0
  77. package/src/cli/cmd/debug/config.ts +14 -0
  78. package/src/cli/cmd/debug/file.ts +87 -0
  79. package/src/cli/cmd/debug/index.ts +87 -0
  80. package/src/cli/cmd/debug/lsp.ts +51 -0
  81. package/src/cli/cmd/debug/ripgrep.ts +99 -0
  82. package/src/cli/cmd/debug/scrap.ts +18 -0
  83. package/src/cli/cmd/debug/skill.ts +15 -0
  84. package/src/cli/cmd/debug/snapshot.ts +50 -0
  85. package/src/cli/cmd/debug/startup.ts +11 -0
  86. package/src/cli/cmd/debug/v2.ts +46 -0
  87. package/src/cli/cmd/export.ts +292 -0
  88. package/src/cli/cmd/generate.ts +54 -0
  89. package/src/cli/cmd/github.handler.ts +1593 -0
  90. package/src/cli/cmd/github.shared.ts +30 -0
  91. package/src/cli/cmd/github.ts +42 -0
  92. package/src/cli/cmd/import.ts +224 -0
  93. package/src/cli/cmd/mcp.ts +846 -0
  94. package/src/cli/cmd/models.ts +66 -0
  95. package/src/cli/cmd/plug.ts +230 -0
  96. package/src/cli/cmd/pr.ts +115 -0
  97. package/src/cli/cmd/prompt-display.ts +48 -0
  98. package/src/cli/cmd/providers.ts +506 -0
  99. package/src/cli/cmd/run/demo.ts +1274 -0
  100. package/src/cli/cmd/run/entry.body.ts +194 -0
  101. package/src/cli/cmd/run/footer.command.tsx +899 -0
  102. package/src/cli/cmd/run/footer.menu.tsx +306 -0
  103. package/src/cli/cmd/run/footer.permission.tsx +475 -0
  104. package/src/cli/cmd/run/footer.prompt.tsx +1207 -0
  105. package/src/cli/cmd/run/footer.question.tsx +579 -0
  106. package/src/cli/cmd/run/footer.subagent.tsx +171 -0
  107. package/src/cli/cmd/run/footer.ts +1092 -0
  108. package/src/cli/cmd/run/footer.view.tsx +935 -0
  109. package/src/cli/cmd/run/otel.ts +117 -0
  110. package/src/cli/cmd/run/permission.shared.ts +256 -0
  111. package/src/cli/cmd/run/prompt.shared.ts +147 -0
  112. package/src/cli/cmd/run/question.shared.ts +340 -0
  113. package/src/cli/cmd/run/runtime.boot.ts +210 -0
  114. package/src/cli/cmd/run/runtime.lifecycle.ts +369 -0
  115. package/src/cli/cmd/run/runtime.queue.ts +347 -0
  116. package/src/cli/cmd/run/runtime.shared.ts +17 -0
  117. package/src/cli/cmd/run/runtime.stdin.ts +37 -0
  118. package/src/cli/cmd/run/runtime.ts +879 -0
  119. package/src/cli/cmd/run/scrollback.shared.ts +92 -0
  120. package/src/cli/cmd/run/scrollback.surface.ts +435 -0
  121. package/src/cli/cmd/run/scrollback.writer.tsx +335 -0
  122. package/src/cli/cmd/run/session-data.ts +1113 -0
  123. package/src/cli/cmd/run/session-replay.ts +301 -0
  124. package/src/cli/cmd/run/session.shared.ts +196 -0
  125. package/src/cli/cmd/run/splash.ts +310 -0
  126. package/src/cli/cmd/run/stream.transport.ts +1465 -0
  127. package/src/cli/cmd/run/stream.ts +175 -0
  128. package/src/cli/cmd/run/subagent-data.ts +844 -0
  129. package/src/cli/cmd/run/theme.ts +603 -0
  130. package/src/cli/cmd/run/tool.ts +1489 -0
  131. package/src/cli/cmd/run/trace.ts +94 -0
  132. package/src/cli/cmd/run/types.ts +342 -0
  133. package/src/cli/cmd/run/variant.shared.ts +215 -0
  134. package/src/cli/cmd/run.ts +879 -0
  135. package/src/cli/cmd/serve.ts +24 -0
  136. package/src/cli/cmd/session.ts +147 -0
  137. package/src/cli/cmd/stats.ts +393 -0
  138. package/src/cli/cmd/tui/app.tsx +1113 -0
  139. package/src/cli/cmd/tui/attach.ts +103 -0
  140. package/src/cli/cmd/tui/attention.ts +262 -0
  141. package/src/cli/cmd/tui/component/bg-pulse-render.ts +436 -0
  142. package/src/cli/cmd/tui/component/bg-pulse.tsx +99 -0
  143. package/src/cli/cmd/tui/component/border.tsx +21 -0
  144. package/src/cli/cmd/tui/component/command-palette.tsx +79 -0
  145. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  146. package/src/cli/cmd/tui/component/dialog-console-org.tsx +103 -0
  147. package/src/cli/cmd/tui/component/dialog-mcp.tsx +85 -0
  148. package/src/cli/cmd/tui/component/dialog-model.tsx +185 -0
  149. package/src/cli/cmd/tui/component/dialog-move-session.tsx +240 -0
  150. package/src/cli/cmd/tui/component/dialog-provider.tsx +687 -0
  151. package/src/cli/cmd/tui/component/dialog-retry-action.tsx +160 -0
  152. package/src/cli/cmd/tui/component/dialog-session-delete-failed.tsx +99 -0
  153. package/src/cli/cmd/tui/component/dialog-session-list.tsx +318 -0
  154. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  155. package/src/cli/cmd/tui/component/dialog-skill.tsx +36 -0
  156. package/src/cli/cmd/tui/component/dialog-stash.tsx +87 -0
  157. package/src/cli/cmd/tui/component/dialog-status.tsx +168 -0
  158. package/src/cli/cmd/tui/component/dialog-tag.tsx +47 -0
  159. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
  160. package/src/cli/cmd/tui/component/dialog-variant.tsx +39 -0
  161. package/src/cli/cmd/tui/component/dialog-workspace-create.tsx +313 -0
  162. package/src/cli/cmd/tui/component/dialog-workspace-file-changes.tsx +144 -0
  163. package/src/cli/cmd/tui/component/dialog-workspace-list.tsx +112 -0
  164. package/src/cli/cmd/tui/component/dialog-workspace-unavailable.tsx +69 -0
  165. package/src/cli/cmd/tui/component/error-component.tsx +81 -0
  166. package/src/cli/cmd/tui/component/logo.tsx +885 -0
  167. package/src/cli/cmd/tui/component/plugin-route-missing.tsx +14 -0
  168. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +799 -0
  169. package/src/cli/cmd/tui/component/prompt/cwd.ts +0 -0
  170. package/src/cli/cmd/tui/component/prompt/frecency.tsx +90 -0
  171. package/src/cli/cmd/tui/component/prompt/history.tsx +117 -0
  172. package/src/cli/cmd/tui/component/prompt/index.tsx +1725 -0
  173. package/src/cli/cmd/tui/component/prompt/move.tsx +192 -0
  174. package/src/cli/cmd/tui/component/prompt/part.ts +31 -0
  175. package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
  176. package/src/cli/cmd/tui/component/prompt/traits.ts +35 -0
  177. package/src/cli/cmd/tui/component/prompt/workspace.tsx +137 -0
  178. package/src/cli/cmd/tui/component/spinner.tsx +24 -0
  179. package/src/cli/cmd/tui/component/startup-loading.tsx +63 -0
  180. package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
  181. package/src/cli/cmd/tui/component/use-connected.tsx +9 -0
  182. package/src/cli/cmd/tui/component/workspace-label.tsx +19 -0
  183. package/src/cli/cmd/tui/config/cwd.ts +5 -0
  184. package/src/cli/cmd/tui/config/keybind.ts +467 -0
  185. package/src/cli/cmd/tui/config/tui-migrate.ts +154 -0
  186. package/src/cli/cmd/tui/config/tui-schema.ts +88 -0
  187. package/src/cli/cmd/tui/config/tui.ts +308 -0
  188. package/src/cli/cmd/tui/context/agent.tsx +11 -0
  189. package/src/cli/cmd/tui/context/aggregate-failures.ts +51 -0
  190. package/src/cli/cmd/tui/context/args.tsx +15 -0
  191. package/src/cli/cmd/tui/context/directory.ts +15 -0
  192. package/src/cli/cmd/tui/context/editor-zed.ts +287 -0
  193. package/src/cli/cmd/tui/context/editor.ts +469 -0
  194. package/src/cli/cmd/tui/context/event.ts +38 -0
  195. package/src/cli/cmd/tui/context/exit.tsx +42 -0
  196. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  197. package/src/cli/cmd/tui/context/kv.tsx +76 -0
  198. package/src/cli/cmd/tui/context/local.tsx +510 -0
  199. package/src/cli/cmd/tui/context/path-format.tsx +39 -0
  200. package/src/cli/cmd/tui/context/project.tsx +111 -0
  201. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  202. package/src/cli/cmd/tui/context/route.tsx +52 -0
  203. package/src/cli/cmd/tui/context/sdk.tsx +142 -0
  204. package/src/cli/cmd/tui/context/sync-v2.tsx +447 -0
  205. package/src/cli/cmd/tui/context/sync.tsx +628 -0
  206. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  207. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  208. package/src/cli/cmd/tui/context/theme/carbonfox.json +248 -0
  209. package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +230 -0
  210. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +230 -0
  211. package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
  212. package/src/cli/cmd/tui/context/theme/cobalt2.json +225 -0
  213. package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
  214. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  215. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  216. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  217. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  218. package/src/cli/cmd/tui/context/theme/gruvbox.json +242 -0
  219. package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
  220. package/src/cli/cmd/tui/context/theme/lucent-orng.json +234 -0
  221. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  222. package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
  223. package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
  224. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  225. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  226. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  227. package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
  228. package/src/cli/cmd/tui/context/theme/opencode.json +245 -0
  229. package/src/cli/cmd/tui/context/theme/orng.json +249 -0
  230. package/src/cli/cmd/tui/context/theme/oryna.json +95 -0
  231. package/src/cli/cmd/tui/context/theme/osaka-jade.json +93 -0
  232. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  233. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  234. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  235. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  236. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  237. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  238. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  239. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  240. package/src/cli/cmd/tui/context/theme.tsx +1341 -0
  241. package/src/cli/cmd/tui/context/thinking.ts +67 -0
  242. package/src/cli/cmd/tui/context/tui-config.tsx +9 -0
  243. package/src/cli/cmd/tui/event.ts +53 -0
  244. package/src/cli/cmd/tui/feature-plugins/home/footer.tsx +98 -0
  245. package/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx +288 -0
  246. package/src/cli/cmd/tui/feature-plugins/home/tips.tsx +59 -0
  247. package/src/cli/cmd/tui/feature-plugins/session/dialog.tsx +356 -0
  248. package/src/cli/cmd/tui/feature-plugins/session/index.tsx +32 -0
  249. package/src/cli/cmd/tui/feature-plugins/session/preview-pane.tsx +288 -0
  250. package/src/cli/cmd/tui/feature-plugins/session/util.tsx +54 -0
  251. package/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx +65 -0
  252. package/src/cli/cmd/tui/feature-plugins/sidebar/files.tsx +70 -0
  253. package/src/cli/cmd/tui/feature-plugins/sidebar/footer.tsx +96 -0
  254. package/src/cli/cmd/tui/feature-plugins/sidebar/lsp.tsx +65 -0
  255. package/src/cli/cmd/tui/feature-plugins/sidebar/mcp.tsx +97 -0
  256. package/src/cli/cmd/tui/feature-plugins/sidebar/todo.tsx +49 -0
  257. package/src/cli/cmd/tui/feature-plugins/system/diff-viewer-file-tree-utils.ts +232 -0
  258. package/src/cli/cmd/tui/feature-plugins/system/diff-viewer-file-tree.tsx +162 -0
  259. package/src/cli/cmd/tui/feature-plugins/system/diff-viewer-ui.tsx +103 -0
  260. package/src/cli/cmd/tui/feature-plugins/system/diff-viewer.tsx +1058 -0
  261. package/src/cli/cmd/tui/feature-plugins/system/notifications.ts +94 -0
  262. package/src/cli/cmd/tui/feature-plugins/system/plugins.tsx +269 -0
  263. package/src/cli/cmd/tui/feature-plugins/system/session-v2.tsx +1184 -0
  264. package/src/cli/cmd/tui/feature-plugins/system/which-key.tsx +608 -0
  265. package/src/cli/cmd/tui/keymap.tsx +283 -0
  266. package/src/cli/cmd/tui/layer.ts +6 -0
  267. package/src/cli/cmd/tui/plugin/api.tsx +390 -0
  268. package/src/cli/cmd/tui/plugin/command-shim.ts +109 -0
  269. package/src/cli/cmd/tui/plugin/internal.ts +42 -0
  270. package/src/cli/cmd/tui/plugin/runtime.ts +1131 -0
  271. package/src/cli/cmd/tui/plugin/slots.tsx +60 -0
  272. package/src/cli/cmd/tui/routes/home/session-destination.tsx +39 -0
  273. package/src/cli/cmd/tui/routes/home.tsx +149 -0
  274. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +76 -0
  275. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +108 -0
  276. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
  277. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
  278. package/src/cli/cmd/tui/routes/session/footer.tsx +91 -0
  279. package/src/cli/cmd/tui/routes/session/index.tsx +2629 -0
  280. package/src/cli/cmd/tui/routes/session/permission.tsx +729 -0
  281. package/src/cli/cmd/tui/routes/session/question.tsx +514 -0
  282. package/src/cli/cmd/tui/routes/session/sidebar.tsx +102 -0
  283. package/src/cli/cmd/tui/routes/session/subagent-footer.tsx +132 -0
  284. package/src/cli/cmd/tui/thread.ts +264 -0
  285. package/src/cli/cmd/tui/ui/dialog-alert.tsx +66 -0
  286. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +108 -0
  287. package/src/cli/cmd/tui/ui/dialog-export-options.tsx +217 -0
  288. package/src/cli/cmd/tui/ui/dialog-help.tsx +40 -0
  289. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +126 -0
  290. package/src/cli/cmd/tui/ui/dialog-select.tsx +712 -0
  291. package/src/cli/cmd/tui/ui/dialog.tsx +218 -0
  292. package/src/cli/cmd/tui/ui/link.tsx +34 -0
  293. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  294. package/src/cli/cmd/tui/ui/toast.tsx +102 -0
  295. package/src/cli/cmd/tui/util/audio.ts +58 -0
  296. package/src/cli/cmd/tui/util/clipboard.ts +181 -0
  297. package/src/cli/cmd/tui/util/collapse-tool-output.ts +19 -0
  298. package/src/cli/cmd/tui/util/editor.ts +43 -0
  299. package/src/cli/cmd/tui/util/layout.ts +25 -0
  300. package/src/cli/cmd/tui/util/model.ts +23 -0
  301. package/src/cli/cmd/tui/util/provider-origin.ts +7 -0
  302. package/src/cli/cmd/tui/util/revert-diff.ts +18 -0
  303. package/src/cli/cmd/tui/util/scroll.ts +25 -0
  304. package/src/cli/cmd/tui/util/selection.ts +73 -0
  305. package/src/cli/cmd/tui/util/signal.ts +41 -0
  306. package/src/cli/cmd/tui/util/transcript.ts +112 -0
  307. package/src/cli/cmd/tui/validate-session.ts +29 -0
  308. package/src/cli/cmd/tui/win32.ts +130 -0
  309. package/src/cli/cmd/tui/worker.ts +99 -0
  310. package/src/cli/cmd/uninstall.ts +353 -0
  311. package/src/cli/cmd/upgrade.ts +74 -0
  312. package/src/cli/cmd/web.ts +84 -0
  313. package/src/cli/effect/prompt.ts +37 -0
  314. package/src/cli/effect-cmd.ts +96 -0
  315. package/src/cli/error.ts +118 -0
  316. package/src/cli/heap.ts +59 -0
  317. package/src/cli/logo.ts +21 -0
  318. package/src/cli/network.ts +64 -0
  319. package/src/cli/ui.ts +132 -0
  320. package/src/cli/upgrade.ts +53 -0
  321. package/src/command/index.ts +181 -0
  322. package/src/command/template/initialize.txt +66 -0
  323. package/src/command/template/review.txt +101 -0
  324. package/src/config/agent.ts +68 -0
  325. package/src/config/command.ts +45 -0
  326. package/src/config/config.ts +679 -0
  327. package/src/config/entry-name.ts +19 -0
  328. package/src/config/managed.ts +77 -0
  329. package/src/config/markdown.ts +36 -0
  330. package/src/config/parse.ts +79 -0
  331. package/src/config/paths.ts +45 -0
  332. package/src/config/plugin.ts +79 -0
  333. package/src/config/reference.ts +48 -0
  334. package/src/config/variable.ts +91 -0
  335. package/src/control-plane/adapters/index.ts +41 -0
  336. package/src/control-plane/adapters/worktree.ts +96 -0
  337. package/src/control-plane/dev/README.md +19 -0
  338. package/src/control-plane/dev/debug-workspace-plugin.ts +73 -0
  339. package/src/control-plane/types.ts +59 -0
  340. package/src/control-plane/util.ts +39 -0
  341. package/src/control-plane/workspace-adapter-runtime.ts +51 -0
  342. package/src/control-plane/workspace-context.ts +26 -0
  343. package/src/control-plane/workspace.ts +1075 -0
  344. package/src/effect/app-runtime.ts +133 -0
  345. package/src/effect/bootstrap-runtime.ts +23 -0
  346. package/src/effect/bridge.ts +84 -0
  347. package/src/effect/config-service.ts +67 -0
  348. package/src/effect/instance-ref.ts +11 -0
  349. package/src/effect/instance-registry.ts +12 -0
  350. package/src/effect/instance-state.ts +72 -0
  351. package/src/effect/promise.ts +17 -0
  352. package/src/effect/run-service.ts +47 -0
  353. package/src/effect/runner.ts +217 -0
  354. package/src/effect/runtime-flags.ts +76 -0
  355. package/src/env/index.ts +40 -0
  356. package/src/event-v2-bridge.ts +76 -0
  357. package/src/format/formatter.ts +404 -0
  358. package/src/format/index.ts +212 -0
  359. package/src/git/index.ts +347 -0
  360. package/src/id/id.ts +80 -0
  361. package/src/ide/index.ts +70 -0
  362. package/src/image/image.ts +177 -0
  363. package/src/index.ts +208 -0
  364. package/src/installation/index.ts +349 -0
  365. package/src/lsp/client.ts +686 -0
  366. package/src/lsp/diagnostic.ts +29 -0
  367. package/src/lsp/language.ts +121 -0
  368. package/src/lsp/launch.ts +21 -0
  369. package/src/lsp/lsp.ts +517 -0
  370. package/src/lsp/server.ts +2064 -0
  371. package/src/markdown.d.ts +4 -0
  372. package/src/mcp/auth.ts +171 -0
  373. package/src/mcp/index.ts +982 -0
  374. package/src/mcp/oauth-callback.ts +232 -0
  375. package/src/mcp/oauth-provider.ts +217 -0
  376. package/src/node.ts +5 -0
  377. package/src/oryna/agent.ts +112 -0
  378. package/src/oryna/reply-service.ts +8 -0
  379. package/src/patch/index.ts +689 -0
  380. package/src/permission/arity.ts +163 -0
  381. package/src/permission/evaluate.ts +1 -0
  382. package/src/permission/index.ts +230 -0
  383. package/src/plugin/azure.ts +26 -0
  384. package/src/plugin/cloudflare.ts +76 -0
  385. package/src/plugin/digitalocean.ts +391 -0
  386. package/src/plugin/github-copilot/copilot.ts +417 -0
  387. package/src/plugin/github-copilot/models.ts +246 -0
  388. package/src/plugin/index.ts +323 -0
  389. package/src/plugin/install.ts +439 -0
  390. package/src/plugin/loader.ts +237 -0
  391. package/src/plugin/meta.ts +188 -0
  392. package/src/plugin/openai/README.md +31 -0
  393. package/src/plugin/openai/codex.ts +647 -0
  394. package/src/plugin/openai/ws-pool.ts +290 -0
  395. package/src/plugin/openai/ws.ts +381 -0
  396. package/src/plugin/oryna.ts +349 -0
  397. package/src/plugin/shared.ts +323 -0
  398. package/src/plugin/xai.ts +742 -0
  399. package/src/project/bootstrap-service.ts +9 -0
  400. package/src/project/bootstrap.ts +80 -0
  401. package/src/project/instance-context.ts +24 -0
  402. package/src/project/instance-layer.ts +11 -0
  403. package/src/project/instance-runtime.ts +16 -0
  404. package/src/project/instance-store.ts +207 -0
  405. package/src/project/project.ts +520 -0
  406. package/src/project/vcs.ts +435 -0
  407. package/src/provider/auth.ts +230 -0
  408. package/src/provider/error.ts +188 -0
  409. package/src/provider/model-status.ts +8 -0
  410. package/src/provider/provider.ts +2009 -0
  411. package/src/provider/transform.ts +1363 -0
  412. package/src/pty-preparation.ts +30 -0
  413. package/src/question/index.ts +229 -0
  414. package/src/question/schema.ts +10 -0
  415. package/src/reference/reference.ts +239 -0
  416. package/src/reference/repository-cache.ts +320 -0
  417. package/src/server/auth.ts +48 -0
  418. package/src/server/cors.ts +34 -0
  419. package/src/server/event.ts +13 -0
  420. package/src/server/global-lifecycle.ts +37 -0
  421. package/src/server/init-projectors.ts +3 -0
  422. package/src/server/mdns.ts +60 -0
  423. package/src/server/projectors.ts +1 -0
  424. package/src/server/proxy-util.ts +48 -0
  425. package/src/server/routes/instance/httpapi/AGENTS.md +39 -0
  426. package/src/server/routes/instance/httpapi/api.ts +78 -0
  427. package/src/server/routes/instance/httpapi/errors.ts +193 -0
  428. package/src/server/routes/instance/httpapi/groups/config.ts +65 -0
  429. package/src/server/routes/instance/httpapi/groups/control-plane.ts +35 -0
  430. package/src/server/routes/instance/httpapi/groups/control.ts +76 -0
  431. package/src/server/routes/instance/httpapi/groups/event.ts +29 -0
  432. package/src/server/routes/instance/httpapi/groups/experimental.ts +260 -0
  433. package/src/server/routes/instance/httpapi/groups/file.ts +172 -0
  434. package/src/server/routes/instance/httpapi/groups/global.ts +138 -0
  435. package/src/server/routes/instance/httpapi/groups/instance.ts +206 -0
  436. package/src/server/routes/instance/httpapi/groups/mcp.ts +156 -0
  437. package/src/server/routes/instance/httpapi/groups/metadata.ts +18 -0
  438. package/src/server/routes/instance/httpapi/groups/permission.ts +61 -0
  439. package/src/server/routes/instance/httpapi/groups/project-copy.ts +88 -0
  440. package/src/server/routes/instance/httpapi/groups/project.ts +93 -0
  441. package/src/server/routes/instance/httpapi/groups/provider.ts +101 -0
  442. package/src/server/routes/instance/httpapi/groups/pty.ts +172 -0
  443. package/src/server/routes/instance/httpapi/groups/query.ts +12 -0
  444. package/src/server/routes/instance/httpapi/groups/question.ts +74 -0
  445. package/src/server/routes/instance/httpapi/groups/session.ts +462 -0
  446. package/src/server/routes/instance/httpapi/groups/sync.ts +113 -0
  447. package/src/server/routes/instance/httpapi/groups/tui.ts +208 -0
  448. package/src/server/routes/instance/httpapi/groups/workspace.ts +141 -0
  449. package/src/server/routes/instance/httpapi/handlers/config.ts +151 -0
  450. package/src/server/routes/instance/httpapi/handlers/control-plane.ts +37 -0
  451. package/src/server/routes/instance/httpapi/handlers/control.ts +40 -0
  452. package/src/server/routes/instance/httpapi/handlers/event.ts +102 -0
  453. package/src/server/routes/instance/httpapi/handlers/experimental.ts +187 -0
  454. package/src/server/routes/instance/httpapi/handlers/file.ts +103 -0
  455. package/src/server/routes/instance/httpapi/handlers/global.ts +157 -0
  456. package/src/server/routes/instance/httpapi/handlers/instance.ts +110 -0
  457. package/src/server/routes/instance/httpapi/handlers/mcp.ts +111 -0
  458. package/src/server/routes/instance/httpapi/handlers/permission.ts +41 -0
  459. package/src/server/routes/instance/httpapi/handlers/project-copy.ts +157 -0
  460. package/src/server/routes/instance/httpapi/handlers/project.ts +63 -0
  461. package/src/server/routes/instance/httpapi/handlers/provider.ts +160 -0
  462. package/src/server/routes/instance/httpapi/handlers/pty.ts +258 -0
  463. package/src/server/routes/instance/httpapi/handlers/question.ts +54 -0
  464. package/src/server/routes/instance/httpapi/handlers/session-errors.ts +21 -0
  465. package/src/server/routes/instance/httpapi/handlers/session.ts +442 -0
  466. package/src/server/routes/instance/httpapi/handlers/sync.ts +95 -0
  467. package/src/server/routes/instance/httpapi/handlers/tui.ts +131 -0
  468. package/src/server/routes/instance/httpapi/handlers/workspace.ts +102 -0
  469. package/src/server/routes/instance/httpapi/lifecycle.ts +57 -0
  470. package/src/server/routes/instance/httpapi/middleware/authorization.ts +147 -0
  471. package/src/server/routes/instance/httpapi/middleware/compression.ts +64 -0
  472. package/src/server/routes/instance/httpapi/middleware/cors-vary.ts +29 -0
  473. package/src/server/routes/instance/httpapi/middleware/error.ts +36 -0
  474. package/src/server/routes/instance/httpapi/middleware/fence.ts +25 -0
  475. package/src/server/routes/instance/httpapi/middleware/instance-context.ts +43 -0
  476. package/src/server/routes/instance/httpapi/middleware/proxy.ts +108 -0
  477. package/src/server/routes/instance/httpapi/middleware/schema-error.ts +42 -0
  478. package/src/server/routes/instance/httpapi/middleware/workspace-routing.ts +250 -0
  479. package/src/server/routes/instance/httpapi/public.ts +535 -0
  480. package/src/server/routes/instance/httpapi/server.ts +277 -0
  481. package/src/server/routes/instance/httpapi/websocket-tracker.ts +57 -0
  482. package/src/server/server.ts +218 -0
  483. package/src/server/shared/fence.ts +68 -0
  484. package/src/server/shared/pty-ticket.ts +15 -0
  485. package/src/server/shared/public-ui.ts +12 -0
  486. package/src/server/shared/tui-control.ts +28 -0
  487. package/src/server/shared/ui.ts +108 -0
  488. package/src/server/shared/workspace-routing.ts +38 -0
  489. package/src/session/compaction.ts +609 -0
  490. package/src/session/instruction.ts +237 -0
  491. package/src/session/llm/AGENTS.md +90 -0
  492. package/src/session/llm/ai-sdk.ts +288 -0
  493. package/src/session/llm/native-request.ts +196 -0
  494. package/src/session/llm/native-runtime.ts +195 -0
  495. package/src/session/llm/request.ts +215 -0
  496. package/src/session/llm.ts +402 -0
  497. package/src/session/message-error.ts +14 -0
  498. package/src/session/message-v2.ts +745 -0
  499. package/src/session/message.ts +148 -0
  500. package/src/session/overflow.ts +34 -0
  501. package/src/session/processor.ts +1063 -0
  502. package/src/session/prompt/anthropic.txt +105 -0
  503. package/src/session/prompt/beast.txt +147 -0
  504. package/src/session/prompt/build-switch.txt +5 -0
  505. package/src/session/prompt/codex.txt +79 -0
  506. package/src/session/prompt/copilot-gpt-5.txt +143 -0
  507. package/src/session/prompt/default.txt +95 -0
  508. package/src/session/prompt/gemini.txt +155 -0
  509. package/src/session/prompt/gpt.txt +107 -0
  510. package/src/session/prompt/kimi.txt +95 -0
  511. package/src/session/prompt/max-steps.txt +16 -0
  512. package/src/session/prompt/plan-mode.txt +70 -0
  513. package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
  514. package/src/session/prompt/plan.txt +26 -0
  515. package/src/session/prompt/reference.ts +72 -0
  516. package/src/session/prompt/trinity.txt +97 -0
  517. package/src/session/prompt.ts +1755 -0
  518. package/src/session/reminders.ts +92 -0
  519. package/src/session/retry.ts +201 -0
  520. package/src/session/revert.ts +153 -0
  521. package/src/session/run-state.ts +153 -0
  522. package/src/session/schema.ts +26 -0
  523. package/src/session/session.ts +1116 -0
  524. package/src/session/status.ts +94 -0
  525. package/src/session/summary.ts +162 -0
  526. package/src/session/system.ts +84 -0
  527. package/src/session/todo.ts +87 -0
  528. package/src/session/tools.ts +211 -0
  529. package/src/share/session.ts +58 -0
  530. package/src/share/share-next.ts +379 -0
  531. package/src/shell/shell.ts +215 -0
  532. package/src/skill/discovery.ts +115 -0
  533. package/src/skill/index.ts +357 -0
  534. package/src/snapshot/index.ts +759 -0
  535. package/src/sql.d.ts +4 -0
  536. package/src/storage/schema.ts +5 -0
  537. package/src/storage/storage.ts +329 -0
  538. package/src/sync/README.md +179 -0
  539. package/src/sync/schema.ts +11 -0
  540. package/src/temporary.ts +33 -0
  541. package/src/tool/apply_patch.ts +313 -0
  542. package/src/tool/apply_patch.txt +33 -0
  543. package/src/tool/edit.ts +737 -0
  544. package/src/tool/edit.txt +10 -0
  545. package/src/tool/external-directory.ts +49 -0
  546. package/src/tool/glob.ts +84 -0
  547. package/src/tool/glob.txt +6 -0
  548. package/src/tool/grep.ts +140 -0
  549. package/src/tool/grep.txt +8 -0
  550. package/src/tool/invalid.ts +21 -0
  551. package/src/tool/json-schema.ts +164 -0
  552. package/src/tool/lsp.ts +113 -0
  553. package/src/tool/lsp.txt +24 -0
  554. package/src/tool/mcp-websearch.ts +96 -0
  555. package/src/tool/plan-enter.txt +14 -0
  556. package/src/tool/plan-exit.txt +13 -0
  557. package/src/tool/plan.ts +79 -0
  558. package/src/tool/question.ts +44 -0
  559. package/src/tool/question.txt +10 -0
  560. package/src/tool/read.ts +392 -0
  561. package/src/tool/read.txt +14 -0
  562. package/src/tool/registry.ts +475 -0
  563. package/src/tool/reply.ts +29 -0
  564. package/src/tool/schema.ts +14 -0
  565. package/src/tool/shell/id.ts +19 -0
  566. package/src/tool/shell/prompt.ts +307 -0
  567. package/src/tool/shell/shell.txt +21 -0
  568. package/src/tool/shell.ts +660 -0
  569. package/src/tool/skill.ts +72 -0
  570. package/src/tool/skill.txt +5 -0
  571. package/src/tool/task.ts +338 -0
  572. package/src/tool/task.txt +19 -0
  573. package/src/tool/todo.ts +57 -0
  574. package/src/tool/todowrite.txt +44 -0
  575. package/src/tool/tool.ts +183 -0
  576. package/src/tool/truncate.ts +160 -0
  577. package/src/tool/truncation-dir.ts +4 -0
  578. package/src/tool/webfetch.ts +192 -0
  579. package/src/tool/webfetch.txt +13 -0
  580. package/src/tool/websearch.ts +143 -0
  581. package/src/tool/websearch.txt +14 -0
  582. package/src/tool/write.ts +104 -0
  583. package/src/tool/write.txt +8 -0
  584. package/src/util/archive.ts +17 -0
  585. package/src/util/bom.ts +27 -0
  586. package/src/util/data-url.ts +9 -0
  587. package/src/util/defer.ts +10 -0
  588. package/src/util/effect-http-client.ts +11 -0
  589. package/src/util/error.ts +88 -0
  590. package/src/util/filesystem.ts +251 -0
  591. package/src/util/format.ts +20 -0
  592. package/src/util/iife.ts +3 -0
  593. package/src/util/lan-scan.ts +90 -0
  594. package/src/util/lazy.ts +20 -0
  595. package/src/util/local-context.ts +25 -0
  596. package/src/util/locale.ts +86 -0
  597. package/src/util/media.ts +26 -0
  598. package/src/util/process.ts +176 -0
  599. package/src/util/proxy-env.ts +72 -0
  600. package/src/util/queue.ts +32 -0
  601. package/src/util/record.ts +3 -0
  602. package/src/util/repository.ts +232 -0
  603. package/src/util/rpc.ts +66 -0
  604. package/src/util/signal.ts +12 -0
  605. package/src/util/timeout.ts +13 -0
  606. package/src/util/token.ts +1 -0
  607. package/src/util/wildcard.ts +59 -0
  608. package/src/worktree/index.ts +645 -0
  609. package/sst-env.d.ts +10 -0
  610. package/test/AGENTS.md +204 -0
  611. package/test/EFFECT_TEST_MIGRATION.md +169 -0
  612. package/test/account/repo.test.ts +353 -0
  613. package/test/account/service.test.ts +453 -0
  614. package/test/acp/config-option.test.ts +229 -0
  615. package/test/acp/content.test.ts +201 -0
  616. package/test/acp/directory.test.ts +186 -0
  617. package/test/acp/error.test.ts +67 -0
  618. package/test/acp/event.test.ts +711 -0
  619. package/test/acp/permission.test.ts +273 -0
  620. package/test/acp/service-session.test.ts +1174 -0
  621. package/test/acp/session.test.ts +200 -0
  622. package/test/acp/tool.test.ts +210 -0
  623. package/test/acp/usage.test.ts +315 -0
  624. package/test/agent/agent.test.ts +710 -0
  625. package/test/agent/plan-mode-subagent-bypass.test.ts +213 -0
  626. package/test/agent/plugin-agent-regression.test.ts +62 -0
  627. package/test/auth/auth.test.ts +77 -0
  628. package/test/background/job.test.ts +243 -0
  629. package/test/cli/account.test.ts +30 -0
  630. package/test/cli/acp/acp-test-client.ts +97 -0
  631. package/test/cli/acp/config-options.test.ts +103 -0
  632. package/test/cli/acp/helpers.ts +96 -0
  633. package/test/cli/acp/initialize-auth.test.ts +61 -0
  634. package/test/cli/acp/lifecycle.test.ts +118 -0
  635. package/test/cli/acp/prompt-content.test.ts +97 -0
  636. package/test/cli/acp/skills.test.ts +38 -0
  637. package/test/cli/cmd/tui/aggregate-failures.test.ts +93 -0
  638. package/test/cli/cmd/tui/attention.test.ts +484 -0
  639. package/test/cli/cmd/tui/dialog-workspace-create.test.ts +28 -0
  640. package/test/cli/cmd/tui/model-options.test.ts +30 -0
  641. package/test/cli/cmd/tui/notifications.test.ts +267 -0
  642. package/test/cli/cmd/tui/prompt-history.test.ts +44 -0
  643. package/test/cli/cmd/tui/prompt-part.test.ts +77 -0
  644. package/test/cli/cmd/tui/prompt-traits.test.ts +29 -0
  645. package/test/cli/cmd/tui/provider-options.test.ts +29 -0
  646. package/test/cli/cmd/tui/sync-fixture.tsx +64 -0
  647. package/test/cli/cmd/tui/sync-live-hydration.test.tsx +278 -0
  648. package/test/cli/cmd/tui/sync-undefined-messages.test.tsx +47 -0
  649. package/test/cli/cmd/tui/sync.test.tsx +70 -0
  650. package/test/cli/effect-cmd-instance-als.test.ts +39 -0
  651. package/test/cli/error.test.ts +95 -0
  652. package/test/cli/github-action.test.ts +199 -0
  653. package/test/cli/github-remote.test.ts +90 -0
  654. package/test/cli/help/__snapshots__/help-snapshots.test.ts.snap +631 -0
  655. package/test/cli/help/help-snapshots.test.ts +137 -0
  656. package/test/cli/import.test.ts +54 -0
  657. package/test/cli/mcp-add.test.ts +74 -0
  658. package/test/cli/plugin-auth-picker.test.ts +120 -0
  659. package/test/cli/run/entry.body.test.ts +536 -0
  660. package/test/cli/run/footer.menu.test.ts +43 -0
  661. package/test/cli/run/footer.view.test.tsx +927 -0
  662. package/test/cli/run/permission.shared.test.ts +144 -0
  663. package/test/cli/run/prompt.shared.test.ts +133 -0
  664. package/test/cli/run/question.shared.test.ts +115 -0
  665. package/test/cli/run/run-process.test.ts +84 -0
  666. package/test/cli/run/runtime.boot.test.ts +282 -0
  667. package/test/cli/run/runtime.queue.test.ts +465 -0
  668. package/test/cli/run/runtime.stdin.test.ts +71 -0
  669. package/test/cli/run/scrollback.surface.test.ts +1048 -0
  670. package/test/cli/run/session-data.test.ts +595 -0
  671. package/test/cli/run/session-replay.test.ts +456 -0
  672. package/test/cli/run/session.shared.test.ts +247 -0
  673. package/test/cli/run/stream.test.ts +56 -0
  674. package/test/cli/run/stream.transport.test.ts +2363 -0
  675. package/test/cli/run/subagent-data.test.ts +456 -0
  676. package/test/cli/run/theme.test.ts +152 -0
  677. package/test/cli/run/variant.shared.test.ts +217 -0
  678. package/test/cli/serve/serve-process.test.ts +61 -0
  679. package/test/cli/smokes/read-only.test.ts +115 -0
  680. package/test/cli/tui/__snapshots__/inline-tool-wrap-snapshot.test.tsx.snap +72 -0
  681. package/test/cli/tui/app-lifecycle.test.ts +261 -0
  682. package/test/cli/tui/dialog-prompt.test.tsx +146 -0
  683. package/test/cli/tui/diff-viewer-file-tree-utils.test.ts +323 -0
  684. package/test/cli/tui/diff-viewer-file-tree.test.tsx +197 -0
  685. package/test/cli/tui/diff-viewer.test.tsx +230 -0
  686. package/test/cli/tui/editor-context-zed.test.ts +384 -0
  687. package/test/cli/tui/editor-context.test.tsx +288 -0
  688. package/test/cli/tui/inline-tool-wrap-snapshot.test.tsx +232 -0
  689. package/test/cli/tui/keymap.test.tsx +136 -0
  690. package/test/cli/tui/plugin-add.test.ts +110 -0
  691. package/test/cli/tui/plugin-install.test.ts +87 -0
  692. package/test/cli/tui/plugin-lifecycle.test.ts +224 -0
  693. package/test/cli/tui/plugin-loader-entrypoint.test.ts +485 -0
  694. package/test/cli/tui/plugin-loader-pure.test.ts +72 -0
  695. package/test/cli/tui/plugin-loader.test.ts +1332 -0
  696. package/test/cli/tui/plugin-toggle.test.ts +264 -0
  697. package/test/cli/tui/prompt-submit-race.test.ts +98 -0
  698. package/test/cli/tui/revert-diff.test.ts +35 -0
  699. package/test/cli/tui/slot-replace.test.tsx +50 -0
  700. package/test/cli/tui/sync-v2.test.tsx +558 -0
  701. package/test/cli/tui/theme-store.test.ts +76 -0
  702. package/test/cli/tui/thinking.test.ts +36 -0
  703. package/test/cli/tui/thread.test.ts +28 -0
  704. package/test/cli/tui/transcript.test.ts +426 -0
  705. package/test/cli/tui/use-event.test.tsx +145 -0
  706. package/test/config/agent-color.test.ts +47 -0
  707. package/test/config/config.test.ts +1991 -0
  708. package/test/config/entry-name.test.ts +57 -0
  709. package/test/config/fixtures/empty-frontmatter.md +4 -0
  710. package/test/config/fixtures/frontmatter.md +28 -0
  711. package/test/config/fixtures/markdown-header.md +11 -0
  712. package/test/config/fixtures/no-frontmatter.md +1 -0
  713. package/test/config/fixtures/weird-model-id.md +13 -0
  714. package/test/config/lsp.test.ts +69 -0
  715. package/test/config/markdown.test.ts +228 -0
  716. package/test/config/plugin.test.ts +0 -0
  717. package/test/config/tui.test.ts +878 -0
  718. package/test/control-plane/adapters.test.ts +71 -0
  719. package/test/control-plane/workspace.test.ts +1704 -0
  720. package/test/effect/app-runtime-logger.test.ts +105 -0
  721. package/test/effect/config-service.test.ts +65 -0
  722. package/test/effect/instance-state.test.ts +391 -0
  723. package/test/effect/run-service.test.ts +89 -0
  724. package/test/effect/runner.test.ts +514 -0
  725. package/test/effect/runtime-flags.test.ts +373 -0
  726. package/test/fake/account.ts +9 -0
  727. package/test/fake/auth.ts +8 -0
  728. package/test/fake/npm.ts +8 -0
  729. package/test/fake/provider.ts +82 -0
  730. package/test/fake/skill.ts +8 -0
  731. package/test/filesystem/filesystem.test.ts +319 -0
  732. package/test/fixture/agent-plugin.constants.ts +6 -0
  733. package/test/fixture/agent-plugin.ts +12 -0
  734. package/test/fixture/config.ts +23 -0
  735. package/test/fixture/db.ts +11 -0
  736. package/test/fixture/fixture.test.ts +26 -0
  737. package/test/fixture/fixture.ts +224 -0
  738. package/test/fixture/flag.ts +20 -0
  739. package/test/fixture/flock-worker.ts +72 -0
  740. package/test/fixture/lsp/fake-lsp-server.js +249 -0
  741. package/test/fixture/plug-worker.ts +93 -0
  742. package/test/fixture/plugin-meta-worker.ts +19 -0
  743. package/test/fixture/plugin.ts +10 -0
  744. package/test/fixture/skills/agents-sdk/SKILL.md +152 -0
  745. package/test/fixture/skills/agents-sdk/references/callable.md +92 -0
  746. package/test/fixture/skills/cloudflare/SKILL.md +211 -0
  747. package/test/fixture/skills/index.json +6 -0
  748. package/test/fixture/tui-plugin.ts +355 -0
  749. package/test/fixture/tui-runtime.ts +64 -0
  750. package/test/fixture/tui-sdk.ts +82 -0
  751. package/test/fixture/workspace.ts +30 -0
  752. package/test/fixtures/recordings/session/native-anthropic-tool-loop.json +49 -0
  753. package/test/fixtures/recordings/session/native-openai-oauth-tool-loop.json +45 -0
  754. package/test/fixtures/recordings/session/native-zen-tool-loop.json +49 -0
  755. package/test/format/format.test.ts +228 -0
  756. package/test/git/git.test.ts +178 -0
  757. package/test/ide/ide.test.ts +82 -0
  758. package/test/image/fixtures/picture-5mb-base64.png +0 -0
  759. package/test/image/image.test.ts +123 -0
  760. package/test/installation/installation.test.ts +230 -0
  761. package/test/lib/cli-process.ts +459 -0
  762. package/test/lib/effect.ts +177 -0
  763. package/test/lib/filesystem.ts +10 -0
  764. package/test/lib/llm-server.ts +771 -0
  765. package/test/lib/snapshot.ts +73 -0
  766. package/test/lib/test-provider.ts +37 -0
  767. package/test/lib/websocket.ts +46 -0
  768. package/test/lsp/client.test.ts +493 -0
  769. package/test/lsp/index.test.ts +232 -0
  770. package/test/lsp/launch.test.ts +22 -0
  771. package/test/lsp/lifecycle.test.ts +160 -0
  772. package/test/mcp/auth.test.ts +78 -0
  773. package/test/mcp/headers.test.ts +126 -0
  774. package/test/mcp/lifecycle.test.ts +888 -0
  775. package/test/mcp/oauth-auto-connect.test.ts +236 -0
  776. package/test/mcp/oauth-browser.test.ts +228 -0
  777. package/test/mcp/oauth-callback.test.ts +34 -0
  778. package/test/mcp/oauth-provider.test.ts +61 -0
  779. package/test/patch/patch.test.ts +383 -0
  780. package/test/permission/arity.test.ts +33 -0
  781. package/test/permission/next.test.ts +1176 -0
  782. package/test/permission-task.test.ts +318 -0
  783. package/test/plugin/auth-override.test.ts +105 -0
  784. package/test/plugin/cloudflare.test.ts +68 -0
  785. package/test/plugin/codex.test.ts +247 -0
  786. package/test/plugin/github-copilot-models.test.ts +332 -0
  787. package/test/plugin/install-concurrency.test.ts +140 -0
  788. package/test/plugin/install.test.ts +570 -0
  789. package/test/plugin/loader-shared.test.ts +1303 -0
  790. package/test/plugin/meta.test.ts +137 -0
  791. package/test/plugin/openai-rollout.test.ts +17 -0
  792. package/test/plugin/openai-ws.test.ts +877 -0
  793. package/test/plugin/shared.test.ts +88 -0
  794. package/test/plugin/trigger.test.ts +120 -0
  795. package/test/plugin/workspace-adapter.test.ts +137 -0
  796. package/test/plugin/xai.test.ts +634 -0
  797. package/test/preload.ts +95 -0
  798. package/test/project/instance-bootstrap.test.ts +110 -0
  799. package/test/project/instance.test.ts +245 -0
  800. package/test/project/migrate-global.test.ts +170 -0
  801. package/test/project/project-directory.test.ts +169 -0
  802. package/test/project/project.test.ts +818 -0
  803. package/test/project/vcs.test.ts +336 -0
  804. package/test/project/worktree-remove.test.ts +126 -0
  805. package/test/project/worktree.test.ts +320 -0
  806. package/test/provider/amazon-bedrock.test.ts +360 -0
  807. package/test/provider/cf-ai-gateway-e2e.test.ts +132 -0
  808. package/test/provider/digitalocean.test.ts +123 -0
  809. package/test/provider/gitlab-duo.test.ts +412 -0
  810. package/test/provider/header-timeout.test.ts +233 -0
  811. package/test/provider/model-status.test.ts +61 -0
  812. package/test/provider/provider.test.ts +1793 -0
  813. package/test/provider/transform.test.ts +3937 -0
  814. package/test/pty/pty-shell.test.ts +102 -0
  815. package/test/question/question.test.ts +465 -0
  816. package/test/reference/reference.test.ts +310 -0
  817. package/test/server/AGENTS.md +15 -0
  818. package/test/server/auth.test.ts +59 -0
  819. package/test/server/global-bus.ts +31 -0
  820. package/test/server/global-session-list.test.ts +107 -0
  821. package/test/server/httpapi-authorization.test.ts +174 -0
  822. package/test/server/httpapi-compression.test.ts +154 -0
  823. package/test/server/httpapi-config.test.ts +113 -0
  824. package/test/server/httpapi-control-plane.test.ts +63 -0
  825. package/test/server/httpapi-cors-vary.test.ts +66 -0
  826. package/test/server/httpapi-cors.test.ts +122 -0
  827. package/test/server/httpapi-error-middleware.test.ts +96 -0
  828. package/test/server/httpapi-event.test.ts +97 -0
  829. package/test/server/httpapi-exercise/assertions.ts +64 -0
  830. package/test/server/httpapi-exercise/backend.ts +144 -0
  831. package/test/server/httpapi-exercise/dsl.ts +210 -0
  832. package/test/server/httpapi-exercise/environment.ts +40 -0
  833. package/test/server/httpapi-exercise/index.ts +1535 -0
  834. package/test/server/httpapi-exercise/report.ts +66 -0
  835. package/test/server/httpapi-exercise/routing.ts +96 -0
  836. package/test/server/httpapi-exercise/runner.ts +267 -0
  837. package/test/server/httpapi-exercise/runtime.ts +52 -0
  838. package/test/server/httpapi-exercise/types.ts +123 -0
  839. package/test/server/httpapi-experimental.test.ts +300 -0
  840. package/test/server/httpapi-file.test.ts +76 -0
  841. package/test/server/httpapi-global.test.ts +66 -0
  842. package/test/server/httpapi-instance-context.test.ts +347 -0
  843. package/test/server/httpapi-instance-route-auth.test.ts +84 -0
  844. package/test/server/httpapi-instance.test.ts +265 -0
  845. package/test/server/httpapi-layer.ts +33 -0
  846. package/test/server/httpapi-listen.test.ts +415 -0
  847. package/test/server/httpapi-mcp-oauth.test.ts +73 -0
  848. package/test/server/httpapi-mcp.test.ts +226 -0
  849. package/test/server/httpapi-mdns.test.ts +82 -0
  850. package/test/server/httpapi-promptasync-context.test.ts +222 -0
  851. package/test/server/httpapi-provider.test.ts +403 -0
  852. package/test/server/httpapi-pty.test.ts +275 -0
  853. package/test/server/httpapi-public-openapi.test.ts +297 -0
  854. package/test/server/httpapi-query-schema-drift.test.ts +330 -0
  855. package/test/server/httpapi-schema-error-body.test.ts +165 -0
  856. package/test/server/httpapi-sdk.test.ts +909 -0
  857. package/test/server/httpapi-session.test.ts +1013 -0
  858. package/test/server/httpapi-sync.test.ts +154 -0
  859. package/test/server/httpapi-ui.test.ts +456 -0
  860. package/test/server/httpapi-v2-location.test.ts +85 -0
  861. package/test/server/httpapi-workspace-routing.test.ts +554 -0
  862. package/test/server/httpapi-workspace.test.ts +515 -0
  863. package/test/server/negative-tokens-regression.test.ts +83 -0
  864. package/test/server/project-copy.test.ts +101 -0
  865. package/test/server/project-init-git.test.ts +117 -0
  866. package/test/server/proxy-util.test.ts +113 -0
  867. package/test/server/sdk-error-shape.test.ts +84 -0
  868. package/test/server/sdk-v1-smoke.test.ts +60 -0
  869. package/test/server/session-actions.test.ts +112 -0
  870. package/test/server/session-diff-missing-patch.test.ts +99 -0
  871. package/test/server/session-list.test.ts +314 -0
  872. package/test/server/session-messages.test.ts +182 -0
  873. package/test/server/session-select.test.ts +69 -0
  874. package/test/server/workspace-proxy.test.ts +181 -0
  875. package/test/server/workspace-routing.test.ts +94 -0
  876. package/test/server/worktree-endpoint-repro.test.ts +307 -0
  877. package/test/session/compaction.test.ts +1835 -0
  878. package/test/session/instruction.test.ts +256 -0
  879. package/test/session/llm-native-recorded.test.ts +433 -0
  880. package/test/session/llm-native.test.ts +760 -0
  881. package/test/session/llm.test.ts +1932 -0
  882. package/test/session/message-v2.test.ts +1661 -0
  883. package/test/session/messages-pagination.test.ts +1059 -0
  884. package/test/session/processor-effect.test.ts +1101 -0
  885. package/test/session/prompt.test.ts +2318 -0
  886. package/test/session/retry.test.ts +439 -0
  887. package/test/session/revert-compact.test.ts +642 -0
  888. package/test/session/schema-decoding.test.ts +313 -0
  889. package/test/session/session-schema.test.ts +78 -0
  890. package/test/session/session.test.ts +251 -0
  891. package/test/session/snapshot-tool-race.test.ts +280 -0
  892. package/test/session/structured-output-integration.test.ts +235 -0
  893. package/test/session/structured-output.test.ts +387 -0
  894. package/test/session/system.test.ts +84 -0
  895. package/test/share/share-next.test.ts +344 -0
  896. package/test/shell/shell.test.ts +99 -0
  897. package/test/skill/discovery.test.ts +139 -0
  898. package/test/skill/skill.test.ts +571 -0
  899. package/test/snapshot/snapshot.test.ts +1121 -0
  900. package/test/storage/storage.test.ts +296 -0
  901. package/test/storage/workspace-time-migration.test.ts +50 -0
  902. package/test/tool/__snapshots__/parameters.test.ts.snap +484 -0
  903. package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
  904. package/test/tool/apply_patch.test.ts +533 -0
  905. package/test/tool/edit.test.ts +578 -0
  906. package/test/tool/external-directory.test.ts +155 -0
  907. package/test/tool/fixtures/large-image.png +0 -0
  908. package/test/tool/fixtures/models-api.json +117299 -0
  909. package/test/tool/glob.test.ts +188 -0
  910. package/test/tool/grep.test.ts +266 -0
  911. package/test/tool/lsp.test.ts +181 -0
  912. package/test/tool/parameters.test.ts +293 -0
  913. package/test/tool/question.test.ts +138 -0
  914. package/test/tool/read.test.ts +654 -0
  915. package/test/tool/registry.test.ts +539 -0
  916. package/test/tool/shell.test.ts +1238 -0
  917. package/test/tool/skill.test.ts +132 -0
  918. package/test/tool/task.test.ts +901 -0
  919. package/test/tool/tool-define.test.ts +153 -0
  920. package/test/tool/truncation.test.ts +266 -0
  921. package/test/tool/webfetch.test.ts +113 -0
  922. package/test/tool/websearch.test.ts +99 -0
  923. package/test/tool/write.test.ts +276 -0
  924. package/test/util/data-url.test.ts +14 -0
  925. package/test/util/error.test.ts +64 -0
  926. package/test/util/filesystem.test.ts +656 -0
  927. package/test/util/format.test.ts +59 -0
  928. package/test/util/glob.test.ts +164 -0
  929. package/test/util/iife.test.ts +36 -0
  930. package/test/util/lazy.test.ts +50 -0
  931. package/test/util/log.test.ts +77 -0
  932. package/test/util/module.test.ts +59 -0
  933. package/test/util/process.test.ts +128 -0
  934. package/test/util/repository.test.ts +93 -0
  935. package/test/util/timeout.test.ts +21 -0
  936. package/test/util/wildcard.test.ts +90 -0
  937. package/test/v2/session-message-updater.test.ts +270 -0
  938. package/tsconfig.json +17 -0
@@ -0,0 +1,1725 @@
1
+ import {
2
+ BoxRenderable,
3
+ RGBA,
4
+ TextareaRenderable,
5
+ MouseEvent,
6
+ PasteEvent,
7
+ decodePasteBytes,
8
+ type KeyEvent,
9
+ type Renderable,
10
+ } from "@opentui/core"
11
+ import type { CommandContext } from "@opentui/keymap"
12
+ import { createEffect, createMemo, onMount, createSignal, onCleanup, on, Show, Switch, Match } from "solid-js"
13
+ import "opentui-spinner/solid"
14
+ import path from "path"
15
+ import { fileURLToPath } from "url"
16
+ import { Filesystem } from "@/util/filesystem"
17
+ import { useLocal } from "@tui/context/local"
18
+ import { tint, useTheme } from "@tui/context/theme"
19
+ import { EmptyBorder, SplitBorder } from "@tui/component/border"
20
+ import { Spinner } from "@tui/component/spinner"
21
+ import { useSDK } from "@tui/context/sdk"
22
+ import { useRoute } from "@tui/context/route"
23
+ import { useProject } from "@tui/context/project"
24
+ import { useSync } from "@tui/context/sync"
25
+ import { useEvent } from "@tui/context/event"
26
+ import { editorSelectionKey, useEditorContext, type EditorSelection } from "@tui/context/editor"
27
+ import { MessageID, PartID } from "@/session/schema"
28
+ import { promptOffsetWidth } from "@/cli/cmd/prompt-display"
29
+ import { createStore, produce, unwrap } from "solid-js/store"
30
+ import { usePromptHistory, type PromptInfo } from "./history"
31
+ import { computePromptTraits } from "./traits"
32
+ import { assign, expandPastedTextPlaceholders, expandTrackedPastedText } from "./part"
33
+ import { usePromptStash } from "./stash"
34
+ import { DialogStash } from "../dialog-stash"
35
+ import { type AutocompleteRef, Autocomplete } from "./autocomplete"
36
+ import { useRenderer, useTerminalDimensions, type JSX } from "@opentui/solid"
37
+ import * as Editor from "@tui/util/editor"
38
+ import { useExit } from "../../context/exit"
39
+ import * as Clipboard from "../../util/clipboard"
40
+ import type { AssistantMessage, FilePart, UserMessage } from "@opencode-ai/sdk/v2"
41
+ import { TuiEvent } from "../../event"
42
+ import { iife } from "@/util/iife"
43
+ import { Locale } from "@/util/locale"
44
+ import { errorMessage } from "@/util/error"
45
+ import { formatDuration } from "@/util/format"
46
+ import { createColors, createFrames } from "../../ui/spinner.ts"
47
+ import { useDialog } from "@tui/ui/dialog"
48
+ import { DialogProvider as DialogProviderConnect } from "../dialog-provider"
49
+ import { DialogAlert } from "../../ui/dialog-alert"
50
+ import { useToast } from "../../ui/toast"
51
+ import { useKV } from "../../context/kv"
52
+ import { createFadeIn } from "../../util/signal"
53
+ import { DialogSkill } from "../dialog-skill"
54
+ import { DialogWorkspaceUnavailable } from "../dialog-workspace-unavailable"
55
+ import { useArgs } from "@tui/context/args"
56
+ import { Flag } from "@opencode-ai/core/flag/flag"
57
+ import { OPENCODE_BASE_MODE, useBindings, useCommandShortcut, useLeaderActive, useOpencodeKeymap } from "../../keymap"
58
+ import { useTuiConfig } from "../../context/tui-config"
59
+ import { usePromptWorkspace } from "./workspace"
60
+ import { usePromptMove } from "./move"
61
+
62
+ export type PromptProps = {
63
+ sessionID?: string
64
+ visible?: boolean
65
+ disabled?: boolean
66
+ onSubmit?: () => void
67
+ ref?: (ref: PromptRef | undefined) => void
68
+ hint?: JSX.Element
69
+ right?: JSX.Element
70
+ showPlaceholder?: boolean
71
+ placeholders?: {
72
+ normal?: string[]
73
+ shell?: string[]
74
+ }
75
+ }
76
+
77
+ export type PromptRef = {
78
+ focused: boolean
79
+ current: PromptInfo
80
+ set(prompt: PromptInfo): void
81
+ reset(): void
82
+ blur(): void
83
+ focus(): void
84
+ submit(): void
85
+ }
86
+
87
+ const money = new Intl.NumberFormat("en-US", {
88
+ style: "currency",
89
+ currency: "USD",
90
+ })
91
+
92
+ const DRAFT_RETENTION_MIN_CHARS = 20
93
+
94
+ function randomIndex(count: number) {
95
+ if (count <= 0) return 0
96
+ return Math.floor(Math.random() * count)
97
+ }
98
+
99
+ function fadeColor(color: RGBA, alpha: number) {
100
+ return RGBA.fromValues(color.r, color.g, color.b, color.a * alpha)
101
+ }
102
+
103
+ function hasEditorRangeSelection(selection: EditorSelection["ranges"][number]) {
104
+ return (
105
+ selection.selection.start.line !== selection.selection.end.line ||
106
+ selection.selection.start.character !== selection.selection.end.character
107
+ )
108
+ }
109
+
110
+ function getEditorRangeLabel(selection: EditorSelection["ranges"][number]) {
111
+ if (!hasEditorRangeSelection(selection)) return
112
+ if (selection.selection.start.line === selection.selection.end.line) return `#${selection.selection.start.line}`
113
+ return `#${selection.selection.start.line}-${selection.selection.end.line}`
114
+ }
115
+
116
+ function formatEditorContext(selection: EditorSelection) {
117
+ const selected = selection.ranges.filter(hasEditorRangeSelection)
118
+ if (selected.length === 0)
119
+ return `<system-reminder>Note: The user opened the file "${selection.filePath}". This may or may not be relevant to the current task.</system-reminder>\n`
120
+
121
+ const ranges = selected.map((range, index) => {
122
+ const prefix = selected.length > 1 ? `Selection ${index + 1}: ` : ""
123
+ return `Note: The user selected ${prefix}${getEditorRangeLabel(range)} from "${selection.filePath}". \`\`\`${range.text}\`\`\`\n\n`
124
+ })
125
+
126
+ return `<system-reminder>${ranges.join("\n")} This may or may not be relevant to the current task.</system-reminder>\n`
127
+ }
128
+
129
+ let stashed: { prompt: PromptInfo; cursor: number } | undefined
130
+
131
+ export function Prompt(props: PromptProps) {
132
+ let input: TextareaRenderable
133
+ let anchor: BoxRenderable
134
+ const [inputTarget, setInputTarget] = createSignal<TextareaRenderable | undefined>()
135
+
136
+ const leader = useLeaderActive()
137
+ const local = useLocal()
138
+ const args = useArgs()
139
+ const sdk = useSDK()
140
+ const editor = useEditorContext()
141
+ const route = useRoute()
142
+ const project = useProject()
143
+ const sync = useSync()
144
+ const tuiConfig = useTuiConfig()
145
+ const dialog = useDialog()
146
+ const toast = useToast()
147
+ const status = createMemo(() => sync.data.session_status?.[props.sessionID ?? ""] ?? { type: "idle" })
148
+ const history = usePromptHistory()
149
+ const stash = usePromptStash()
150
+ const keymap = useOpencodeKeymap()
151
+ const agentShortcut = useCommandShortcut("agent.cycle")
152
+ const paletteShortcut = useCommandShortcut("command.palette.show")
153
+ const renderer = useRenderer()
154
+ const dimensions = useTerminalDimensions()
155
+ const { theme, syntax } = useTheme()
156
+ const kv = useKV()
157
+ const animationsEnabled = createMemo(() => kv.get("animations_enabled", true))
158
+ const list = createMemo(() => props.placeholders?.normal ?? [])
159
+ const shell = createMemo(() => props.placeholders?.shell ?? [])
160
+ const fileContextEnabled = createMemo(() => kv.get("file_context_enabled", true))
161
+ const [dismissedEditorSelectionKey, setDismissedEditorSelectionKey] = createSignal<string>()
162
+ const editorContext = createMemo(() => {
163
+ const selection = fileContextEnabled() ? editor.selection() : undefined
164
+ if (!selection) return
165
+ return editorSelectionKey(selection) === dismissedEditorSelectionKey() ? undefined : selection
166
+ })
167
+ const editorPath = createMemo(() => editorContext()?.filePath)
168
+ const editorSelectionLabel = createMemo(() => {
169
+ const ranges = editorContext()?.ranges
170
+ if (!ranges) return
171
+ const first = ranges.find(hasEditorRangeSelection) ?? ranges[0]
172
+ if (!first) return
173
+ return [getEditorRangeLabel(first), ranges.length > 1 ? `+${ranges.length - 1}` : undefined]
174
+ .filter(Boolean)
175
+ .join(" ")
176
+ })
177
+ const editorFileLabel = createMemo(() => {
178
+ const value = editorPath()
179
+ if (!value) return
180
+ const filename = path.basename(value)
181
+ const file = /^index\.[^./]+$/.test(filename)
182
+ ? [path.basename(path.dirname(value)), filename].filter(Boolean).join("/")
183
+ : filename
184
+ return `${file.split(path.sep).join("/")}${editorSelectionLabel() ?? ""}`
185
+ })
186
+ const editorFileLabelDisplay = createMemo(() => {
187
+ const file = editorFileLabel()
188
+ if (!file) return
189
+ return Locale.truncateMiddle(file, Math.max(12, Math.min(48, Math.floor(dimensions().width / 3))))
190
+ })
191
+ const editorContextLabelState = createMemo(() => editor.labelState())
192
+ const [auto, setAuto] = createSignal<AutocompleteRef>()
193
+ const workspace = usePromptWorkspace(props.sessionID)
194
+ const move = usePromptMove({ projectID: project.project, sessionID: () => props.sessionID })
195
+ const [cursorVersion, setCursorVersion] = createSignal(0)
196
+ const currentProviderLabel = createMemo(() => local.model.parsed().provider)
197
+ const hasRightContent = createMemo(() => Boolean(props.right))
198
+
199
+ function promptModelWarning() {
200
+ toast.show({
201
+ variant: "warning",
202
+ message: "Connect a provider to send prompts",
203
+ duration: 3000,
204
+ })
205
+ if (sync.data.provider.length === 0) {
206
+ dialog.replace(() => <DialogProviderConnect />)
207
+ }
208
+ }
209
+
210
+ function dismissEditorContext() {
211
+ setDismissedEditorSelectionKey(editorSelectionKey(editorContext()))
212
+ editor.clearSelection()
213
+ }
214
+ const fileStyleId = syntax().getStyleId("extmark.file")!
215
+ const agentStyleId = syntax().getStyleId("extmark.agent")!
216
+ const pasteStyleId = syntax().getStyleId("extmark.paste")!
217
+ let promptPartTypeId = 0
218
+ const event = useEvent()
219
+
220
+ event.on(TuiEvent.PromptAppend.type, (evt, { workspace }) => {
221
+ if (workspace !== project.workspace.current()) return
222
+ if (!input || input.isDestroyed) return
223
+ input.insertText(evt.properties.text)
224
+ setTimeout(() => {
225
+ // setTimeout is a workaround and needs to be addressed properly
226
+ if (!input || input.isDestroyed) return
227
+ input.getLayoutNode().markDirty()
228
+ input.gotoBufferEnd()
229
+ renderer.requestRender()
230
+ }, 0)
231
+ })
232
+
233
+ createEffect(() => {
234
+ if (!input || input.isDestroyed) return
235
+ if (props.disabled) input.cursorColor = theme.backgroundElement
236
+ if (!props.disabled) input.cursorColor = theme.text
237
+ })
238
+
239
+ const lastUserMessage = createMemo(() => {
240
+ if (!props.sessionID) return undefined
241
+ const messages = sync.data.message[props.sessionID]
242
+ if (!messages) return undefined
243
+ return messages.findLast((m): m is UserMessage => m.role === "user")
244
+ })
245
+
246
+ const usage = createMemo(() => {
247
+ if (!props.sessionID) return
248
+ const session = sync.session.get(props.sessionID)
249
+ const msg = sync.data.message[props.sessionID] ?? []
250
+ const last = msg.findLast((item): item is AssistantMessage => item.role === "assistant" && item.tokens.output > 0)
251
+ if (!last) return
252
+
253
+ const tokens =
254
+ last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write
255
+ if (tokens <= 0) return
256
+
257
+ const model = sync.data.provider.find((item) => item.id === last.providerID)?.models[last.modelID]
258
+ const pct = model?.limit.context ? `${Math.round((tokens / model.limit.context) * 100)}%` : undefined
259
+ const cost = session?.cost ?? 0
260
+ return {
261
+ context: pct ? `${Locale.number(tokens)} (${pct})` : Locale.number(tokens),
262
+ cost: cost > 0 ? money.format(cost) : undefined,
263
+ }
264
+ })
265
+
266
+ const [store, setStore] = createStore<{
267
+ prompt: PromptInfo
268
+ mode: "normal" | "shell"
269
+ extmarkToPartIndex: Map<number, number>
270
+ interrupt: number
271
+ placeholder: number
272
+ }>({
273
+ placeholder: randomIndex(list().length),
274
+ prompt: {
275
+ input: "",
276
+ parts: [],
277
+ },
278
+ mode: "normal",
279
+ extmarkToPartIndex: new Map(),
280
+ interrupt: 0,
281
+ })
282
+
283
+ createEffect(
284
+ on(
285
+ () => props.sessionID,
286
+ () => {
287
+ setStore("placeholder", randomIndex(list().length))
288
+ },
289
+ { defer: true },
290
+ ),
291
+ )
292
+
293
+ // Initialize agent/model/variant from last user message when session changes
294
+ let syncedSessionID: string | undefined
295
+ createEffect(() => {
296
+ const sessionID = props.sessionID
297
+ const msg = lastUserMessage()
298
+
299
+ if (sessionID !== syncedSessionID) {
300
+ if (!sessionID || !msg) return
301
+
302
+ syncedSessionID = sessionID
303
+
304
+ // Only set agent if it's a primary agent (not a subagent)
305
+ const isPrimaryAgent = local.agent.list().some((x) => x.name === msg.agent)
306
+ if (msg.agent && isPrimaryAgent) {
307
+ // Keep command line --agent if specified.
308
+ if (!args.agent) local.agent.set(msg.agent)
309
+ if (msg.model) {
310
+ local.model.set(msg.model)
311
+ local.model.variant.set(msg.model.variant)
312
+ }
313
+ }
314
+ }
315
+ })
316
+
317
+ const promptCommands = createMemo(() =>
318
+ [
319
+ {
320
+ title: "Clear prompt",
321
+ name: "prompt.clear",
322
+ category: "Prompt",
323
+ hidden: true,
324
+ run: () => {
325
+ clearPrompt()
326
+ dialog.clear()
327
+ },
328
+ },
329
+ {
330
+ title: "Submit prompt",
331
+ name: "prompt.submit",
332
+ category: "Prompt",
333
+ hidden: true,
334
+ run: async () => {
335
+ if (!input.focused) return
336
+ const handled = await submit()
337
+ if (!handled) return
338
+
339
+ dialog.clear()
340
+ },
341
+ },
342
+ {
343
+ title: "Remove editor context",
344
+ name: "prompt.editor_context.clear",
345
+ category: "Prompt",
346
+ enabled: Boolean(editorContext()),
347
+ run: () => {
348
+ dismissEditorContext()
349
+ dialog.clear()
350
+ },
351
+ },
352
+ {
353
+ title: "Paste",
354
+ name: "prompt.paste",
355
+ category: "Prompt",
356
+ hidden: true,
357
+ run: async (ctx: CommandContext<Renderable, KeyEvent>) => {
358
+ ctx.event.preventDefault()
359
+ ctx.event.stopPropagation()
360
+ const content = await Clipboard.read()
361
+ if (content?.mime.startsWith("image/")) {
362
+ await pasteAttachment({
363
+ filename: "clipboard",
364
+ mime: content.mime,
365
+ content: content.data,
366
+ })
367
+ return
368
+ }
369
+ if (content?.mime === "text/plain") {
370
+ await pasteInputText(content.data)
371
+ }
372
+ },
373
+ },
374
+ {
375
+ title: "Interrupt session",
376
+ name: "session.interrupt",
377
+ category: "Session",
378
+ hidden: true,
379
+ enabled: status().type !== "idle",
380
+ run: () => {
381
+ if (auto()?.visible) return
382
+ if (!input.focused) return
383
+ // TODO: this should be its own command
384
+ if (store.mode === "shell") {
385
+ setStore("mode", "normal")
386
+ return
387
+ }
388
+ if (!props.sessionID) return
389
+
390
+ setStore("interrupt", store.interrupt + 1)
391
+
392
+ setTimeout(() => {
393
+ setStore("interrupt", 0)
394
+ }, 5000)
395
+
396
+ if (store.interrupt >= 2) {
397
+ void sdk.client.session.abort({
398
+ sessionID: props.sessionID,
399
+ })
400
+ setStore("interrupt", 0)
401
+ }
402
+ dialog.clear()
403
+ },
404
+ },
405
+ {
406
+ title: "Open editor",
407
+ category: "Session",
408
+ name: "prompt.editor",
409
+ slashName: "editor",
410
+ run: async () => {
411
+ dialog.clear()
412
+
413
+ // replace summarized text parts with the actual text
414
+ const text = store.prompt.parts
415
+ .filter((p) => p.type === "text")
416
+ .reduce((acc, p) => {
417
+ if (!p.source) return acc
418
+ return acc.replace(p.source.text.value, p.text)
419
+ }, store.prompt.input)
420
+
421
+ const nonTextParts = store.prompt.parts.filter((p) => p.type !== "text")
422
+
423
+ const value = text
424
+ const content = await Editor.open({
425
+ value,
426
+ renderer,
427
+ cwd:
428
+ (project.instance.path().worktree === "/" ? undefined : project.instance.path().worktree) ||
429
+ project.instance.directory() ||
430
+ process.cwd(),
431
+ })
432
+ if (!content) return
433
+
434
+ input.setText(content)
435
+
436
+ // Update positions for nonTextParts based on their location in new content
437
+ // Filter out parts whose virtual text was deleted
438
+ // this handles a case where the user edits the text in the editor
439
+ // such that the virtual text moves around or is deleted
440
+ const updatedNonTextParts = nonTextParts
441
+ .map((part) => {
442
+ let virtualText = ""
443
+ if (part.type === "file" && part.source?.text) {
444
+ virtualText = part.source.text.value
445
+ } else if (part.type === "agent" && part.source) {
446
+ virtualText = part.source.value
447
+ }
448
+
449
+ if (!virtualText) return part
450
+
451
+ const newStart = content.indexOf(virtualText)
452
+ // if the virtual text is deleted, remove the part
453
+ if (newStart === -1) return null
454
+
455
+ const newEnd = newStart + virtualText.length
456
+
457
+ if (part.type === "file" && part.source?.text) {
458
+ return {
459
+ ...part,
460
+ source: {
461
+ ...part.source,
462
+ text: {
463
+ ...part.source.text,
464
+ start: newStart,
465
+ end: newEnd,
466
+ },
467
+ },
468
+ }
469
+ }
470
+
471
+ if (part.type === "agent" && part.source) {
472
+ return {
473
+ ...part,
474
+ source: {
475
+ ...part.source,
476
+ start: newStart,
477
+ end: newEnd,
478
+ },
479
+ }
480
+ }
481
+
482
+ return part
483
+ })
484
+ .filter((part) => part !== null)
485
+
486
+ setStore("prompt", {
487
+ input: content,
488
+ // keep only the non-text parts because the text parts were
489
+ // already expanded inline
490
+ parts: updatedNonTextParts,
491
+ })
492
+ restoreExtmarksFromParts(updatedNonTextParts)
493
+ input.cursorOffset = Bun.stringWidth(content)
494
+ },
495
+ },
496
+ {
497
+ title: "Skills",
498
+ name: "prompt.skills",
499
+ category: "Prompt",
500
+ slashName: "skills",
501
+ run: () => {
502
+ dialog.replace(() => (
503
+ <DialogSkill
504
+ onSelect={(skill) => {
505
+ input.setText(`/${skill} `)
506
+ setStore("prompt", {
507
+ input: `/${skill} `,
508
+ parts: [],
509
+ })
510
+ input.gotoBufferEnd()
511
+ }}
512
+ />
513
+ ))
514
+ },
515
+ },
516
+ {
517
+ title: "Warp",
518
+ desc: "Change the workspace for the session",
519
+ name: "workspace.set",
520
+ category: "Session",
521
+ enabled: Flag.OPENCODE_EXPERIMENTAL_WORKSPACES,
522
+ slashName: "warp",
523
+ run: () => {
524
+ workspace.open()
525
+ },
526
+ },
527
+ {
528
+ title: "Move session",
529
+ desc: "Move the session to another project directory",
530
+ name: "session.move",
531
+ category: "Session",
532
+ slashName: "move",
533
+ run: () => {
534
+ move.open()
535
+ },
536
+ },
537
+ ].map((entry) => ({
538
+ namespace: "palette",
539
+ ...entry,
540
+ })),
541
+ )
542
+
543
+ useBindings(() => ({
544
+ commands: promptCommands(),
545
+ }))
546
+
547
+ useBindings(() => ({
548
+ mode: OPENCODE_BASE_MODE,
549
+ bindings: tuiConfig.keybinds.gather("prompt.palette", [
550
+ "prompt.submit",
551
+ "prompt.editor",
552
+ "prompt.editor_context.clear",
553
+ "prompt.stash",
554
+ "prompt.stash.pop",
555
+ "prompt.stash.list",
556
+ "session.interrupt",
557
+ "workspace.set",
558
+ "session.move",
559
+ ]),
560
+ }))
561
+
562
+ const ref: PromptRef = {
563
+ get focused() {
564
+ return input.focused
565
+ },
566
+ get current() {
567
+ return store.prompt
568
+ },
569
+ focus() {
570
+ input.focus()
571
+ },
572
+ blur() {
573
+ input.blur()
574
+ },
575
+ set(prompt) {
576
+ input.setText(prompt.input)
577
+ setStore("prompt", prompt)
578
+ restoreExtmarksFromParts(prompt.parts)
579
+ input.gotoBufferEnd()
580
+ },
581
+ reset() {
582
+ input.clear()
583
+ input.extmarks.clear()
584
+ setStore("prompt", {
585
+ input: "",
586
+ parts: [],
587
+ })
588
+ setStore("extmarkToPartIndex", new Map())
589
+ },
590
+ submit() {
591
+ void submit()
592
+ },
593
+ }
594
+
595
+ onMount(() => {
596
+ const saved = stashed
597
+ stashed = undefined
598
+ if (store.prompt.input) return
599
+ if (saved && saved.prompt.input) {
600
+ input.setText(saved.prompt.input)
601
+ setStore("prompt", saved.prompt)
602
+ restoreExtmarksFromParts(saved.prompt.parts)
603
+ input.cursorOffset = saved.cursor
604
+ }
605
+ })
606
+
607
+ onCleanup(() => {
608
+ if (store.prompt.input) {
609
+ stashed = { prompt: unwrap(store.prompt), cursor: input.cursorOffset }
610
+ }
611
+ setInputTarget(undefined)
612
+ props.ref?.(undefined)
613
+ })
614
+
615
+ createEffect(() => {
616
+ if (!input || input.isDestroyed) return
617
+ if (props.visible === false || dialog.stack.length > 0) {
618
+ if (input.focused) input.blur()
619
+ return
620
+ }
621
+
622
+ // Slot/plugin updates can remount the background prompt while a dialog is open.
623
+ // Keep focus with the dialog and let the prompt reclaim it after the dialog closes.
624
+ if (!input.focused) input.focus()
625
+ })
626
+
627
+ createEffect(() => {
628
+ if (!input || input.isDestroyed) return
629
+ input.traits = {
630
+ ...input.traits,
631
+ ...computePromptTraits({
632
+ mode: store.mode,
633
+ autocompleteVisible: !!auto()?.visible,
634
+ }),
635
+ }
636
+ })
637
+
638
+ function restoreExtmarksFromParts(parts: PromptInfo["parts"]) {
639
+ input.extmarks.clear()
640
+ setStore("extmarkToPartIndex", new Map())
641
+
642
+ parts.forEach((part, partIndex) => {
643
+ let start = 0
644
+ let end = 0
645
+ let virtualText = ""
646
+ let styleId: number | undefined
647
+
648
+ if (part.type === "file" && part.source?.text) {
649
+ start = part.source.text.start
650
+ end = part.source.text.end
651
+ virtualText = part.source.text.value
652
+ styleId = fileStyleId
653
+ } else if (part.type === "agent" && part.source) {
654
+ start = part.source.start
655
+ end = part.source.end
656
+ virtualText = part.source.value
657
+ styleId = agentStyleId
658
+ } else if (part.type === "text" && part.source?.text) {
659
+ start = part.source.text.start
660
+ end = part.source.text.end
661
+ virtualText = part.source.text.value
662
+ styleId = pasteStyleId
663
+ }
664
+
665
+ if (virtualText) {
666
+ const extmarkId = input.extmarks.create({
667
+ start,
668
+ end,
669
+ virtual: true,
670
+ styleId,
671
+ typeId: promptPartTypeId,
672
+ })
673
+ setStore("extmarkToPartIndex", (map: Map<number, number>) => {
674
+ const newMap = new Map(map)
675
+ newMap.set(extmarkId, partIndex)
676
+ return newMap
677
+ })
678
+ }
679
+ })
680
+ }
681
+
682
+ function syncExtmarksWithPromptParts() {
683
+ const allExtmarks = input.extmarks.getAllForTypeId(promptPartTypeId)
684
+ setStore(
685
+ produce((draft) => {
686
+ const newMap = new Map<number, number>()
687
+ const newParts: typeof draft.prompt.parts = []
688
+
689
+ for (const extmark of allExtmarks) {
690
+ const partIndex = draft.extmarkToPartIndex.get(extmark.id)
691
+ if (partIndex !== undefined) {
692
+ const part = draft.prompt.parts[partIndex]
693
+ if (part) {
694
+ if (part.type === "agent" && part.source) {
695
+ part.source.start = extmark.start
696
+ part.source.end = extmark.end
697
+ } else if (part.type === "file" && part.source?.text) {
698
+ part.source.text.start = extmark.start
699
+ part.source.text.end = extmark.end
700
+ } else if (part.type === "text" && part.source?.text) {
701
+ part.source.text.start = extmark.start
702
+ part.source.text.end = extmark.end
703
+ }
704
+ newMap.set(extmark.id, newParts.length)
705
+ newParts.push(part)
706
+ }
707
+ }
708
+ }
709
+
710
+ draft.extmarkToPartIndex = newMap
711
+ draft.prompt.parts = newParts
712
+ }),
713
+ )
714
+ }
715
+
716
+ const stashCommands = createMemo(() =>
717
+ [
718
+ {
719
+ title: "Stash prompt",
720
+ name: "prompt.stash",
721
+ category: "Prompt",
722
+ enabled: !!store.prompt.input,
723
+ run: () => {
724
+ if (!store.prompt.input) return
725
+ stash.push({
726
+ input: store.prompt.input,
727
+ parts: store.prompt.parts,
728
+ })
729
+ input.extmarks.clear()
730
+ input.clear()
731
+ setStore("prompt", { input: "", parts: [] })
732
+ setStore("extmarkToPartIndex", new Map())
733
+ dialog.clear()
734
+ },
735
+ },
736
+ {
737
+ title: "Stash pop",
738
+ name: "prompt.stash.pop",
739
+ category: "Prompt",
740
+ enabled: stash.list().length > 0,
741
+ run: () => {
742
+ const entry = stash.pop()
743
+ if (entry) {
744
+ input.setText(entry.input)
745
+ setStore("prompt", { input: entry.input, parts: entry.parts })
746
+ restoreExtmarksFromParts(entry.parts)
747
+ input.gotoBufferEnd()
748
+ }
749
+ dialog.clear()
750
+ },
751
+ },
752
+ {
753
+ title: "Stash list",
754
+ name: "prompt.stash.list",
755
+ category: "Prompt",
756
+ enabled: stash.list().length > 0,
757
+ run: () => {
758
+ dialog.replace(() => (
759
+ <DialogStash
760
+ onSelect={(entry) => {
761
+ input.setText(entry.input)
762
+ setStore("prompt", { input: entry.input, parts: entry.parts })
763
+ restoreExtmarksFromParts(entry.parts)
764
+ input.gotoBufferEnd()
765
+ }}
766
+ />
767
+ ))
768
+ },
769
+ },
770
+ ].map((entry) => ({
771
+ namespace: "palette",
772
+ ...entry,
773
+ })),
774
+ )
775
+
776
+ useBindings(() => ({
777
+ commands: stashCommands(),
778
+ }))
779
+
780
+ useBindings(() => {
781
+ return {
782
+ target: inputTarget,
783
+ enabled: inputTarget() !== undefined && !props.disabled,
784
+ bindings: tuiConfig.keybinds.get("prompt.paste"),
785
+ }
786
+ })
787
+
788
+ useBindings(() => {
789
+ return {
790
+ target: inputTarget,
791
+ enabled: inputTarget() !== undefined && !props.disabled && store.prompt.input !== "",
792
+ bindings: tuiConfig.keybinds.get("prompt.clear"),
793
+ }
794
+ })
795
+
796
+ useBindings(() => {
797
+ return {
798
+ target: inputTarget,
799
+ enabled: (() => {
800
+ cursorVersion()
801
+ return (
802
+ inputTarget() !== undefined &&
803
+ !props.disabled &&
804
+ store.mode === "normal" &&
805
+ !auto()?.visible &&
806
+ input?.visualCursor.offset === 0
807
+ )
808
+ })(),
809
+ bindings: [
810
+ {
811
+ key: "!",
812
+ desc: "Shell mode",
813
+ group: "Prompt",
814
+ cmd: () => {
815
+ setStore("placeholder", randomIndex(shell().length))
816
+ setStore("mode", "shell")
817
+ },
818
+ },
819
+ ],
820
+ }
821
+ })
822
+
823
+ useBindings(() => {
824
+ return {
825
+ target: inputTarget,
826
+ enabled: inputTarget() !== undefined && store.mode === "shell",
827
+ bindings: [{ key: "escape", desc: "Exit shell mode", group: "Prompt", cmd: () => setStore("mode", "normal") }],
828
+ }
829
+ })
830
+
831
+ useBindings(() => {
832
+ return {
833
+ target: inputTarget,
834
+ enabled: (() => {
835
+ cursorVersion()
836
+ return inputTarget() !== undefined && store.mode === "shell" && input?.visualCursor.offset === 0
837
+ })(),
838
+ bindings: [{ key: "backspace", desc: "Exit shell mode", group: "Prompt", cmd: () => setStore("mode", "normal") }],
839
+ }
840
+ })
841
+
842
+ useBindings(() => {
843
+ return {
844
+ target: inputTarget,
845
+ enabled: (() => {
846
+ cursorVersion()
847
+ return inputTarget() !== undefined && !props.disabled && !auto()?.visible && input !== undefined
848
+ })(),
849
+ commands: [
850
+ {
851
+ name: "prompt.history.previous",
852
+ title: "Previous prompt history",
853
+ category: "Prompt",
854
+ run() {
855
+ if (input.cursorOffset !== 0) {
856
+ if (input.scrollY + input.visualCursor.visualRow === 0) input.cursorOffset = 0
857
+ return false
858
+ }
859
+
860
+ const item = history.move(-1, input.plainText)
861
+ if (!item) return false
862
+ input.setText(item.input)
863
+ setStore("prompt", item)
864
+ setStore("mode", item.mode ?? "normal")
865
+ restoreExtmarksFromParts(item.parts)
866
+ input.cursorOffset = 0
867
+ },
868
+ },
869
+ ],
870
+ bindings: tuiConfig.keybinds.get("prompt.history.previous"),
871
+ }
872
+ })
873
+
874
+ useBindings(() => {
875
+ return {
876
+ target: inputTarget,
877
+ enabled: (() => {
878
+ cursorVersion()
879
+ return inputTarget() !== undefined && !props.disabled && !auto()?.visible && input !== undefined
880
+ })(),
881
+ commands: [
882
+ {
883
+ name: "prompt.history.next",
884
+ title: "Next prompt history",
885
+ category: "Prompt",
886
+ run() {
887
+ if (input.cursorOffset !== input.plainText.length) {
888
+ if (
889
+ input.scrollY + input.visualCursor.visualRow ===
890
+ Math.max(0, input.editorView.getTotalVirtualLineCount() - 1)
891
+ )
892
+ input.cursorOffset = input.plainText.length
893
+ return false
894
+ }
895
+
896
+ const item = history.move(1, input.plainText)
897
+ if (!item) return false
898
+ input.setText(item.input)
899
+ setStore("prompt", item)
900
+ setStore("mode", item.mode ?? "normal")
901
+ restoreExtmarksFromParts(item.parts)
902
+ input.cursorOffset = input.plainText.length
903
+ },
904
+ },
905
+ ],
906
+ bindings: tuiConfig.keybinds.get("prompt.history.next"),
907
+ }
908
+ })
909
+
910
+ let submitting = false
911
+ async function submit() {
912
+ // Prevent overlapping invocations (e.g. a double-pressed Enter, or the
913
+ // input's native onSubmit racing another dispatch). Without this guard,
914
+ // a second call slips past the empty-input check before the first call
915
+ // clears `store.prompt.input`, then awaits its own `session.create` and
916
+ // ultimately reads the now-empty store — sending a phantom empty prompt
917
+ // to a freshly created session.
918
+ if (submitting) return false
919
+ submitting = true
920
+ try {
921
+ return await submitInner()
922
+ } finally {
923
+ submitting = false
924
+ }
925
+ }
926
+
927
+ async function submitInner() {
928
+ workspace.clearNotice()
929
+
930
+ // IME: double-defer may fire before onContentChange flushes the last
931
+ // composed character (e.g. Korean hangul) to the store, so read
932
+ // plainText directly and sync before any downstream reads.
933
+ if (input && !input.isDestroyed && input.plainText !== store.prompt.input) {
934
+ setStore("prompt", "input", input.plainText)
935
+ syncExtmarksWithPromptParts()
936
+ }
937
+ if (props.disabled) return false
938
+ if (workspace.creating() || move.creating()) return false
939
+ if (auto()?.visible) return false
940
+ if (!store.prompt.input) return false
941
+ const agent = local.agent.current()
942
+ if (!agent) return false
943
+ const trimmed = store.prompt.input.trim()
944
+ if (trimmed === "exit" || trimmed === "quit" || trimmed === ":q") {
945
+ void exit()
946
+ return true
947
+ }
948
+ const selectedModel = local.model.current()
949
+ if (!selectedModel) {
950
+ void promptModelWarning()
951
+ return false
952
+ }
953
+
954
+ const workspaceSession = props.sessionID ? sync.session.get(props.sessionID) : undefined
955
+ const workspaceID = workspaceSession?.workspaceID
956
+ const workspaceStatus = workspaceID ? (project.workspace.status(workspaceID) ?? "error") : undefined
957
+ if (props.sessionID && workspaceID && workspaceStatus !== "connected") {
958
+ dialog.replace(() => (
959
+ <DialogWorkspaceUnavailable
960
+ onRestore={() => {
961
+ workspace.open()
962
+ return false
963
+ }}
964
+ />
965
+ ))
966
+ return false
967
+ }
968
+
969
+ const variant = local.model.variant.current()
970
+ let sessionID = props.sessionID
971
+ let finishMoveProgress = false
972
+ if (sessionID == null) {
973
+ const selectedWorkspace = workspace.selection()
974
+ const workspaceID = iife(() => {
975
+ if (!selectedWorkspace) return undefined
976
+ if (selectedWorkspace.type === "none") return undefined
977
+ if (selectedWorkspace.type === "existing") return selectedWorkspace.workspaceID
978
+ return undefined
979
+ })
980
+
981
+ const directory = await move.getDirectory(store.prompt.input)
982
+ if (move.pending() && !directory) return false
983
+ finishMoveProgress = Boolean(move.progress())
984
+
985
+ const res = await sdk.client.session.create({
986
+ directory,
987
+ workspace: workspaceID,
988
+ agent: agent.name,
989
+ model: {
990
+ providerID: selectedModel.providerID,
991
+ id: selectedModel.modelID,
992
+ variant,
993
+ },
994
+ })
995
+
996
+ if (res.error) {
997
+ if (finishMoveProgress) move.finishSubmit()
998
+ console.log("Creating a session failed:", res.error)
999
+
1000
+ toast.show({
1001
+ message: "Creating a session failed. Open console for more details.",
1002
+ variant: "error",
1003
+ })
1004
+
1005
+ return true
1006
+ }
1007
+
1008
+ sessionID = res.data.id
1009
+ }
1010
+
1011
+ const messageID = MessageID.ascending()
1012
+ const inputText = expandTrackedPastedText(
1013
+ store.prompt.input,
1014
+ input.extmarks.getAllForTypeId(promptPartTypeId).flatMap((extmark) => {
1015
+ const partIndex = store.extmarkToPartIndex.get(extmark.id)
1016
+ const part = partIndex === undefined ? undefined : store.prompt.parts[partIndex]
1017
+ if (part?.type !== "text") return []
1018
+ return [{ start: extmark.start, end: extmark.end, text: part.text }]
1019
+ }),
1020
+ )
1021
+
1022
+ // Filter out text parts (pasted content) since they're now expanded inline
1023
+ const nonTextParts = store.prompt.parts.filter((part) => part.type !== "text")
1024
+
1025
+ // Capture mode before it gets reset
1026
+ const currentMode = store.mode
1027
+ const editorSelection = editorContext()
1028
+ const editorParts =
1029
+ editorSelection && editor.labelState() === "pending"
1030
+ ? [
1031
+ {
1032
+ id: PartID.ascending(),
1033
+ type: "text" as const,
1034
+ text: formatEditorContext(editorSelection),
1035
+ synthetic: true,
1036
+ metadata: {
1037
+ kind: "editor_context",
1038
+ source: editorSelection.source ?? "editor",
1039
+ filePath: editorSelection.filePath,
1040
+ ranges: editorSelection.ranges,
1041
+ },
1042
+ },
1043
+ ]
1044
+ : []
1045
+
1046
+ if (store.mode === "shell") {
1047
+ move.startSubmit()
1048
+ void sdk.client.session.shell({
1049
+ sessionID,
1050
+ agent: agent.name,
1051
+ model: {
1052
+ providerID: selectedModel.providerID,
1053
+ modelID: selectedModel.modelID,
1054
+ },
1055
+ command: inputText,
1056
+ })
1057
+ setStore("mode", "normal")
1058
+ } else if (
1059
+ inputText.startsWith("/") &&
1060
+ iife(() => {
1061
+ const firstLine = inputText.split("\n")[0]
1062
+ const command = firstLine.split(" ")[0].slice(1)
1063
+ return sync.data.command.some((x) => x.name === command)
1064
+ })
1065
+ ) {
1066
+ move.startSubmit()
1067
+ // Parse command from first line, preserve multi-line content in arguments
1068
+ const firstLineEnd = inputText.indexOf("\n")
1069
+ const firstLine = firstLineEnd === -1 ? inputText : inputText.slice(0, firstLineEnd)
1070
+ const [command, ...firstLineArgs] = firstLine.split(" ")
1071
+ const restOfInput = firstLineEnd === -1 ? "" : inputText.slice(firstLineEnd + 1)
1072
+ const args = firstLineArgs.join(" ") + (restOfInput ? "\n" + restOfInput : "")
1073
+
1074
+ void sdk.client.session.command({
1075
+ sessionID,
1076
+ command: command.slice(1),
1077
+ arguments: args,
1078
+ agent: agent.name,
1079
+ model: `${selectedModel.providerID}/${selectedModel.modelID}`,
1080
+ messageID,
1081
+ variant,
1082
+ parts: nonTextParts
1083
+ .filter((x) => x.type === "file")
1084
+ .map((x) => ({
1085
+ id: PartID.ascending(),
1086
+ ...x,
1087
+ })),
1088
+ })
1089
+ } else {
1090
+ move.startSubmit()
1091
+ sdk.client.session
1092
+ .prompt({
1093
+ sessionID,
1094
+ ...selectedModel,
1095
+ messageID,
1096
+ agent: agent.name,
1097
+ model: selectedModel,
1098
+ variant,
1099
+ parts: [
1100
+ ...editorParts,
1101
+ {
1102
+ id: PartID.ascending(),
1103
+ type: "text",
1104
+ text: inputText,
1105
+ },
1106
+ ...nonTextParts.map(assign),
1107
+ ],
1108
+ })
1109
+ .catch(() => {})
1110
+ if (editorParts.length > 0) editor.markSelectionSent()
1111
+ }
1112
+ history.append({
1113
+ ...store.prompt,
1114
+ mode: currentMode,
1115
+ })
1116
+ input.extmarks.clear()
1117
+ setStore("prompt", {
1118
+ input: "",
1119
+ parts: [],
1120
+ })
1121
+ setStore("extmarkToPartIndex", new Map())
1122
+ props.onSubmit?.()
1123
+
1124
+ // temporary hack to make sure the message is sent
1125
+ if (!props.sessionID) {
1126
+ if (editorParts.length > 0) editor.preserveSelectionFromNewSession()
1127
+ setTimeout(() => {
1128
+ route.navigate({
1129
+ type: "session",
1130
+ sessionID,
1131
+ })
1132
+ }, 50)
1133
+ }
1134
+ input.clear()
1135
+ if (finishMoveProgress) move.finishSubmit()
1136
+ return true
1137
+ }
1138
+ const exit = useExit()
1139
+
1140
+ function pasteText(text: string, virtualText: string) {
1141
+ const currentOffset = input.cursorOffset
1142
+ const extmarkStart = currentOffset
1143
+ const extmarkEnd = extmarkStart + promptOffsetWidth(virtualText)
1144
+
1145
+ input.insertText(virtualText + " ")
1146
+
1147
+ const extmarkId = input.extmarks.create({
1148
+ start: extmarkStart,
1149
+ end: extmarkEnd,
1150
+ virtual: true,
1151
+ styleId: pasteStyleId,
1152
+ typeId: promptPartTypeId,
1153
+ })
1154
+
1155
+ setStore(
1156
+ produce((draft) => {
1157
+ const partIndex = draft.prompt.parts.length
1158
+ draft.prompt.parts.push({
1159
+ type: "text" as const,
1160
+ text,
1161
+ source: {
1162
+ text: {
1163
+ start: extmarkStart,
1164
+ end: extmarkEnd,
1165
+ value: virtualText,
1166
+ },
1167
+ },
1168
+ })
1169
+ draft.extmarkToPartIndex.set(extmarkId, partIndex)
1170
+ }),
1171
+ )
1172
+ }
1173
+
1174
+ async function pasteInputText(text: string) {
1175
+ const normalizedText = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n")
1176
+ const pastedContent = normalizedText.trim()
1177
+ const filepath = iife(() => {
1178
+ const raw = pastedContent.replace(/^['"]+|['"]+$/g, "")
1179
+ if (raw.startsWith("file://")) {
1180
+ try {
1181
+ return fileURLToPath(raw)
1182
+ } catch {}
1183
+ }
1184
+ if (process.platform === "win32") return raw
1185
+ return raw.replace(/\\(.)/g, "$1")
1186
+ })
1187
+ const isUrl = /^(https?):\/\//.test(filepath)
1188
+ if (!isUrl) {
1189
+ try {
1190
+ const mime = await Filesystem.mimeType(filepath)
1191
+ const filename = path.basename(filepath)
1192
+ if (mime === "image/svg+xml") {
1193
+ const content = await Filesystem.readText(filepath).catch(() => {})
1194
+ if (content) {
1195
+ pasteText(content, `[SVG: ${filename ?? "image"}]`)
1196
+ return
1197
+ }
1198
+ }
1199
+ if (mime.startsWith("image/") || mime === "application/pdf") {
1200
+ const content = await Filesystem.readArrayBuffer(filepath)
1201
+ .then((buffer) => Buffer.from(buffer).toString("base64"))
1202
+ .catch(() => {})
1203
+ if (content) {
1204
+ await pasteAttachment({
1205
+ filename,
1206
+ filepath,
1207
+ mime,
1208
+ content,
1209
+ })
1210
+ return
1211
+ }
1212
+ }
1213
+ } catch {}
1214
+ }
1215
+
1216
+ const lineCount = (pastedContent.match(/\n/g)?.length ?? 0) + 1
1217
+ if (
1218
+ (lineCount >= 3 || pastedContent.length > 150) &&
1219
+ kv.get("paste_summary_enabled", !sync.data.config.experimental?.disable_paste_summary)
1220
+ ) {
1221
+ pasteText(pastedContent, `[Pasted ~${lineCount} lines]`)
1222
+ return
1223
+ }
1224
+
1225
+ input.insertText(normalizedText)
1226
+
1227
+ setTimeout(() => {
1228
+ if (!input || input.isDestroyed) return
1229
+ input.getLayoutNode().markDirty()
1230
+ renderer.requestRender()
1231
+ }, 0)
1232
+ }
1233
+
1234
+ async function pasteAttachment(file: { filename?: string; filepath?: string; content: string; mime: string }) {
1235
+ const currentOffset = input.cursorOffset
1236
+ const extmarkStart = currentOffset
1237
+ const pdf = file.mime === "application/pdf"
1238
+ const count = store.prompt.parts.filter((x) => {
1239
+ if (x.type !== "file") return false
1240
+ if (pdf) return x.mime === "application/pdf"
1241
+ return x.mime.startsWith("image/")
1242
+ }).length
1243
+ const virtualText = pdf ? `[PDF ${count + 1}]` : `[Image ${count + 1}]`
1244
+ const extmarkEnd = extmarkStart + virtualText.length
1245
+ const textToInsert = virtualText + " "
1246
+
1247
+ input.insertText(textToInsert)
1248
+
1249
+ const extmarkId = input.extmarks.create({
1250
+ start: extmarkStart,
1251
+ end: extmarkEnd,
1252
+ virtual: true,
1253
+ styleId: pasteStyleId,
1254
+ typeId: promptPartTypeId,
1255
+ })
1256
+
1257
+ const part: Omit<FilePart, "id" | "messageID" | "sessionID"> = {
1258
+ type: "file" as const,
1259
+ mime: file.mime,
1260
+ filename: file.filename,
1261
+ url: `data:${file.mime};base64,${file.content}`,
1262
+ source: {
1263
+ type: "file",
1264
+ path: file.filepath ?? file.filename ?? "",
1265
+ text: {
1266
+ start: extmarkStart,
1267
+ end: extmarkEnd,
1268
+ value: virtualText,
1269
+ },
1270
+ },
1271
+ }
1272
+ setStore(
1273
+ produce((draft) => {
1274
+ const partIndex = draft.prompt.parts.length
1275
+ draft.prompt.parts.push(part)
1276
+ draft.extmarkToPartIndex.set(extmarkId, partIndex)
1277
+ }),
1278
+ )
1279
+ return
1280
+ }
1281
+
1282
+ function clearPrompt() {
1283
+ if (store.prompt.input.trim().length >= DRAFT_RETENTION_MIN_CHARS || store.prompt.parts.length > 0) {
1284
+ history.append({
1285
+ ...store.prompt,
1286
+ mode: store.mode,
1287
+ })
1288
+ }
1289
+ input.clear()
1290
+ input.extmarks.clear()
1291
+ setStore("prompt", {
1292
+ input: "",
1293
+ parts: [],
1294
+ })
1295
+ setStore("extmarkToPartIndex", new Map())
1296
+ }
1297
+
1298
+ const highlight = createMemo(() => {
1299
+ if (leader()) return theme.border
1300
+ if (store.mode === "shell") return theme.primary
1301
+ const agent = local.agent.current()
1302
+ if (!agent) return theme.border
1303
+ return local.agent.color(agent.name)
1304
+ })
1305
+
1306
+ const showVariant = createMemo(() => {
1307
+ const variants = local.model.variant.list()
1308
+ if (variants.length === 0) return false
1309
+ const current = local.model.variant.current()
1310
+ return !!current
1311
+ })
1312
+
1313
+ const agentMetaAlpha = createFadeIn(() => !!local.agent.current(), animationsEnabled)
1314
+ const modelMetaAlpha = createFadeIn(() => !!local.agent.current() && store.mode === "normal", animationsEnabled)
1315
+ const variantMetaAlpha = createFadeIn(
1316
+ () => !!local.agent.current() && store.mode === "normal" && showVariant(),
1317
+ animationsEnabled,
1318
+ )
1319
+ const borderHighlight = createMemo(() => tint(theme.border, highlight(), agentMetaAlpha()))
1320
+
1321
+ const placeholderText = createMemo(() => {
1322
+ if (props.showPlaceholder === false) return undefined
1323
+ if (store.mode === "shell") {
1324
+ if (!shell().length) return undefined
1325
+ const example = shell()[store.placeholder % shell().length]
1326
+ return `Run a command... "${example}"`
1327
+ }
1328
+ if (!list().length) return undefined
1329
+ return `Ask anything... "${list()[store.placeholder % list().length]}"`
1330
+ })
1331
+
1332
+ const spinnerDef = createMemo(() => {
1333
+ const agent =
1334
+ status().type !== "idle"
1335
+ ? (local.agent.list().find((a) => a.name === lastUserMessage()?.agent) ?? local.agent.current())
1336
+ : local.agent.current()
1337
+ const color = agent ? local.agent.color(agent.name) : theme.border
1338
+ return {
1339
+ frames: createFrames({
1340
+ color,
1341
+ style: "blocks",
1342
+ inactiveFactor: 0.6,
1343
+ // enableFading: false,
1344
+ minAlpha: 0.3,
1345
+ }),
1346
+ color: createColors({
1347
+ color,
1348
+ style: "blocks",
1349
+ inactiveFactor: 0.6,
1350
+ // enableFading: false,
1351
+ minAlpha: 0.3,
1352
+ }),
1353
+ }
1354
+ })
1355
+ const maxHeight = createMemo(() => tuiConfig.prompt?.max_height ?? Math.max(6, Math.floor(dimensions().height / 3)))
1356
+ const moveLabelWidth = createMemo(() => Math.max(12, Math.min(44, dimensions().width - 48)))
1357
+
1358
+ return (
1359
+ <>
1360
+ <box ref={(r: BoxRenderable) => (anchor = r)} visible={props.visible !== false} width="100%">
1361
+ <box
1362
+ width="100%"
1363
+ border={["left"]}
1364
+ borderColor={borderHighlight()}
1365
+ customBorderChars={{
1366
+ ...SplitBorder.customBorderChars,
1367
+ bottomLeft: "╹",
1368
+ }}
1369
+ >
1370
+ <box
1371
+ paddingLeft={2}
1372
+ paddingRight={2}
1373
+ paddingTop={1}
1374
+ flexShrink={0}
1375
+ backgroundColor={theme.backgroundElement}
1376
+ flexGrow={1}
1377
+ width="100%"
1378
+ >
1379
+ <textarea
1380
+ width="100%"
1381
+ placeholder={placeholderText()}
1382
+ placeholderColor={theme.textMuted}
1383
+ textColor={leader() ? theme.textMuted : theme.text}
1384
+ focusedTextColor={leader() ? theme.textMuted : theme.text}
1385
+ minHeight={1}
1386
+ maxHeight={maxHeight()}
1387
+ onContentChange={() => {
1388
+ const value = input.plainText
1389
+ setStore("prompt", "input", value)
1390
+ auto()?.onInput(value)
1391
+ syncExtmarksWithPromptParts()
1392
+ setCursorVersion((value) => value + 1)
1393
+ }}
1394
+ onCursorChange={() => setCursorVersion((value) => value + 1)}
1395
+ onKeyDown={(e: { preventDefault(): void }) => {
1396
+ if (props.disabled) {
1397
+ e.preventDefault()
1398
+ return
1399
+ }
1400
+ }}
1401
+ onSubmit={() => {
1402
+ // IME: double-defer so the last composed character (e.g. Korean
1403
+ // hangul) is flushed to plainText before we read it for submission.
1404
+ setTimeout(() => setTimeout(() => submit(), 0), 0)
1405
+ }}
1406
+ onPaste={async (event: PasteEvent) => {
1407
+ if (props.disabled) {
1408
+ event.preventDefault()
1409
+ return
1410
+ }
1411
+
1412
+ // Normalize line endings at the boundary
1413
+ // Windows ConPTY/Terminal often sends CR-only newlines in bracketed paste
1414
+ // Replace CRLF first, then any remaining CR
1415
+ const normalizedText = decodePasteBytes(event.bytes).replace(/\r\n/g, "\n").replace(/\r/g, "\n")
1416
+ const pastedContent = normalizedText.trim()
1417
+
1418
+ // Windows Terminal <1.25 can surface image-only clipboard as an
1419
+ // empty bracketed paste. Windows Terminal 1.25+ does not.
1420
+ if (!pastedContent) {
1421
+ keymap.dispatchCommand("prompt.paste")
1422
+ return
1423
+ }
1424
+
1425
+ // Once we cross an async boundary below, the terminal may perform its
1426
+ // default paste unless we suppress it first and handle insertion ourselves.
1427
+ event.preventDefault()
1428
+
1429
+ await pasteInputText(normalizedText)
1430
+ }}
1431
+ ref={(r: TextareaRenderable) => {
1432
+ input = r
1433
+ Object.assign(r, {
1434
+ getClipboardText: (text: string) => expandPastedTextPlaceholders(text, store.prompt.parts),
1435
+ })
1436
+ setInputTarget(r)
1437
+ if (promptPartTypeId === 0) {
1438
+ promptPartTypeId = input.extmarks.registerType("prompt-part")
1439
+ }
1440
+ props.ref?.(ref)
1441
+ setTimeout(() => {
1442
+ // setTimeout is a workaround and needs to be addressed properly
1443
+ if (!input || input.isDestroyed) return
1444
+ input.cursorColor = theme.text
1445
+ }, 0)
1446
+ }}
1447
+ onMouseDown={(r: MouseEvent) => r.target?.focus()}
1448
+ focusedBackgroundColor={theme.backgroundElement}
1449
+ cursorColor={props.disabled ? theme.backgroundElement : theme.text}
1450
+ syntaxStyle={syntax()}
1451
+ />
1452
+ <box flexDirection="row" flexShrink={0} paddingTop={1} gap={1} justifyContent="space-between">
1453
+ <box flexDirection="row" gap={1}>
1454
+ <Show when={local.agent.current()} fallback={<box height={1} />}>
1455
+ {(agent) => (
1456
+ <>
1457
+ <text fg={fadeColor(highlight(), agentMetaAlpha())}>
1458
+ {store.mode === "shell" ? "Shell" : Locale.titlecase(agent().name)}
1459
+ </text>
1460
+ <Show when={store.mode === "normal"}>
1461
+ <box flexDirection="row" gap={1}>
1462
+ <text fg={fadeColor(theme.textMuted, modelMetaAlpha())}>·</text>
1463
+ <Show
1464
+ when={props.sessionID}
1465
+ fallback={
1466
+ <text
1467
+ flexShrink={0}
1468
+ fg={fadeColor(leader() ? theme.textMuted : theme.text, modelMetaAlpha())}
1469
+ >
1470
+ {local.model.parsed().model || "Oryna AI"}
1471
+ </text>
1472
+ }
1473
+ >
1474
+ <text
1475
+ flexShrink={0}
1476
+ fg={fadeColor(leader() ? theme.textMuted : theme.text, modelMetaAlpha())}
1477
+ >
1478
+ {local.model.parsed().model}
1479
+ </text>
1480
+ </Show>
1481
+ <text fg={fadeColor(theme.textMuted, modelMetaAlpha())}>{currentProviderLabel()}</text>
1482
+ <Show when={showVariant()}>
1483
+ <text fg={fadeColor(theme.textMuted, variantMetaAlpha())}>·</text>
1484
+ <text>
1485
+ <span style={{ fg: fadeColor(theme.warning, variantMetaAlpha()), bold: true }}>
1486
+ {local.model.variant.current()}
1487
+ </span>
1488
+ </text>
1489
+ </Show>
1490
+ </box>
1491
+ </Show>
1492
+ </>
1493
+ )}
1494
+ </Show>
1495
+ </box>
1496
+ <Show when={hasRightContent()}>
1497
+ <box flexDirection="row" gap={1} alignItems="center">
1498
+ {props.right}
1499
+ </box>
1500
+ </Show>
1501
+ </box>
1502
+ </box>
1503
+ </box>
1504
+ <box
1505
+ height={1}
1506
+ border={["left"]}
1507
+ borderColor={borderHighlight()}
1508
+ customBorderChars={{
1509
+ ...EmptyBorder,
1510
+ vertical: theme.backgroundElement.a !== 0 ? "╹" : " ",
1511
+ }}
1512
+ >
1513
+ <box
1514
+ height={1}
1515
+ border={["bottom"]}
1516
+ borderColor={theme.backgroundElement}
1517
+ customBorderChars={
1518
+ theme.backgroundElement.a !== 0
1519
+ ? {
1520
+ ...EmptyBorder,
1521
+ horizontal: "▀",
1522
+ }
1523
+ : {
1524
+ ...EmptyBorder,
1525
+ horizontal: " ",
1526
+ }
1527
+ }
1528
+ />
1529
+ </box>
1530
+ <box width="100%" flexDirection="row" justifyContent="space-between">
1531
+ <Switch>
1532
+ <Match when={status().type !== "idle"}>
1533
+ <box
1534
+ flexDirection="row"
1535
+ gap={1}
1536
+ flexGrow={1}
1537
+ justifyContent={status().type === "retry" ? "space-between" : "flex-start"}
1538
+ >
1539
+ <box flexShrink={0} flexDirection="row" gap={1}>
1540
+ <box marginLeft={1}>
1541
+ <Show when={kv.get("animations_enabled", true)} fallback={<text fg={theme.textMuted}>[⋯]</text>}>
1542
+ <spinner color={spinnerDef().color} frames={spinnerDef().frames} interval={40} />
1543
+ </Show>
1544
+ </box>
1545
+ <box flexDirection="row" gap={1} flexShrink={0}>
1546
+ {(() => {
1547
+ const retry = createMemo(() => {
1548
+ const s = status()
1549
+ if (s.type !== "retry") return
1550
+ return s
1551
+ })
1552
+ const message = createMemo(() => {
1553
+ const r = retry()
1554
+ if (!r) return
1555
+ if (r.message.includes("exceeded your current quota") && r.message.includes("gemini"))
1556
+ return "gemini is way too hot right now"
1557
+ if (r.message.length > 80) return r.message.slice(0, 80) + "..."
1558
+ return r.message
1559
+ })
1560
+ const isTruncated = createMemo(() => {
1561
+ const r = retry()
1562
+ if (!r) return false
1563
+ return r.message.length > 120
1564
+ })
1565
+ const [seconds, setSeconds] = createSignal(0)
1566
+ onMount(() => {
1567
+ const timer = setInterval(() => {
1568
+ const next = retry()?.next
1569
+ if (next) setSeconds(Math.round((next - Date.now()) / 1000))
1570
+ }, 1000)
1571
+
1572
+ onCleanup(() => {
1573
+ clearInterval(timer)
1574
+ })
1575
+ })
1576
+ const handleMessageClick = () => {
1577
+ const r = retry()
1578
+ if (!r) return
1579
+ if (isTruncated()) {
1580
+ void DialogAlert.show(dialog, "Retry Error", r.message)
1581
+ }
1582
+ }
1583
+
1584
+ const retryText = () => {
1585
+ const r = retry()
1586
+ if (!r) return ""
1587
+ const baseMessage = message()
1588
+ const truncatedHint = isTruncated() ? " (click to expand)" : ""
1589
+ const duration = formatDuration(seconds())
1590
+ const retryInfo = ` [retrying ${duration ? `in ${duration} ` : ""}attempt #${r.attempt}]`
1591
+ return baseMessage + truncatedHint + retryInfo
1592
+ }
1593
+
1594
+ return (
1595
+ <Show when={retry()}>
1596
+ <box onMouseUp={handleMessageClick}>
1597
+ <text fg={theme.error}>{retryText()}</text>
1598
+ </box>
1599
+ </Show>
1600
+ )
1601
+ })()}
1602
+ </box>
1603
+ </box>
1604
+ <text fg={store.interrupt > 0 ? theme.primary : theme.text}>
1605
+ esc{" "}
1606
+ <span style={{ fg: store.interrupt > 0 ? theme.primary : theme.textMuted }}>
1607
+ {store.interrupt > 0 ? "again to interrupt" : "interrupt"}
1608
+ </span>
1609
+ </text>
1610
+ </box>
1611
+ </Match>
1612
+ <Match when={workspace.notice()}>
1613
+ {(notice) => (
1614
+ <box paddingLeft={3}>
1615
+ <text fg={theme.accent}>{notice()}</text>
1616
+ </box>
1617
+ )}
1618
+ </Match>
1619
+ <Match when={workspace.label()}>
1620
+ {(label) => (
1621
+ <box paddingLeft={3} flexDirection="row" gap={1}>
1622
+ <Show when={workspace.creating()}>
1623
+ <Spinner color={theme.accent} />
1624
+ </Show>
1625
+ <text fg={workspace.creating() ? theme.accent : theme.text}>
1626
+ {(() => {
1627
+ const item = label()
1628
+ if (item.type === "new") {
1629
+ if (workspace.creating())
1630
+ return `Creating ${item.workspaceType}${".".repeat(workspace.creatingDots())}`
1631
+ return (
1632
+ <>
1633
+ Workspace <span style={{ fg: theme.textMuted }}>(new {item.workspaceType})</span>
1634
+ </>
1635
+ )
1636
+ }
1637
+ return (
1638
+ <>
1639
+ Workspace <span style={{ fg: theme.textMuted }}>{item.workspaceName}</span>
1640
+ </>
1641
+ )
1642
+ })()}
1643
+ </text>
1644
+ </box>
1645
+ )}
1646
+ </Match>
1647
+ <Match when={move.progress()}>
1648
+ {(progress) => (
1649
+ <box paddingLeft={3}>
1650
+ <Spinner color={theme.accent}>
1651
+ {progress()}
1652
+ <span style={{ fg: theme.textMuted }}>{".".repeat(move.creatingDots())}</span>
1653
+ </Spinner>
1654
+ </box>
1655
+ )}
1656
+ </Match>
1657
+ <Match when={move.pendingNew()}>
1658
+ <box paddingLeft={3}>
1659
+ <text fg={theme.accent}>(new working copy)</text>
1660
+ </box>
1661
+ </Match>
1662
+ <Match when={true}>{props.hint ?? <text />}</Match>
1663
+ </Switch>
1664
+ <Show when={status().type !== "retry"}>
1665
+ <box gap={2} flexDirection="row">
1666
+ <Show when={editorContextLabelState() !== "none" ? editorFileLabelDisplay() : undefined}>
1667
+ {(file) => (
1668
+ <text fg={editorContextLabelState() === "pending" ? theme.secondary : theme.textMuted}>{file()}</text>
1669
+ )}
1670
+ </Show>
1671
+ <Switch>
1672
+ <Match when={store.mode === "normal"}>
1673
+ <Switch>
1674
+ <Match when={usage()}>
1675
+ {(item) => (
1676
+ <text fg={theme.textMuted} wrapMode="none">
1677
+ {[item().context, item().cost].filter(Boolean).join(" · ")}
1678
+ </text>
1679
+ )}
1680
+ </Match>
1681
+ <Match when={true}>
1682
+ <text fg={theme.text}>
1683
+ {agentShortcut()} <span style={{ fg: theme.textMuted }}>agents</span>
1684
+ </text>
1685
+ </Match>
1686
+ </Switch>
1687
+ <text fg={theme.text}>
1688
+ {paletteShortcut()} <span style={{ fg: theme.textMuted }}>commands</span>
1689
+ </text>
1690
+ </Match>
1691
+ <Match when={store.mode === "shell"}>
1692
+ <text fg={theme.text}>
1693
+ esc <span style={{ fg: theme.textMuted }}>exit shell mode</span>
1694
+ </text>
1695
+ </Match>
1696
+ </Switch>
1697
+ </box>
1698
+ </Show>
1699
+ </box>
1700
+ </box>
1701
+ <Autocomplete
1702
+ sessionID={props.sessionID}
1703
+ ref={(r) => {
1704
+ setAuto(() => r)
1705
+ }}
1706
+ anchor={() => anchor}
1707
+ input={() => input}
1708
+ setPrompt={(cb) => {
1709
+ setStore("prompt", produce(cb))
1710
+ }}
1711
+ setExtmark={(partIndex, extmarkId) => {
1712
+ setStore("extmarkToPartIndex", (map: Map<number, number>) => {
1713
+ const newMap = new Map(map)
1714
+ newMap.set(extmarkId, partIndex)
1715
+ return newMap
1716
+ })
1717
+ }}
1718
+ value={store.prompt.input}
1719
+ fileStyleId={fileStyleId}
1720
+ agentStyleId={agentStyleId}
1721
+ promptPartTypeId={() => promptPartTypeId}
1722
+ />
1723
+ </>
1724
+ )
1725
+ }