@crouton-kit/crouter 0.3.31 → 0.3.33

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 (443) hide show
  1. package/README.md +8 -0
  2. package/dist/build-root.js +1 -0
  3. package/dist/builtin-memory/crouter-development/marketplaces.md +2 -2
  4. package/dist/builtin-memory/crouter-development/personas/base-prompt.md +2 -2
  5. package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +5 -5
  6. package/dist/builtin-memory/crouter-development/personas.md +3 -4
  7. package/dist/builtin-memory/crouter-development/plugins.md +18 -18
  8. package/dist/builtin-memory/design.md +4 -1
  9. package/dist/builtin-memory/development.md +4 -1
  10. package/dist/builtin-memory/internal/INDEX.md +1 -1
  11. package/dist/builtin-memory/internal/nodes-and-canvas.md +6 -6
  12. package/dist/builtin-memory/internal/storage-tiers.md +5 -5
  13. package/dist/builtin-memory/planning.md +4 -1
  14. package/dist/builtin-memory/product.md +80 -0
  15. package/dist/builtin-memory/spec.md +4 -1
  16. package/dist/builtin-personas/advisor/PERSONA.md +10 -0
  17. package/dist/builtin-personas/design/PERSONA.md +2 -2
  18. package/dist/builtin-personas/design/orchestrator.md +3 -3
  19. package/dist/builtin-personas/developer/PERSONA.md +1 -1
  20. package/dist/builtin-personas/developer/orchestrator.md +2 -2
  21. package/dist/builtin-personas/explore/PERSONA.md +3 -3
  22. package/dist/builtin-personas/explore/orchestrator.md +4 -2
  23. package/dist/builtin-personas/general/PERSONA.md +1 -1
  24. package/dist/builtin-personas/general/orchestrator.md +1 -1
  25. package/dist/builtin-personas/orchestration-kernel.md +7 -14
  26. package/dist/builtin-personas/plan/PERSONA.md +1 -1
  27. package/dist/builtin-personas/plan/orchestrator.md +2 -2
  28. package/dist/builtin-personas/plan/reviewers/architecture-fit/PERSONA.md +1 -1
  29. package/dist/builtin-personas/plan/reviewers/code-smells/PERSONA.md +1 -1
  30. package/dist/builtin-personas/plan/reviewers/pattern-consistency/PERSONA.md +1 -1
  31. package/dist/builtin-personas/plan/reviewers/requirements-coverage/PERSONA.md +1 -1
  32. package/dist/builtin-personas/plan/reviewers/security/PERSONA.md +1 -1
  33. package/dist/builtin-personas/product/PERSONA.md +18 -0
  34. package/dist/builtin-personas/product/orchestrator.md +14 -0
  35. package/dist/builtin-personas/product/teardown/PERSONA.md +13 -0
  36. package/dist/builtin-personas/review/PERSONA.md +1 -1
  37. package/dist/builtin-personas/review/orchestrator.md +1 -1
  38. package/dist/builtin-personas/runtime-base.md +5 -0
  39. package/dist/builtin-personas/spec/PERSONA.md +1 -1
  40. package/dist/builtin-personas/spec/orchestrator.md +2 -2
  41. package/dist/builtin-personas/spec/requirements/PERSONA.md +1 -1
  42. package/dist/builtin-views/canvas/core.mjs +82 -1
  43. package/dist/builtin-views/canvas/tui.mjs +9 -5
  44. package/dist/builtin-views/canvas/web.jsx +3 -2
  45. package/dist/builtin-views/chat/core.mjs +725 -0
  46. package/dist/builtin-views/chat/text.mjs +101 -0
  47. package/dist/builtin-views/chat/tui.mjs +368 -0
  48. package/dist/builtin-views/chat/web.jsx +367 -0
  49. package/dist/builtin-views/prompt-review/core.mjs +863 -0
  50. package/dist/builtin-views/prompt-review/text.mjs +15 -0
  51. package/dist/builtin-views/prompt-review/tui.mjs +196 -0
  52. package/dist/builtin-views/prompt-review/web.jsx +484 -0
  53. package/dist/builtin-views/settings/core.mjs +397 -0
  54. package/dist/builtin-views/settings/text.mjs +40 -0
  55. package/dist/builtin-views/settings/tui.mjs +95 -0
  56. package/dist/builtin-views/settings/web.jsx +167 -0
  57. package/dist/cli.js +16 -3
  58. package/dist/clients/attach/__tests__/autocomplete-and-bash-mode.test.js +96 -0
  59. package/dist/clients/attach/__tests__/bash-bang-routing.test.js +58 -0
  60. package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.d.ts +1 -0
  61. package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.js +56 -0
  62. package/dist/clients/attach/__tests__/titled-editor.test.js +22 -1
  63. package/dist/clients/attach/attach-cmd.d.ts +18 -0
  64. package/dist/clients/attach/attach-cmd.js +1723 -795
  65. package/dist/clients/attach/canvas-panels.js +2 -2
  66. package/dist/clients/attach/chat-view.d.ts +10 -0
  67. package/dist/clients/attach/chat-view.js +23 -0
  68. package/dist/clients/attach/config-load.d.ts +8 -4
  69. package/dist/clients/attach/config-load.js +2 -0
  70. package/dist/clients/attach/extension-dialogs.d.ts +2 -3
  71. package/dist/clients/attach/extension-dialogs.js +7 -9
  72. package/dist/clients/attach/graph-overlay.js +3 -2
  73. package/dist/clients/attach/input-controller.d.ts +4 -3
  74. package/dist/clients/attach/input-controller.js +24 -3
  75. package/dist/clients/attach/slash-commands.d.ts +7 -7
  76. package/dist/clients/attach/slash-commands.js +38 -14
  77. package/dist/clients/attach/titled-editor.js +18 -14
  78. package/dist/clients/attach/view-socket.d.ts +2 -1
  79. package/dist/clients/attach/view-socket.js +27 -8
  80. package/dist/clients/web/__tests__/source-cache.test.d.ts +1 -0
  81. package/dist/clients/web/__tests__/source-cache.test.js +32 -0
  82. package/dist/clients/web/dev-server.js +1 -0
  83. package/dist/clients/web/events.js +9 -11
  84. package/dist/clients/web/server.d.ts +4 -3
  85. package/dist/clients/web/server.js +138 -31
  86. package/dist/clients/web/source-cache.d.ts +10 -0
  87. package/dist/clients/web/source-cache.js +57 -0
  88. package/dist/clients/web/web-cmd.js +28 -9
  89. package/dist/commands/__tests__/human.test.js +30 -28
  90. package/dist/commands/canvas-history/search.js +1 -1
  91. package/dist/commands/canvas-history.js +5 -8
  92. package/dist/commands/canvas-issue.d.ts +2 -0
  93. package/dist/commands/canvas-issue.js +148 -0
  94. package/dist/commands/canvas-prune.js +19 -0
  95. package/dist/commands/canvas-rebuild-index.d.ts +2 -0
  96. package/dist/commands/canvas-rebuild-index.js +57 -0
  97. package/dist/commands/canvas-snapshot.d.ts +2 -0
  98. package/dist/commands/canvas-snapshot.js +48 -0
  99. package/dist/commands/canvas-tmux-spread.d.ts +2 -0
  100. package/dist/commands/canvas-tmux-spread.js +188 -0
  101. package/dist/commands/canvas.d.ts +1 -0
  102. package/dist/commands/canvas.js +16 -1
  103. package/dist/commands/chord.js +143 -48
  104. package/dist/commands/daemon.js +3 -3
  105. package/dist/commands/human/prompts.d.ts +0 -1
  106. package/dist/commands/human/prompts.js +39 -54
  107. package/dist/commands/human/queue.d.ts +12 -1
  108. package/dist/commands/human/queue.js +468 -73
  109. package/dist/commands/human/shared.d.ts +3 -4
  110. package/dist/commands/human/shared.js +3 -3
  111. package/dist/commands/human.js +11 -10
  112. package/dist/commands/memory/find.js +3 -2
  113. package/dist/commands/memory/list.js +4 -3
  114. package/dist/commands/memory/read.js +3 -3
  115. package/dist/commands/memory/shared.d.ts +1 -4
  116. package/dist/commands/memory/shared.js +6 -9
  117. package/dist/commands/memory/write.js +3 -3
  118. package/dist/commands/memory.js +4 -7
  119. package/dist/commands/node-context.d.ts +2 -0
  120. package/dist/commands/node-context.js +172 -0
  121. package/dist/commands/node-snapshot.d.ts +2 -0
  122. package/dist/commands/node-snapshot.js +123 -0
  123. package/dist/commands/node.js +228 -52
  124. package/dist/commands/pkg/plugin-inspect.js +6 -6
  125. package/dist/commands/pkg/plugin-manage.js +1 -1
  126. package/dist/commands/pkg/plugin.js +2 -2
  127. package/dist/commands/pkg.js +2 -2
  128. package/dist/commands/push.d.ts +2 -0
  129. package/dist/commands/push.js +79 -11
  130. package/dist/commands/revive.js +74 -1
  131. package/dist/commands/search/answer.d.ts +1 -0
  132. package/dist/commands/search/answer.js +50 -0
  133. package/dist/commands/search/contents.d.ts +1 -0
  134. package/dist/commands/search/contents.js +96 -0
  135. package/dist/commands/search/exa.d.ts +53 -0
  136. package/dist/commands/search/exa.js +155 -0
  137. package/dist/commands/search/puremd.d.ts +11 -0
  138. package/dist/commands/search/puremd.js +53 -0
  139. package/dist/commands/search/web.d.ts +1 -0
  140. package/dist/commands/search/web.js +65 -0
  141. package/dist/commands/search.d.ts +2 -0
  142. package/dist/commands/search.js +24 -0
  143. package/dist/commands/sys/__tests__/sync-import.test.d.ts +1 -0
  144. package/dist/commands/sys/__tests__/sync-import.test.js +72 -0
  145. package/dist/commands/sys/config.js +59 -10
  146. package/dist/commands/sys/doctor.js +56 -4
  147. package/dist/commands/sys/prompt-review.d.ts +1 -0
  148. package/dist/commands/sys/prompt-review.js +178 -0
  149. package/dist/commands/sys/promptstudio.d.ts +2 -0
  150. package/dist/commands/sys/promptstudio.js +65 -0
  151. package/dist/commands/sys/settings.d.ts +2 -0
  152. package/dist/commands/sys/settings.js +16 -0
  153. package/dist/commands/sys/sync.js +311 -159
  154. package/dist/commands/sys/sysprompt.d.ts +1 -0
  155. package/dist/commands/sys/sysprompt.js +86 -0
  156. package/dist/commands/sys.js +9 -5
  157. package/dist/commands/view-new.js +2 -2
  158. package/dist/core/__tests__/artifact-paths.test.d.ts +1 -0
  159. package/dist/core/__tests__/artifact-paths.test.js +44 -0
  160. package/dist/core/__tests__/broker-preflight.test.d.ts +1 -0
  161. package/dist/core/__tests__/broker-preflight.test.js +85 -0
  162. package/dist/core/__tests__/broker-sdk-wiring.test.js +15 -0
  163. package/dist/core/__tests__/canvas.test.js +37 -1
  164. package/dist/core/__tests__/chat-view-reconnect.test.d.ts +1 -0
  165. package/dist/core/__tests__/chat-view-reconnect.test.js +70 -0
  166. package/dist/core/__tests__/child-death-wake.test.js +11 -2
  167. package/dist/core/__tests__/close.test.js +39 -1
  168. package/dist/core/__tests__/connection-reconnect-resume.test.d.ts +1 -0
  169. package/dist/core/__tests__/connection-reconnect-resume.test.js +37 -0
  170. package/dist/core/__tests__/context-intro.test.js +48 -14
  171. package/dist/core/__tests__/daemon-boot.test.js +182 -9
  172. package/dist/core/__tests__/editor-label.test.d.ts +1 -0
  173. package/dist/core/__tests__/editor-label.test.js +26 -0
  174. package/dist/core/__tests__/fault-marker.test.d.ts +1 -0
  175. package/dist/core/__tests__/fault-marker.test.js +112 -0
  176. package/dist/core/__tests__/fault-retry-rewind.test.d.ts +1 -0
  177. package/dist/core/__tests__/fault-retry-rewind.test.js +123 -0
  178. package/dist/core/__tests__/fixtures/fake-engine.d.ts +30 -4
  179. package/dist/core/__tests__/fixtures/fake-engine.js +156 -17
  180. package/dist/core/__tests__/fixtures/fake-pi-host.js +10 -1
  181. package/dist/core/__tests__/full/human-new-window-regression.test.js +1 -1
  182. package/dist/core/__tests__/helpers/harness.d.ts +1 -1
  183. package/dist/core/__tests__/helpers/harness.js +9 -3
  184. package/dist/core/__tests__/human-stranded-deliver.test.js +38 -1
  185. package/dist/core/__tests__/human-surface-target.test.js +1 -1
  186. package/dist/core/__tests__/kickoff.test.js +18 -19
  187. package/dist/core/__tests__/memory-resolver.test.js +12 -1
  188. package/dist/core/__tests__/migration.test.js +49 -14
  189. package/dist/core/__tests__/model-ladders.test.d.ts +1 -0
  190. package/dist/core/__tests__/model-ladders.test.js +160 -0
  191. package/dist/core/__tests__/node-env.test.d.ts +1 -0
  192. package/dist/core/__tests__/node-env.test.js +26 -0
  193. package/dist/core/__tests__/push-final-guard.test.js +7 -1
  194. package/dist/core/__tests__/relaunch-root.test.js +9 -9
  195. package/dist/core/__tests__/revive.test.js +36 -6
  196. package/dist/core/__tests__/spawn-root.test.js +24 -1
  197. package/dist/core/__tests__/stop-guard.test.js +10 -0
  198. package/dist/core/__tests__/stranded-relaunch.test.d.ts +1 -0
  199. package/dist/core/__tests__/stranded-relaunch.test.js +72 -0
  200. package/dist/core/__tests__/tmux-surface.test.js +5 -1
  201. package/dist/core/__tests__/unknown-path.test.js +19 -19
  202. package/dist/core/__tests__/yield-ensures-daemon.test.d.ts +1 -0
  203. package/dist/core/__tests__/yield-ensures-daemon.test.js +54 -0
  204. package/dist/core/artifact.d.ts +2 -33
  205. package/dist/core/artifact.js +27 -87
  206. package/dist/core/bootstrap.js +2 -0
  207. package/dist/core/canvas/browse/__tests__/model.test.js +35 -1
  208. package/dist/core/canvas/browse/app.js +87 -17
  209. package/dist/core/canvas/browse/model.d.ts +10 -1
  210. package/dist/core/canvas/browse/model.js +37 -1
  211. package/dist/core/canvas/browse/render.js +35 -33
  212. package/dist/core/canvas/canvas.d.ts +7 -0
  213. package/dist/core/canvas/canvas.js +54 -5
  214. package/dist/core/canvas/db.js +35 -8
  215. package/dist/core/canvas/labels.d.ts +6 -8
  216. package/dist/core/canvas/labels.js +8 -11
  217. package/dist/core/canvas/nav-model.d.ts +5 -4
  218. package/dist/core/canvas/nav-model.js +31 -17
  219. package/dist/core/canvas/paths.d.ts +18 -1
  220. package/dist/core/canvas/paths.js +37 -2
  221. package/dist/core/canvas/render.d.ts +25 -0
  222. package/dist/core/canvas/render.js +96 -13
  223. package/dist/core/canvas/status-glyph.d.ts +35 -0
  224. package/dist/core/canvas/status-glyph.js +104 -0
  225. package/dist/core/canvas/types.d.ts +2 -2
  226. package/dist/core/config.js +114 -16
  227. package/dist/core/fault-classifier.d.ts +41 -0
  228. package/dist/core/fault-classifier.js +110 -0
  229. package/dist/core/feed/__tests__/empty-node-guard.test.d.ts +1 -0
  230. package/dist/core/feed/__tests__/empty-node-guard.test.js +45 -0
  231. package/dist/core/feed/feed.js +8 -0
  232. package/dist/core/feed/inbox.d.ts +15 -0
  233. package/dist/core/feed/inbox.js +40 -14
  234. package/dist/core/frontmatter.d.ts +1 -11
  235. package/dist/core/frontmatter.js +1 -55
  236. package/dist/core/hearth/config.d.ts +2 -0
  237. package/dist/core/hearth/config.js +75 -0
  238. package/dist/core/hearth/guest-env.d.ts +2 -0
  239. package/dist/core/hearth/guest-env.js +11 -0
  240. package/dist/core/hearth/index.d.ts +5 -0
  241. package/dist/core/hearth/index.js +5 -0
  242. package/dist/core/hearth/provider.d.ts +21 -0
  243. package/dist/core/hearth/provider.js +10 -0
  244. package/dist/core/hearth/providers/blaxel-bootstrap.d.ts +10 -0
  245. package/dist/core/hearth/providers/blaxel-bootstrap.js +39 -0
  246. package/dist/core/hearth/providers/blaxel-home.d.ts +11 -0
  247. package/dist/core/hearth/providers/blaxel-home.js +126 -0
  248. package/dist/core/hearth/providers/blaxel.d.ts +26 -0
  249. package/dist/core/hearth/providers/blaxel.js +208 -0
  250. package/dist/core/hearth/providers/types.d.ts +72 -0
  251. package/dist/core/hearth/providers/types.js +1 -0
  252. package/dist/core/hearth/registry.d.ts +15 -0
  253. package/dist/core/hearth/registry.js +179 -0
  254. package/dist/core/hearth/types.d.ts +121 -0
  255. package/dist/core/hearth/types.js +1 -0
  256. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.d.ts +1 -0
  257. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.js +79 -0
  258. package/dist/core/{skill-sync → host-exports}/builtins.d.ts +21 -13
  259. package/dist/core/host-exports/builtins.js +68 -0
  260. package/dist/core/{skill-sync → host-exports}/export.d.ts +9 -6
  261. package/dist/core/{skill-sync → host-exports}/export.js +37 -14
  262. package/dist/core/io.js +1 -1
  263. package/dist/core/log.d.ts +9 -0
  264. package/dist/core/log.js +113 -0
  265. package/dist/core/memory-resolver.d.ts +8 -8
  266. package/dist/core/memory-resolver.js +15 -28
  267. package/dist/core/personas/index.d.ts +4 -4
  268. package/dist/core/personas/index.js +2 -2
  269. package/dist/core/personas/loader.d.ts +51 -0
  270. package/dist/core/personas/loader.js +54 -50
  271. package/dist/core/personas/resolve.d.ts +43 -27
  272. package/dist/core/personas/resolve.js +336 -94
  273. package/dist/core/runtime/auth-reload.d.ts +7 -0
  274. package/dist/core/runtime/auth-reload.js +69 -0
  275. package/dist/core/runtime/bearings.d.ts +35 -15
  276. package/dist/core/runtime/bearings.js +305 -44
  277. package/dist/core/runtime/branded-host.d.ts +4 -2
  278. package/dist/core/runtime/branded-host.js +66 -4
  279. package/dist/core/runtime/broker-protocol.d.ts +38 -3
  280. package/dist/core/runtime/broker.js +343 -18
  281. package/dist/core/runtime/close.js +37 -29
  282. package/dist/core/runtime/connectivity.d.ts +12 -0
  283. package/dist/core/runtime/connectivity.js +73 -0
  284. package/dist/core/runtime/fault-recovery-nudge.d.ts +2 -0
  285. package/dist/core/runtime/fault-recovery-nudge.js +2 -0
  286. package/dist/core/runtime/fault-recovery.d.ts +23 -0
  287. package/dist/core/runtime/fault-recovery.js +74 -0
  288. package/dist/core/runtime/fault.d.ts +25 -0
  289. package/dist/core/runtime/fault.js +176 -0
  290. package/dist/core/runtime/front-door.d.ts +1 -1
  291. package/dist/core/runtime/front-door.js +2 -2
  292. package/dist/core/runtime/host.d.ts +10 -0
  293. package/dist/core/runtime/host.js +71 -9
  294. package/dist/core/runtime/kickoff.js +31 -45
  295. package/dist/core/runtime/launch.d.ts +32 -18
  296. package/dist/core/runtime/launch.js +148 -43
  297. package/dist/core/runtime/model-swap.d.ts +18 -0
  298. package/dist/core/runtime/model-swap.js +95 -0
  299. package/dist/core/runtime/naming.d.ts +7 -0
  300. package/dist/core/runtime/naming.js +61 -9
  301. package/dist/core/runtime/nodes.js +6 -1
  302. package/dist/core/runtime/persona.js +16 -17
  303. package/dist/core/runtime/placement.d.ts +36 -31
  304. package/dist/core/runtime/placement.js +149 -64
  305. package/dist/core/runtime/promote.d.ts +2 -0
  306. package/dist/core/runtime/promote.js +23 -3
  307. package/dist/core/runtime/recycle.js +6 -4
  308. package/dist/core/runtime/reset.d.ts +5 -2
  309. package/dist/core/runtime/reset.js +2 -2
  310. package/dist/core/runtime/revive.d.ts +4 -0
  311. package/dist/core/runtime/revive.js +27 -9
  312. package/dist/core/runtime/roadmap.d.ts +1 -1
  313. package/dist/core/runtime/roadmap.js +1 -1
  314. package/dist/core/runtime/spawn.d.ts +2 -2
  315. package/dist/core/runtime/spawn.js +30 -6
  316. package/dist/core/runtime/stop-guard.js +6 -1
  317. package/dist/core/runtime/tmux-chrome.d.ts +1 -1
  318. package/dist/core/runtime/tmux-chrome.js +1 -1
  319. package/dist/core/runtime/tmux.d.ts +29 -6
  320. package/dist/core/runtime/tmux.js +134 -80
  321. package/dist/core/runtime/view-socket-client.d.ts +46 -0
  322. package/dist/core/runtime/view-socket-client.js +203 -0
  323. package/dist/core/substrate/index.d.ts +1 -1
  324. package/dist/core/substrate/index.js +1 -1
  325. package/dist/core/substrate/on-read.js +16 -4
  326. package/dist/core/substrate/render.d.ts +10 -13
  327. package/dist/core/substrate/render.js +56 -51
  328. package/dist/core/tui/host.js +83 -18
  329. package/dist/core/view/__tests__/transport-cache.test.d.ts +1 -0
  330. package/dist/core/view/__tests__/transport-cache.test.js +62 -0
  331. package/dist/core/view/contract.d.ts +20 -0
  332. package/dist/core/view/stream-local.d.ts +3 -0
  333. package/dist/core/view/stream-local.js +184 -0
  334. package/dist/core/view/transport-cache.d.ts +8 -0
  335. package/dist/core/view/transport-cache.js +38 -0
  336. package/dist/core/view/transport-local.js +4 -0
  337. package/dist/core/view/transport.d.ts +7 -1
  338. package/dist/daemon/crtrd.d.ts +3 -19
  339. package/dist/daemon/crtrd.js +139 -178
  340. package/dist/daemon/manage.d.ts +18 -1
  341. package/dist/daemon/manage.js +54 -9
  342. package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.d.ts +1 -0
  343. package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.js +289 -0
  344. package/dist/hearth/wake-proxy/__tests__/config-timeout.test.d.ts +1 -0
  345. package/dist/hearth/wake-proxy/__tests__/config-timeout.test.js +34 -0
  346. package/dist/hearth/wake-proxy/__tests__/guest-source.test.d.ts +1 -0
  347. package/dist/hearth/wake-proxy/__tests__/guest-source.test.js +203 -0
  348. package/dist/hearth/wake-proxy/__tests__/hardening.test.d.ts +1 -0
  349. package/dist/hearth/wake-proxy/__tests__/hardening.test.js +59 -0
  350. package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.d.ts +1 -0
  351. package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.js +372 -0
  352. package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.d.ts +1 -0
  353. package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.js +258 -0
  354. package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.d.ts +1 -0
  355. package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.js +437 -0
  356. package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.d.ts +1 -0
  357. package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.js +455 -0
  358. package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.d.ts +1 -0
  359. package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.js +15 -0
  360. package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.d.ts +1 -0
  361. package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.js +141 -0
  362. package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.d.ts +1 -0
  363. package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.js +143 -0
  364. package/dist/hearth/wake-proxy/anthropic-oauth.d.ts +58 -0
  365. package/dist/hearth/wake-proxy/anthropic-oauth.js +189 -0
  366. package/dist/hearth/wake-proxy/auth.d.ts +22 -0
  367. package/dist/hearth/wake-proxy/auth.js +128 -0
  368. package/dist/hearth/wake-proxy/config.d.ts +2 -0
  369. package/dist/hearth/wake-proxy/config.js +151 -0
  370. package/dist/hearth/wake-proxy/guest-source.d.ts +14 -0
  371. package/dist/hearth/wake-proxy/guest-source.js +130 -0
  372. package/dist/hearth/wake-proxy/hearth-status.d.ts +44 -0
  373. package/dist/hearth/wake-proxy/hearth-status.js +267 -0
  374. package/dist/hearth/wake-proxy/home.d.ts +67 -0
  375. package/dist/hearth/wake-proxy/home.js +297 -0
  376. package/dist/hearth/wake-proxy/main.d.ts +1 -0
  377. package/dist/hearth/wake-proxy/main.js +134 -0
  378. package/dist/hearth/wake-proxy/model-auth.d.ts +13 -0
  379. package/dist/hearth/wake-proxy/model-auth.js +345 -0
  380. package/dist/hearth/wake-proxy/proxy.d.ts +35 -0
  381. package/dist/hearth/wake-proxy/proxy.js +716 -0
  382. package/dist/hearth/wake-proxy/public-source-gate.d.ts +9 -0
  383. package/dist/hearth/wake-proxy/public-source-gate.js +409 -0
  384. package/dist/hearth/wake-proxy/redact.d.ts +1 -0
  385. package/dist/hearth/wake-proxy/redact.js +17 -0
  386. package/dist/hearth/wake-proxy/server.d.ts +11 -0
  387. package/dist/hearth/wake-proxy/server.js +142 -0
  388. package/dist/hearth/wake-proxy/state.d.ts +18 -0
  389. package/dist/hearth/wake-proxy/state.js +342 -0
  390. package/dist/hearth/wake-proxy/types.d.ts +76 -0
  391. package/dist/hearth/wake-proxy/types.js +1 -0
  392. package/dist/index.js +3 -2
  393. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +6 -5
  394. package/dist/pi-extensions/canvas-context-intro.js +7 -7
  395. package/dist/pi-extensions/canvas-doc-substrate.js +19 -22
  396. package/dist/pi-extensions/canvas-nav.js +15 -8
  397. package/dist/pi-extensions/canvas-stophook.d.ts +1 -1
  398. package/dist/pi-extensions/canvas-stophook.js +78 -11
  399. package/dist/types.d.ts +39 -20
  400. package/dist/types.js +38 -13
  401. package/dist/web/runtime.js +141 -42
  402. package/dist/web/transport-http.js +7 -5
  403. package/dist/web/transport-stream.d.ts +3 -0
  404. package/dist/web/transport-stream.js +204 -0
  405. package/dist/web-client/assets/fragment-mono-latin-400-normal-BYwT3kSJ.woff +0 -0
  406. package/dist/web-client/assets/fragment-mono-latin-400-normal-yxdJ5AmL.woff2 +0 -0
  407. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-BbKYyvR9.woff2 +0 -0
  408. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-CT4YFKeK.woff +0 -0
  409. package/dist/web-client/assets/fraunces-latin-ext-wght-normal-Ca2vKHc0.woff2 +0 -0
  410. package/dist/web-client/assets/fraunces-latin-wght-normal-ukD16Tqj.woff2 +0 -0
  411. package/dist/web-client/assets/fraunces-vietnamese-wght-normal-CnvboYUG.woff2 +0 -0
  412. package/dist/web-client/assets/index-BZUxTkv5.css +2 -0
  413. package/dist/web-client/assets/index-D36PNBj4.js +80 -0
  414. package/dist/web-client/assets/instrument-sans-latin-ext-wght-normal-B5bTHO_g.woff2 +0 -0
  415. package/dist/web-client/assets/instrument-sans-latin-wght-normal-BbzFLZTg.woff2 +0 -0
  416. package/dist/web-client/assets/martian-mono-cyrillic-wght-normal-B84CD5C_.woff2 +0 -0
  417. package/dist/web-client/assets/martian-mono-latin-ext-wght-normal-DlL6xMw5.woff2 +0 -0
  418. package/dist/web-client/assets/martian-mono-latin-wght-normal-5W32yIyr.woff2 +0 -0
  419. package/dist/web-client/index.html +3 -2
  420. package/package.json +32 -10
  421. package/dist/core/__tests__/error-stall-recycle.test.js +0 -141
  422. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +0 -57
  423. package/dist/core/skill-sync/builtins.js +0 -112
  424. package/dist/core/skill-sync/claude-plugins.d.ts +0 -23
  425. package/dist/core/skill-sync/claude-plugins.js +0 -71
  426. package/dist/core/skill-sync/engine.d.ts +0 -42
  427. package/dist/core/skill-sync/engine.js +0 -633
  428. package/dist/core/skill-sync/manifest.d.ts +0 -64
  429. package/dist/core/skill-sync/manifest.js +0 -181
  430. package/dist/core/skill-sync/profile.d.ts +0 -76
  431. package/dist/core/skill-sync/profile.js +0 -173
  432. package/dist/core/skill-sync/snapshot.d.ts +0 -57
  433. package/dist/core/skill-sync/snapshot.js +0 -120
  434. package/dist/pi-extensions/canvas-commands.d.ts +0 -37
  435. package/dist/pi-extensions/canvas-commands.js +0 -113
  436. package/dist/pi-extensions/canvas-resume.d.ts +0 -21
  437. package/dist/pi-extensions/canvas-resume.js +0 -83
  438. package/dist/pi-extensions/canvas-view.d.ts +0 -21
  439. package/dist/pi-extensions/canvas-view.js +0 -76
  440. package/dist/web-client/assets/index-BUvQb4hR.css +0 -2
  441. package/dist/web-client/assets/index-ClLQXYAE.js +0 -10
  442. /package/dist/{core/__tests__/error-stall-recycle.test.d.ts → clients/attach/__tests__/autocomplete-and-bash-mode.test.d.ts} +0 -0
  443. /package/dist/{core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts → clients/attach/__tests__/bash-bang-routing.test.d.ts} +0 -0
@@ -0,0 +1,367 @@
1
+ // @ts-check
2
+ /**
3
+ * Chat — the WEB presenter for the `chat` builtin view (React + Tailwind).
4
+ * Browser-only: consumed solely by the web serve path; NEVER Node-imported.
5
+ * The default export is a pure function of `state`; DOM events call
6
+ * `dispatch(intentName, payload?)`.
7
+ *
8
+ * @module chat/web
9
+ */
10
+
11
+ import { Loading, Empty, ErrorState, NotReady } from '@crouton-kit/crouter/web';
12
+ import { messageText } from './core.mjs';
13
+
14
+ /** @typedef {import('./core.mjs').ChatState} ChatState */
15
+
16
+ const NOTICE_STYLES = {
17
+ info: 'border-slate-200 bg-slate-50 text-slate-700',
18
+ action: 'border-amber-200 bg-amber-50 text-amber-800',
19
+ error: 'border-rose-200 bg-rose-50 text-rose-800',
20
+ };
21
+
22
+ const LEVEL_BADGES = {
23
+ info: 'bg-slate-100 text-slate-700',
24
+ action: 'bg-amber-100 text-amber-800',
25
+ error: 'bg-rose-100 text-rose-800',
26
+ };
27
+
28
+ /** @param {unknown} value @returns {value is Record<string, unknown>} */
29
+ function isRecord(value) {
30
+ return typeof value === 'object' && value !== null;
31
+ }
32
+
33
+ /** @param {string} text @param {number} max */
34
+ function truncate(text, max = 120) {
35
+ const value = String(text || '');
36
+ return value.length > max ? `${value.slice(0, Math.max(0, max - 1))}…` : value;
37
+ }
38
+
39
+ /** @param {unknown} message */
40
+ function messageRole(message) {
41
+ return isRecord(message) && typeof message.role === 'string' ? message.role : '';
42
+ }
43
+
44
+ /** @param {unknown} message */
45
+ function messageKind(message) {
46
+ if (!isRecord(message)) return '';
47
+ if (typeof message.customType === 'string' && message.customType) return `custom · ${message.customType}`;
48
+ if (typeof message.type === 'string' && message.type) return message.type;
49
+ return '';
50
+ }
51
+
52
+ /** @param {unknown} message */
53
+ function messageTs(message) {
54
+ if (!isRecord(message)) return '';
55
+ if (typeof message.timestamp === 'string') return message.timestamp;
56
+ if (typeof message.timestamp === 'number') return new Date(message.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
57
+ return '';
58
+ }
59
+
60
+ /** @param {unknown} value */
61
+ function blockText(value) {
62
+ if (!isRecord(value)) return '';
63
+ if (typeof value.text === 'string') return value.text;
64
+ if (Array.isArray(value.text)) return value.text.map((part) => (isRecord(part) && typeof part.text === 'string' ? part.text : '')).join('');
65
+ if (typeof value.content === 'string') return value.content;
66
+ return '';
67
+ }
68
+
69
+ /** @param {unknown} value */
70
+ function summarizeBlock(value) {
71
+ if (!isRecord(value)) return '';
72
+ const type = typeof value.type === 'string' ? value.type : '';
73
+ const name = typeof value.name === 'string' ? value.name : '';
74
+ switch (type) {
75
+ case 'text':
76
+ return '';
77
+ case 'thinking':
78
+ case 'reasoning': {
79
+ const text = truncate(blockText(value).replace(/\s+/g, ' ').trim(), 88);
80
+ return text ? `Thinking · ${text}` : 'Thinking';
81
+ }
82
+ case 'toolCall': {
83
+ const args = blockText(value);
84
+ const tail = args ? ` · ${truncate(args.replace(/\s+/g, ' ').trim(), 88)}` : '';
85
+ return `Tool · ${name || 'tool'}${tail}`;
86
+ }
87
+ case 'toolResult': {
88
+ const result = blockText(value);
89
+ const tail = result ? ` · ${truncate(result.replace(/\s+/g, ' ').trim(), 88)}` : '';
90
+ return `Tool result${tail}`;
91
+ }
92
+ default:
93
+ return type ? `${type}${name ? ` · ${name}` : ''}` : '';
94
+ }
95
+ }
96
+
97
+ /** @param {unknown} message */
98
+ function messageSummaries(message) {
99
+ const summaries = [];
100
+ if (!isRecord(message)) return summaries;
101
+ if (messageRole(message) === 'bashExecution') {
102
+ const command = typeof message.command === 'string' ? message.command : '';
103
+ if (command) summaries.push(`$ ${truncate(command.replace(/\s+/g, ' ').trim(), 96)}`);
104
+ if (typeof message.exitCode === 'number') summaries.push(`exit ${message.exitCode}`);
105
+ if (message.cancelled === true) summaries.push('cancelled');
106
+ if (message.truncated === true) summaries.push('truncated');
107
+ }
108
+
109
+ const content = Array.isArray(message.content) ? message.content : [];
110
+ for (const block of content) {
111
+ const summary = summarizeBlock(block);
112
+ if (summary) summaries.push(summary);
113
+ }
114
+
115
+ const kind = messageKind(message);
116
+ if (kind && messageRole(message) === 'custom') summaries.push(kind);
117
+ if (messageRole(message) === 'compactionSummary') summaries.push('Compaction summary');
118
+ if (messageRole(message) === 'branchSummary') summaries.push('Branch summary');
119
+ return summaries;
120
+ }
121
+
122
+ /** @param {ChatState} state */
123
+ function connectionGuidance(state) {
124
+ const target = state.target || '<node-id>';
125
+ switch (state.conn) {
126
+ case 'no-broker':
127
+ return {
128
+ level: 'action',
129
+ Comp: NotReady,
130
+ headline: 'node is asleep',
131
+ explanation: 'Focus or revive the node first, then reconnect.',
132
+ nextStep: `crtr node focus ${target} · or crtr canvas revive ${target}`,
133
+ };
134
+ case 'no-node':
135
+ return {
136
+ level: 'error',
137
+ Comp: ErrorState,
138
+ headline: 'node not found',
139
+ explanation: 'Check the target node id and try again.',
140
+ nextStep: `crtr view run chat --target ${target}`,
141
+ };
142
+ case 'invalid':
143
+ return {
144
+ level: 'error',
145
+ Comp: ErrorState,
146
+ headline: 'invalid target',
147
+ explanation: 'The target was rejected by the broker.',
148
+ nextStep: `crtr view run chat --target <node-id>`,
149
+ };
150
+ case 'error':
151
+ if (!state.target) {
152
+ return {
153
+ level: 'error',
154
+ Comp: ErrorState,
155
+ headline: 'missing target',
156
+ explanation: 'Open chat with a node id so the view can connect.',
157
+ nextStep: 'crtr view run chat --target <node-id>',
158
+ };
159
+ }
160
+ return {
161
+ level: 'error',
162
+ Comp: ErrorState,
163
+ headline: 'chat connection failed',
164
+ explanation: 'Reconnect to retry the stream.',
165
+ nextStep: 'Press Reconnect',
166
+ };
167
+ default:
168
+ return null;
169
+ }
170
+ }
171
+
172
+ /** @param {{ label: string, value: string, cls?: string }} props */
173
+ function Pill({ label, value, cls = '' }) {
174
+ return (
175
+ <span className={`inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[11px] ${cls}`}>
176
+ <span className="opacity-60">{label}</span>
177
+ <span className="font-semibold">{value}</span>
178
+ </span>
179
+ );
180
+ }
181
+
182
+ /** @param {{ notice: ChatState['notices'][number], onDismiss: (id: string) => void }} props */
183
+ function NoticeRow({ notice, onDismiss }) {
184
+ return (
185
+ <div className={`flex items-start gap-3 rounded-xl border px-3 py-2 text-sm ${NOTICE_STYLES[notice.level] || NOTICE_STYLES.info}`}>
186
+ <span className={`rounded-full px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide ${LEVEL_BADGES[notice.level] || LEVEL_BADGES.info}`}>{notice.level}</span>
187
+ <div className="min-w-0 flex-1 whitespace-pre-wrap">{notice.text}</div>
188
+ <button type="button" className="shrink-0 text-slate-400 hover:text-slate-700" onClick={() => onDismiss(notice.id)} aria-label="Dismiss notice">
189
+ ×
190
+ </button>
191
+ </div>
192
+ );
193
+ }
194
+
195
+ /** @param {{ message: unknown, index: number, streaming: boolean }} props */
196
+ function TranscriptRow({ message, index, streaming }) {
197
+ const role = messageRole(message);
198
+ const user = role === 'user';
199
+ const assistant = role === 'assistant';
200
+ const bubbleCls = user ? 'border-emerald-200 bg-emerald-50' : assistant ? 'border-cyan-200 bg-cyan-50' : 'border-slate-200 bg-white';
201
+ const labelCls = user ? 'text-emerald-700' : assistant ? 'text-cyan-700' : 'text-slate-700';
202
+ const text = messageText(message).trim();
203
+ const summaries = messageSummaries(message);
204
+ const ts = messageTs(message);
205
+
206
+ return (
207
+ <article className={`rounded-2xl border px-3 py-2 shadow-sm ${bubbleCls}`}>
208
+ <div className="flex items-baseline gap-2">
209
+ <div className={`font-semibold ${labelCls}`}>{user ? 'You' : assistant ? 'Agent' : role || 'Message'}</div>
210
+ {streaming ? <span className="rounded-full bg-cyan-100 px-2 py-0.5 text-[11px] font-semibold text-cyan-800">streaming</span> : null}
211
+ {role === 'bashExecution' ? <span className="rounded-full bg-slate-100 px-2 py-0.5 text-[11px] font-semibold text-slate-700">bash</span> : null}
212
+ {ts ? <div className="ml-auto text-xs text-slate-400">{ts}</div> : null}
213
+ </div>
214
+ {text ? <div className="mt-1 whitespace-pre-wrap text-slate-800">{text}</div> : null}
215
+ {summaries.length ? (
216
+ <div className="mt-2 space-y-1 border-t border-slate-200 pt-2 text-xs text-slate-500">
217
+ {summaries.map((summary, i) => (
218
+ <div key={`${index}:${i}`} className="whitespace-pre-wrap pl-3 text-slate-500">
219
+ • {summary}
220
+ </div>
221
+ ))}
222
+ </div>
223
+ ) : null}
224
+ </article>
225
+ );
226
+ }
227
+
228
+ /** @param {{ state: ChatState, dispatch: (intent: string, payload?: unknown) => void }} props */
229
+ export default function Chat({ state, dispatch }) {
230
+ const guidance = connectionGuidance(state);
231
+ if (state.target === null) {
232
+ return (
233
+ <div className="flex h-full items-center justify-center p-6 text-sm text-slate-500">
234
+ <NotReady
235
+ headline="missing target"
236
+ explanation="Open chat with a node id so the view can connect."
237
+ nextStep="crtr view run chat --target <node-id>"
238
+ onRetry={() => dispatch('reconnect')}
239
+ />
240
+ </div>
241
+ );
242
+ }
243
+ if (guidance) {
244
+ const Comp = guidance.Comp;
245
+ return (
246
+ <div className="flex h-full items-center justify-center p-6 text-sm text-slate-500">
247
+ <Comp
248
+ headline={guidance.headline}
249
+ explanation={guidance.explanation}
250
+ nextStep={guidance.nextStep}
251
+ onRetry={() => dispatch('reconnect')}
252
+ />
253
+ </div>
254
+ );
255
+ }
256
+
257
+ const connLabel = state.conn === 'open' ? 'connected' : state.conn === 'connecting' ? 'connecting…' : state.conn;
258
+ const sessionName = state.session?.sessionName || '';
259
+ const model = state.session?.model || '';
260
+ const streaming = state.session?.isStreaming === true;
261
+ const canSend = state.channel != null && state.conn === 'open' && state.role === 'controller' && state.draft.trim() !== '';
262
+ const canAbort = state.channel != null && state.conn === 'open' && state.role === 'controller' && streaming;
263
+ const controlLabel = state.role === 'controller' ? 'Release control' : 'Request control';
264
+ const controlIntent = state.role === 'controller' ? 'releaseControl' : 'requestControl';
265
+ const placeholder = state.conn !== 'open'
266
+ ? 'Waiting for connection…'
267
+ : state.role === 'observer'
268
+ ? 'Take control before sending…'
269
+ : streaming
270
+ ? 'Type a steer…'
271
+ : 'Type a message…';
272
+
273
+ return (
274
+ <div className="flex h-full min-h-0 flex-col gap-3 font-mono text-sm text-slate-900 outline-none">
275
+ <div className="flex flex-wrap items-center gap-2 rounded-2xl border border-slate-200 bg-white px-3 py-2 shadow-sm">
276
+ <Pill label="target" value={state.target || '—'} cls="border-slate-200 bg-slate-50 text-slate-700" />
277
+ <Pill label="conn" value={connLabel} cls={state.conn === 'open' ? 'border-emerald-200 bg-emerald-50 text-emerald-700' : state.conn === 'connecting' ? 'border-amber-200 bg-amber-50 text-amber-800' : 'border-slate-200 bg-slate-50 text-slate-700'} />
278
+ <Pill label="role" value={state.role} cls={state.role === 'controller' ? 'border-cyan-200 bg-cyan-50 text-cyan-700' : 'border-slate-200 bg-slate-50 text-slate-700'} />
279
+ {model ? <Pill label="model" value={model} cls="border-violet-200 bg-violet-50 text-violet-700" /> : null}
280
+ {sessionName ? <Pill label="session" value={sessionName} cls="border-slate-200 bg-slate-50 text-slate-700" /> : null}
281
+ {state.controllerId ? <Pill label="controller" value={state.controllerId} cls="border-slate-200 bg-slate-50 text-slate-700" /> : null}
282
+ {typeof state.contextTokens === 'number' ? <Pill label="tokens" value={String(state.contextTokens)} cls="border-slate-200 bg-slate-50 text-slate-700" /> : null}
283
+ {state.transcript.activity ? <Pill label="activity" value={state.transcript.activity} cls="border-amber-200 bg-amber-50 text-amber-800" /> : null}
284
+ {state.queued.length ? <Pill label="queued" value={state.queued.join(' · ')} cls="border-slate-200 bg-slate-50 text-slate-700" /> : null}
285
+ <div className="ml-auto flex flex-wrap items-center gap-2">
286
+ <button type="button" className="rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-700 hover:bg-slate-50" onClick={() => dispatch('reconnect')}>
287
+ Reconnect
288
+ </button>
289
+ <button type="button" className="rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-700 hover:bg-slate-50" onClick={() => dispatch(controlIntent)}>
290
+ {controlLabel}
291
+ </button>
292
+ </div>
293
+ </div>
294
+
295
+ {state.notices.length ? (
296
+ <div className="space-y-2">
297
+ {state.notices.map((notice) => (
298
+ <NoticeRow key={notice.id} notice={notice} onDismiss={(id) => dispatch('clearNotice', id)} />
299
+ ))}
300
+ </div>
301
+ ) : null}
302
+
303
+ <div className="min-h-0 flex-1 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
304
+ <div className="flex h-full min-h-0 flex-col">
305
+ <div className="border-b border-slate-200 px-3 py-2 text-xs uppercase tracking-[0.18em] text-slate-400">
306
+ Transcript
307
+ </div>
308
+ <div className="min-h-0 flex-1 space-y-3 overflow-y-auto p-3">
309
+ {state.transcript.messages.length === 0 ? (
310
+ state.conn === 'connecting' ? (
311
+ <Loading label="Connecting to chat…" />
312
+ ) : state.conn === 'open' ? (
313
+ <Empty label="No messages yet." />
314
+ ) : (
315
+ <div className="flex h-full items-center justify-center text-slate-400">Waiting for welcome…</div>
316
+ )
317
+ ) : (
318
+ state.transcript.messages.map((message, index) => (
319
+ <TranscriptRow
320
+ key={index}
321
+ message={message}
322
+ index={index}
323
+ streaming={index === state.transcript.streamingIndex && streaming}
324
+ />
325
+ ))
326
+ )}
327
+ </div>
328
+ </div>
329
+ </div>
330
+
331
+ <div className="rounded-2xl border border-slate-200 bg-white p-3 shadow-sm">
332
+ <textarea
333
+ value={state.draft}
334
+ onChange={(e) => dispatch('setDraft', e.target.value)}
335
+ onKeyDown={(e) => {
336
+ if (e.key === 'Enter' && !e.shiftKey) {
337
+ e.preventDefault();
338
+ dispatch('submitDraft');
339
+ }
340
+ }}
341
+ placeholder={placeholder}
342
+ rows={3}
343
+ className="w-full resize-none rounded-xl border border-slate-200 bg-slate-50 px-3 py-2 font-mono text-sm outline-none focus:border-slate-400"
344
+ />
345
+ <div className="mt-3 flex flex-wrap items-center gap-2">
346
+ <button
347
+ type="button"
348
+ className={`rounded-lg px-3 py-1.5 text-sm font-medium ${canSend ? 'bg-slate-900 text-white hover:bg-slate-800' : 'cursor-not-allowed bg-slate-200 text-slate-500'}`}
349
+ onClick={() => dispatch('submitDraft')}
350
+ disabled={!canSend}
351
+ >
352
+ Send
353
+ </button>
354
+ <button
355
+ type="button"
356
+ className={`rounded-lg border px-3 py-1.5 text-sm ${canAbort ? 'border-rose-200 bg-rose-50 text-rose-700 hover:bg-rose-100' : 'cursor-not-allowed border-slate-200 bg-slate-50 text-slate-400'}`}
357
+ onClick={() => dispatch('abort')}
358
+ disabled={!canAbort}
359
+ >
360
+ Stop
361
+ </button>
362
+ <div className="ml-auto text-xs text-slate-400">Enter sends · Shift+Enter inserts a newline</div>
363
+ </div>
364
+ </div>
365
+ </div>
366
+ );
367
+ }