whale-code 6.5.4 → 6.5.6

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 (853) hide show
  1. package/README.md +39 -31
  2. package/bin/{swagmanager-mcp.js → whale-code.js} +17 -2
  3. package/dist/cli/app.js +148 -72
  4. package/dist/cli/app.js.map +1 -0
  5. package/dist/cli/chat/AgentSelector.js +105 -10
  6. package/dist/cli/chat/AgentSelector.js.map +1 -0
  7. package/dist/cli/chat/ChatApp.d.ts +31 -0
  8. package/dist/cli/chat/ChatApp.js +539 -286
  9. package/dist/cli/chat/ChatApp.js.map +1 -0
  10. package/dist/cli/chat/ChatInput.js +1088 -770
  11. package/dist/cli/chat/ChatInput.js.map +1 -0
  12. package/dist/cli/chat/MarkdownText.js +39 -14
  13. package/dist/cli/chat/MarkdownText.js.map +1 -0
  14. package/dist/cli/chat/MemoryManager.js +181 -46
  15. package/dist/cli/chat/MemoryManager.js.map +1 -0
  16. package/dist/cli/chat/MessageList.d.ts +2 -3
  17. package/dist/cli/chat/MessageList.js +186 -45
  18. package/dist/cli/chat/MessageList.js.map +1 -0
  19. package/dist/cli/chat/ModelSelector.js +282 -63
  20. package/dist/cli/chat/ModelSelector.js.map +1 -0
  21. package/dist/cli/chat/NodeManager.js +165 -75
  22. package/dist/cli/chat/NodeManager.js.map +1 -0
  23. package/dist/cli/chat/NodeSelector.js +171 -30
  24. package/dist/cli/chat/NodeSelector.js.map +1 -0
  25. package/dist/cli/chat/PlanApproval.js +281 -57
  26. package/dist/cli/chat/PlanApproval.js.map +1 -0
  27. package/dist/cli/chat/RewindViewer.js +559 -144
  28. package/dist/cli/chat/RewindViewer.js.map +1 -0
  29. package/dist/cli/chat/SessionManager.js +137 -30
  30. package/dist/cli/chat/SessionManager.js.map +1 -0
  31. package/dist/cli/chat/SlashMenu.js +293 -164
  32. package/dist/cli/chat/SlashMenu.js.map +1 -0
  33. package/dist/cli/chat/StatusBar.js +172 -9
  34. package/dist/cli/chat/StatusBar.js.map +1 -0
  35. package/dist/cli/chat/StoreSelector.js +147 -18
  36. package/dist/cli/chat/StoreSelector.js.map +1 -0
  37. package/dist/cli/chat/StreamingText.d.ts +1 -5
  38. package/dist/cli/chat/StreamingText.js +22 -7
  39. package/dist/cli/chat/StreamingText.js.map +1 -0
  40. package/dist/cli/chat/SubagentPanel.d.ts +1 -2
  41. package/dist/cli/chat/SubagentPanel.js +612 -72
  42. package/dist/cli/chat/SubagentPanel.js.map +1 -0
  43. package/dist/cli/chat/TeamPanel.d.ts +1 -0
  44. package/dist/cli/chat/TeamPanel.js +230 -30
  45. package/dist/cli/chat/TeamPanel.js.map +1 -0
  46. package/dist/cli/chat/ThemeSelector.js +84 -24
  47. package/dist/cli/chat/ThemeSelector.js.map +1 -0
  48. package/dist/cli/chat/ToolIndicator.js +1476 -371
  49. package/dist/cli/chat/ToolIndicator.js.map +1 -0
  50. package/dist/cli/chat/hooks/useAgentLoop.d.ts +1 -0
  51. package/dist/cli/chat/hooks/useAgentLoop.js +481 -367
  52. package/dist/cli/chat/hooks/useAgentLoop.js.map +1 -0
  53. package/dist/cli/chat/hooks/useSlashCommands.d.ts +3 -14
  54. package/dist/cli/chat/hooks/useSlashCommands.js +744 -572
  55. package/dist/cli/chat/hooks/useSlashCommands.js.map +1 -0
  56. package/dist/cli/commands/config-cmd.js +56 -57
  57. package/dist/cli/commands/config-cmd.js.map +1 -0
  58. package/dist/cli/commands/db.js +184 -169
  59. package/dist/cli/commands/db.js.map +1 -0
  60. package/dist/cli/commands/doctor.js +212 -122
  61. package/dist/cli/commands/doctor.js.map +1 -0
  62. package/dist/cli/commands/init.js +211 -244
  63. package/dist/cli/commands/init.js.map +1 -0
  64. package/dist/cli/commands/mcp.js +127 -122
  65. package/dist/cli/commands/mcp.js.map +1 -0
  66. package/dist/cli/login/LoginApp.js +355 -141
  67. package/dist/cli/login/LoginApp.js.map +1 -0
  68. package/dist/cli/print-mode.js +196 -177
  69. package/dist/cli/print-mode.js.map +1 -0
  70. package/dist/cli/serve-mode.js +615 -530
  71. package/dist/cli/serve-mode.js.map +1 -0
  72. package/dist/cli/services/agent-config.d.ts +29 -0
  73. package/dist/cli/services/agent-config.js +91 -0
  74. package/dist/cli/services/agent-config.js.map +1 -0
  75. package/dist/cli/services/agent-definitions.d.ts +4 -1
  76. package/dist/cli/services/agent-definitions.js +97 -56
  77. package/dist/cli/services/agent-definitions.js.map +1 -0
  78. package/dist/cli/services/agent-events.js +225 -162
  79. package/dist/cli/services/agent-events.js.map +1 -0
  80. package/dist/cli/services/agent-loop.js +978 -669
  81. package/dist/cli/services/agent-loop.js.map +1 -0
  82. package/dist/cli/services/agent-worker-base.d.ts +35 -5
  83. package/dist/cli/services/agent-worker-base.js +337 -153
  84. package/dist/cli/services/agent-worker-base.js.map +1 -0
  85. package/dist/cli/services/api-retry.js +69 -64
  86. package/dist/cli/services/api-retry.js.map +1 -0
  87. package/dist/cli/services/auth-service.d.ts +3 -3
  88. package/dist/cli/services/auth-service.js +209 -132
  89. package/dist/cli/services/auth-service.js.map +1 -0
  90. package/dist/cli/services/background-processes.js +343 -267
  91. package/dist/cli/services/background-processes.js.map +1 -0
  92. package/dist/cli/services/browser-auth.d.ts +2 -2
  93. package/dist/cli/services/browser-auth.js +159 -118
  94. package/dist/cli/services/browser-auth.js.map +1 -0
  95. package/dist/cli/services/claude-md-loader.js +40 -36
  96. package/dist/cli/services/claude-md-loader.js.map +1 -0
  97. package/dist/cli/services/config-store.d.ts +9 -4
  98. package/dist/cli/services/config-store.js +164 -117
  99. package/dist/cli/services/config-store.js.map +1 -0
  100. package/dist/cli/services/debug-log.d.ts +1 -1
  101. package/dist/cli/services/debug-log.js +34 -35
  102. package/dist/cli/services/debug-log.js.map +1 -0
  103. package/dist/cli/services/env-detect.d.ts +7 -0
  104. package/dist/cli/services/env-detect.js +9 -0
  105. package/dist/cli/services/env-detect.js.map +1 -0
  106. package/dist/cli/services/error-logger.d.ts +2 -3
  107. package/dist/cli/services/error-logger.js +189 -180
  108. package/dist/cli/services/error-logger.js.map +1 -0
  109. package/dist/cli/services/file-history.d.ts +1 -1
  110. package/dist/cli/services/file-history.js +50 -54
  111. package/dist/cli/services/file-history.js.map +1 -0
  112. package/dist/cli/services/format-server-response.js +332 -372
  113. package/dist/cli/services/format-server-response.js.map +1 -0
  114. package/dist/cli/services/git-context.js +61 -45
  115. package/dist/cli/services/git-context.js.map +1 -0
  116. package/dist/cli/services/hooks.d.ts +2 -2
  117. package/dist/cli/services/hooks.js +195 -180
  118. package/dist/cli/services/hooks.js.map +1 -0
  119. package/dist/cli/services/ink-incremental.d.ts +19 -0
  120. package/dist/cli/services/ink-incremental.js +59 -0
  121. package/dist/cli/services/ink-incremental.js.map +1 -0
  122. package/dist/cli/services/ink-resize-fix.js +54 -44
  123. package/dist/cli/services/ink-resize-fix.js.map +1 -0
  124. package/dist/cli/services/ink-sync-output.d.ts +12 -0
  125. package/dist/cli/services/ink-sync-output.js +16 -0
  126. package/dist/cli/services/ink-sync-output.js.map +1 -0
  127. package/dist/cli/services/interactive-tools.js +268 -212
  128. package/dist/cli/services/interactive-tools.js.map +1 -0
  129. package/dist/cli/services/keybinding-manager.d.ts +11 -1
  130. package/dist/cli/services/keybinding-manager.js +126 -63
  131. package/dist/cli/services/keybinding-manager.js.map +1 -0
  132. package/dist/cli/services/local-tools.d.ts +1 -1
  133. package/dist/cli/services/local-tools.js +939 -656
  134. package/dist/cli/services/local-tools.js.map +1 -0
  135. package/dist/cli/services/lsp-manager.js +757 -594
  136. package/dist/cli/services/lsp-manager.js.map +1 -0
  137. package/dist/cli/services/mcp-client.d.ts +1 -1
  138. package/dist/cli/services/mcp-client.js +173 -134
  139. package/dist/cli/services/mcp-client.js.map +1 -0
  140. package/dist/cli/services/memory-manager.js +53 -40
  141. package/dist/cli/services/memory-manager.js.map +1 -0
  142. package/dist/cli/services/model-manager.js +55 -40
  143. package/dist/cli/services/model-manager.js.map +1 -0
  144. package/dist/cli/services/model-router.js +115 -85
  145. package/dist/cli/services/model-router.js.map +1 -0
  146. package/dist/cli/services/paths.d.ts +30 -0
  147. package/dist/cli/services/paths.js +81 -0
  148. package/dist/cli/services/paths.js.map +1 -0
  149. package/dist/cli/services/permission-modes.js +32 -25
  150. package/dist/cli/services/permission-modes.js.map +1 -0
  151. package/dist/cli/services/rewind.js +182 -168
  152. package/dist/cli/services/rewind.js.map +1 -0
  153. package/dist/cli/services/ripgrep.js +115 -115
  154. package/dist/cli/services/ripgrep.js.map +1 -0
  155. package/dist/cli/services/sandbox.d.ts +1 -1
  156. package/dist/cli/services/sandbox.js +58 -37
  157. package/dist/cli/services/sandbox.js.map +1 -0
  158. package/dist/cli/services/server-tools.js +738 -565
  159. package/dist/cli/services/server-tools.js.map +1 -0
  160. package/dist/cli/services/session-persistence.js +69 -74
  161. package/dist/cli/services/session-persistence.js.map +1 -0
  162. package/dist/cli/services/subagent-worker.js +42 -27
  163. package/dist/cli/services/subagent-worker.js.map +1 -0
  164. package/dist/cli/services/subagent.d.ts +2 -0
  165. package/dist/cli/services/subagent.js +606 -430
  166. package/dist/cli/services/subagent.js.map +1 -0
  167. package/dist/cli/services/system-prompt.js +86 -78
  168. package/dist/cli/services/system-prompt.js.map +1 -0
  169. package/dist/cli/services/task-decomposer.d.ts +1 -1
  170. package/dist/cli/services/task-decomposer.js +172 -139
  171. package/dist/cli/services/task-decomposer.js.map +1 -0
  172. package/dist/cli/services/team-lead.d.ts +2 -2
  173. package/dist/cli/services/team-lead.js +727 -529
  174. package/dist/cli/services/team-lead.js.map +1 -0
  175. package/dist/cli/services/team-state.js +319 -319
  176. package/dist/cli/services/team-state.js.map +1 -0
  177. package/dist/cli/services/teammate.d.ts +8 -2
  178. package/dist/cli/services/teammate.js +862 -560
  179. package/dist/cli/services/teammate.js.map +1 -0
  180. package/dist/cli/services/telemetry.d.ts +6 -1
  181. package/dist/cli/services/telemetry.js +180 -157
  182. package/dist/cli/services/telemetry.js.map +1 -0
  183. package/dist/cli/services/tools/agent-tools.d.ts +3 -3
  184. package/dist/cli/services/tools/agent-tools.js +480 -322
  185. package/dist/cli/services/tools/agent-tools.js.map +1 -0
  186. package/dist/cli/services/tools/file-ops.js +563 -450
  187. package/dist/cli/services/tools/file-ops.js.map +1 -0
  188. package/dist/cli/services/tools/search-tools.js +231 -162
  189. package/dist/cli/services/tools/search-tools.js.map +1 -0
  190. package/dist/cli/services/tools/shell-exec.js +197 -151
  191. package/dist/cli/services/tools/shell-exec.js.map +1 -0
  192. package/dist/cli/services/tools/task-manager.js +206 -173
  193. package/dist/cli/services/tools/task-manager.js.map +1 -0
  194. package/dist/cli/services/tools/web-tools.js +388 -341
  195. package/dist/cli/services/tools/web-tools.js.map +1 -0
  196. package/dist/cli/setup/SetupApp.d.ts +2 -2
  197. package/dist/cli/setup/SetupApp.js +608 -160
  198. package/dist/cli/setup/SetupApp.js.map +1 -0
  199. package/dist/cli/shared/ErrorBoundary.d.ts +22 -0
  200. package/dist/cli/shared/ErrorBoundary.js +73 -0
  201. package/dist/cli/shared/ErrorBoundary.js.map +1 -0
  202. package/dist/cli/shared/MatrixIntro.js +66 -69
  203. package/dist/cli/shared/MatrixIntro.js.map +1 -0
  204. package/dist/cli/shared/SpinnerSlot.d.ts +14 -0
  205. package/dist/cli/shared/SpinnerSlot.js +63 -0
  206. package/dist/cli/shared/SpinnerSlot.js.map +1 -0
  207. package/dist/cli/shared/Theme.d.ts +1 -1
  208. package/dist/cli/shared/Theme.js +136 -92
  209. package/dist/cli/shared/Theme.js.map +1 -0
  210. package/dist/cli/shared/WhaleBanner.js +99 -11
  211. package/dist/cli/shared/WhaleBanner.js.map +1 -0
  212. package/dist/cli/shared/markdown.d.ts +3 -1
  213. package/dist/cli/shared/markdown.js +736 -674
  214. package/dist/cli/shared/markdown.js.map +1 -0
  215. package/dist/cli/shared/marked-terminal.d.js +2 -0
  216. package/dist/cli/shared/marked-terminal.d.js.map +1 -0
  217. package/dist/cli/shared/theme-manager.js +99 -90
  218. package/dist/cli/shared/theme-manager.js.map +1 -0
  219. package/dist/cli/shared/theme-presets.js +256 -254
  220. package/dist/cli/shared/theme-presets.js.map +1 -0
  221. package/dist/cli/status/StatusApp.js +235 -86
  222. package/dist/cli/status/StatusApp.js.map +1 -0
  223. package/dist/cli/stores/StoreApp.js +275 -65
  224. package/dist/cli/stores/StoreApp.js.map +1 -0
  225. package/dist/index.d.ts +2 -2
  226. package/dist/index.js +509 -396
  227. package/dist/index.js.map +1 -0
  228. package/dist/local-agent/connection.d.ts +2 -2
  229. package/dist/local-agent/connection.js +352 -293
  230. package/dist/local-agent/connection.js.map +1 -0
  231. package/dist/local-agent/discovery.js +259 -122
  232. package/dist/local-agent/discovery.js.map +1 -0
  233. package/dist/local-agent/executor.js +216 -193
  234. package/dist/local-agent/executor.js.map +1 -0
  235. package/dist/local-agent/index.d.ts +2 -2
  236. package/dist/local-agent/index.js +156 -156
  237. package/dist/local-agent/index.js.map +1 -0
  238. package/dist/node/adapters/base.js +18 -8
  239. package/dist/node/adapters/base.js.map +1 -0
  240. package/dist/node/adapters/discord.js +286 -275
  241. package/dist/node/adapters/discord.js.map +1 -0
  242. package/dist/node/adapters/email.js +189 -202
  243. package/dist/node/adapters/email.js.map +1 -0
  244. package/dist/node/adapters/imessage.js +145 -142
  245. package/dist/node/adapters/imessage.js.map +1 -0
  246. package/dist/node/adapters/slack.js +237 -236
  247. package/dist/node/adapters/slack.js.map +1 -0
  248. package/dist/node/adapters/sms.js +149 -151
  249. package/dist/node/adapters/sms.js.map +1 -0
  250. package/dist/node/adapters/telegram.js +88 -92
  251. package/dist/node/adapters/telegram.js.map +1 -0
  252. package/dist/node/adapters/webchat.js +160 -136
  253. package/dist/node/adapters/webchat.js.map +1 -0
  254. package/dist/node/adapters/whatsapp.js +212 -215
  255. package/dist/node/adapters/whatsapp.js.map +1 -0
  256. package/dist/node/cli.js +884 -653
  257. package/dist/node/cli.js.map +1 -0
  258. package/dist/node/config.js +20 -18
  259. package/dist/node/config.js.map +1 -0
  260. package/dist/node/gateway-client.js +191 -181
  261. package/dist/node/gateway-client.js.map +1 -0
  262. package/dist/node/portal/clipboard.js +161 -130
  263. package/dist/node/portal/clipboard.js.map +1 -0
  264. package/dist/node/portal/discovery.js +51 -45
  265. package/dist/node/portal/discovery.js.map +1 -0
  266. package/dist/node/portal/forward.js +64 -58
  267. package/dist/node/portal/forward.js.map +1 -0
  268. package/dist/node/portal/index.js +246 -221
  269. package/dist/node/portal/index.js.map +1 -0
  270. package/dist/node/portal/multiplexer.js +192 -182
  271. package/dist/node/portal/multiplexer.js.map +1 -0
  272. package/dist/node/portal/permissions.js +102 -70
  273. package/dist/node/portal/permissions.js.map +1 -0
  274. package/dist/node/portal/protocol.js +153 -116
  275. package/dist/node/portal/protocol.js.map +1 -0
  276. package/dist/node/portal/screen.js +80 -69
  277. package/dist/node/portal/screen.js.map +1 -0
  278. package/dist/node/portal/session.js +124 -117
  279. package/dist/node/portal/session.js.map +1 -0
  280. package/dist/node/portal/shell.js +140 -113
  281. package/dist/node/portal/shell.js.map +1 -0
  282. package/dist/node/portal/stream.js +77 -75
  283. package/dist/node/portal/stream.js.map +1 -0
  284. package/dist/node/portal/transfer.js +190 -167
  285. package/dist/node/portal/transfer.js.map +1 -0
  286. package/dist/node/portal/ui.js +124 -99
  287. package/dist/node/portal/ui.js.map +1 -0
  288. package/dist/node/remote-desktop/compile-helper.js +50 -45
  289. package/dist/node/remote-desktop/compile-helper.js.map +1 -0
  290. package/dist/node/remote-desktop/index.js +215 -187
  291. package/dist/node/remote-desktop/index.js.map +1 -0
  292. package/dist/node/remote-desktop/protocol.js +45 -29
  293. package/dist/node/remote-desktop/protocol.js.map +1 -0
  294. package/dist/node/runtime.js +493 -410
  295. package/dist/node/runtime.js.map +1 -0
  296. package/dist/server/handlers/__test-utils__/test-db.js +39 -89
  297. package/dist/server/handlers/__test-utils__/test-db.js.map +1 -0
  298. package/dist/server/handlers/analytics.js +467 -261
  299. package/dist/server/handlers/analytics.js.map +1 -0
  300. package/dist/server/handlers/api-docs.d.ts +6 -0
  301. package/dist/server/handlers/api-docs.js +1613 -0
  302. package/dist/server/handlers/api-docs.js.map +1 -0
  303. package/dist/server/handlers/api-keys.js +295 -232
  304. package/dist/server/handlers/api-keys.js.map +1 -0
  305. package/dist/server/handlers/billing.js +330 -239
  306. package/dist/server/handlers/billing.js.map +1 -0
  307. package/dist/server/handlers/browser.js +468 -395
  308. package/dist/server/handlers/browser.js.map +1 -0
  309. package/dist/server/handlers/catalog.js +1377 -978
  310. package/dist/server/handlers/catalog.js.map +1 -0
  311. package/dist/server/handlers/clickhouse.js +157 -109
  312. package/dist/server/handlers/clickhouse.js.map +1 -0
  313. package/dist/server/handlers/comms.d.ts +0 -53
  314. package/dist/server/handlers/comms.js +1443 -970
  315. package/dist/server/handlers/comms.js.map +1 -0
  316. package/dist/server/handlers/creations.js +461 -394
  317. package/dist/server/handlers/creations.js.map +1 -0
  318. package/dist/server/handlers/crm.js +1082 -791
  319. package/dist/server/handlers/crm.js.map +1 -0
  320. package/dist/server/handlers/discovery.js +251 -232
  321. package/dist/server/handlers/discovery.js.map +1 -0
  322. package/dist/server/handlers/embeddings.js +241 -164
  323. package/dist/server/handlers/embeddings.js.map +1 -0
  324. package/dist/server/handlers/enrichment.js +887 -718
  325. package/dist/server/handlers/enrichment.js.map +1 -0
  326. package/dist/server/handlers/image-gen.js +467 -376
  327. package/dist/server/handlers/image-gen.js.map +1 -0
  328. package/dist/server/handlers/inventory.js +797 -424
  329. package/dist/server/handlers/inventory.js.map +1 -0
  330. package/dist/server/handlers/kali.js +272 -230
  331. package/dist/server/handlers/kali.js.map +1 -0
  332. package/dist/server/handlers/llm-providers.js +803 -580
  333. package/dist/server/handlers/llm-providers.js.map +1 -0
  334. package/dist/server/handlers/local-agent.js +133 -105
  335. package/dist/server/handlers/local-agent.js.map +1 -0
  336. package/dist/server/handlers/media.js +1179 -857
  337. package/dist/server/handlers/media.js.map +1 -0
  338. package/dist/server/handlers/meta-ads.js +2669 -2093
  339. package/dist/server/handlers/meta-ads.js.map +1 -0
  340. package/dist/server/handlers/nodes.js +1321 -913
  341. package/dist/server/handlers/nodes.js.map +1 -0
  342. package/dist/server/handlers/operations.js +183 -157
  343. package/dist/server/handlers/operations.js.map +1 -0
  344. package/dist/server/handlers/platform.js +346 -210
  345. package/dist/server/handlers/platform.js.map +1 -0
  346. package/dist/server/handlers/remove-bg.js +118 -86
  347. package/dist/server/handlers/remove-bg.js.map +1 -0
  348. package/dist/server/handlers/storefront.js +586 -446
  349. package/dist/server/handlers/storefront.js.map +1 -0
  350. package/dist/server/handlers/supply-chain.js +546 -326
  351. package/dist/server/handlers/supply-chain.js.map +1 -0
  352. package/dist/server/handlers/transcription.js +106 -97
  353. package/dist/server/handlers/transcription.js.map +1 -0
  354. package/dist/server/handlers/video-gen.js +593 -424
  355. package/dist/server/handlers/video-gen.js.map +1 -0
  356. package/dist/server/handlers/voice.js +1458 -1017
  357. package/dist/server/handlers/voice.js.map +1 -0
  358. package/dist/server/handlers/workflow-steps.js +2837 -2116
  359. package/dist/server/handlers/workflow-steps.js.map +1 -0
  360. package/dist/server/handlers/workflows.js +1630 -933
  361. package/dist/server/handlers/workflows.js.map +1 -0
  362. package/dist/server/index.js +3166 -2390
  363. package/dist/server/index.js.map +1 -0
  364. package/dist/server/lib/batch-client.js +471 -409
  365. package/dist/server/lib/batch-client.js.map +1 -0
  366. package/dist/server/lib/clickhouse-buffer.js +118 -104
  367. package/dist/server/lib/clickhouse-buffer.js.map +1 -0
  368. package/dist/server/lib/clickhouse-client.js +107 -107
  369. package/dist/server/lib/clickhouse-client.js.map +1 -0
  370. package/dist/server/lib/coa-renderer.js +1786 -356
  371. package/dist/server/lib/coa-renderer.js.map +1 -0
  372. package/dist/server/lib/code-worker-pool.js +227 -177
  373. package/dist/server/lib/code-worker-pool.js.map +1 -0
  374. package/dist/server/lib/code-worker.js +174 -164
  375. package/dist/server/lib/code-worker.js.map +1 -0
  376. package/dist/server/lib/compaction-service.d.ts +2 -12
  377. package/dist/server/lib/compaction-service.js +74 -184
  378. package/dist/server/lib/compaction-service.js.map +1 -0
  379. package/dist/server/lib/logger.js +36 -24
  380. package/dist/server/lib/logger.js.map +1 -0
  381. package/dist/server/lib/otel.js +101 -80
  382. package/dist/server/lib/otel.js.map +1 -0
  383. package/dist/server/lib/pdf-renderer.d.ts +1 -1
  384. package/dist/server/lib/pdf-renderer.js +954 -776
  385. package/dist/server/lib/pdf-renderer.js.map +1 -0
  386. package/dist/server/lib/prompt-sanitizer.js +188 -108
  387. package/dist/server/lib/prompt-sanitizer.js.map +1 -0
  388. package/dist/server/lib/provider-capabilities.js +136 -138
  389. package/dist/server/lib/provider-capabilities.js.map +1 -0
  390. package/dist/server/lib/provider-failover.js +190 -168
  391. package/dist/server/lib/provider-failover.js.map +1 -0
  392. package/dist/server/lib/rate-limiter.js +186 -117
  393. package/dist/server/lib/rate-limiter.js.map +1 -0
  394. package/dist/server/lib/react-pdf-layout.js +551 -382
  395. package/dist/server/lib/react-pdf-layout.js.map +1 -0
  396. package/dist/server/lib/server-agent-loop.d.ts +9 -0
  397. package/dist/server/lib/server-agent-loop.js +906 -624
  398. package/dist/server/lib/server-agent-loop.js.map +1 -0
  399. package/dist/server/lib/server-subagent.d.ts +2 -0
  400. package/dist/server/lib/server-subagent.js +260 -162
  401. package/dist/server/lib/server-subagent.js.map +1 -0
  402. package/dist/server/lib/session-checkpoint.js +105 -96
  403. package/dist/server/lib/session-checkpoint.js.map +1 -0
  404. package/dist/server/lib/ssrf-guard.js +193 -184
  405. package/dist/server/lib/ssrf-guard.js.map +1 -0
  406. package/dist/server/lib/supabase-client.js +94 -82
  407. package/dist/server/lib/supabase-client.js.map +1 -0
  408. package/dist/server/lib/template-resolver.js +154 -176
  409. package/dist/server/lib/template-resolver.js.map +1 -0
  410. package/dist/server/lib/utils.js +242 -133
  411. package/dist/server/lib/utils.js.map +1 -0
  412. package/dist/server/local-agent-gateway.d.ts +2 -2
  413. package/dist/server/local-agent-gateway.js +785 -627
  414. package/dist/server/local-agent-gateway.js.map +1 -0
  415. package/dist/server/providers/anthropic.js +254 -176
  416. package/dist/server/providers/anthropic.js.map +1 -0
  417. package/dist/server/providers/bedrock.js +221 -162
  418. package/dist/server/providers/bedrock.js.map +1 -0
  419. package/dist/server/providers/gemini.js +548 -418
  420. package/dist/server/providers/gemini.js.map +1 -0
  421. package/dist/server/providers/openai.js +571 -437
  422. package/dist/server/providers/openai.js.map +1 -0
  423. package/dist/server/providers/registry.js +23 -18
  424. package/dist/server/providers/registry.js.map +1 -0
  425. package/dist/server/providers/shared.js +123 -95
  426. package/dist/server/providers/shared.js.map +1 -0
  427. package/dist/server/providers/types.js +1 -11
  428. package/dist/server/providers/types.js.map +1 -0
  429. package/dist/server/proxy-handlers.js +209 -165
  430. package/dist/server/proxy-handlers.js.map +1 -0
  431. package/dist/server/tool-router.d.ts +13 -0
  432. package/dist/server/tool-router.js +960 -598
  433. package/dist/server/tool-router.js.map +1 -0
  434. package/dist/server/validation.js +248 -188
  435. package/dist/server/validation.js.map +1 -0
  436. package/dist/server/worker.js +202 -133
  437. package/dist/server/worker.js.map +1 -0
  438. package/dist/setup.d.ts +2 -2
  439. package/dist/setup.js +151 -147
  440. package/dist/setup.js.map +1 -0
  441. package/dist/shared/agent-core.d.ts +191 -24
  442. package/dist/shared/agent-core.js +971 -462
  443. package/dist/shared/agent-core.js.map +1 -0
  444. package/dist/shared/anthropic-types.js +1 -6
  445. package/dist/shared/anthropic-types.js.map +1 -0
  446. package/dist/shared/api-client.d.ts +17 -9
  447. package/dist/shared/api-client.js +419 -327
  448. package/dist/shared/api-client.js.map +1 -0
  449. package/dist/shared/compaction.d.ts +36 -0
  450. package/dist/shared/compaction.js +138 -0
  451. package/dist/shared/compaction.js.map +1 -0
  452. package/dist/shared/constants.js +67 -64
  453. package/dist/shared/constants.js.map +1 -0
  454. package/dist/shared/sse-parser.js +221 -219
  455. package/dist/shared/sse-parser.js.map +1 -0
  456. package/dist/shared/tool-dispatch.d.ts +4 -2
  457. package/dist/shared/tool-dispatch.js +226 -165
  458. package/dist/shared/tool-dispatch.js.map +1 -0
  459. package/dist/shared/types.js +1 -6
  460. package/dist/shared/types.js.map +1 -0
  461. package/dist/types/cli-highlight.d.js +2 -0
  462. package/dist/types/cli-highlight.d.js.map +1 -0
  463. package/dist/types/diff.d.js +2 -0
  464. package/dist/types/diff.d.js.map +1 -0
  465. package/dist/types/pdf-parse.d.js +2 -0
  466. package/dist/types/pdf-parse.d.js.map +1 -0
  467. package/dist/updater.d.ts +1 -1
  468. package/dist/updater.js +118 -92
  469. package/dist/updater.js.map +1 -0
  470. package/dist/webchat/widget.js +227 -380
  471. package/dist/webchat/widget.js.map +1 -0
  472. package/package.json +22 -10
  473. package/vendor/ink/build/ansi-tokenizer.d.ts +38 -0
  474. package/vendor/ink/build/ansi-tokenizer.js +316 -0
  475. package/vendor/ink/build/ansi-tokenizer.js.map +1 -0
  476. package/vendor/ink/build/apply-styles.js +175 -0
  477. package/vendor/ink/build/build-layout.js +77 -0
  478. package/vendor/ink/build/calculate-wrapped-text.js +53 -0
  479. package/vendor/ink/build/colorize.d.ts +3 -0
  480. package/vendor/ink/build/colorize.js +48 -0
  481. package/vendor/ink/build/colorize.js.map +1 -0
  482. package/vendor/ink/build/components/AccessibilityContext.d.ts +3 -0
  483. package/vendor/ink/build/components/AccessibilityContext.js +5 -0
  484. package/vendor/ink/build/components/AccessibilityContext.js.map +1 -0
  485. package/vendor/ink/build/components/App.d.ts +18 -0
  486. package/vendor/ink/build/components/App.js +351 -0
  487. package/vendor/ink/build/components/App.js.map +1 -0
  488. package/vendor/ink/build/components/AppContext.d.ts +15 -0
  489. package/vendor/ink/build/components/AppContext.js +11 -0
  490. package/vendor/ink/build/components/AppContext.js.map +1 -0
  491. package/vendor/ink/build/components/BackgroundContext.d.ts +4 -0
  492. package/vendor/ink/build/components/BackgroundContext.js +3 -0
  493. package/vendor/ink/build/components/BackgroundContext.js.map +1 -0
  494. package/vendor/ink/build/components/Box.d.ts +117 -0
  495. package/vendor/ink/build/components/Box.js +34 -0
  496. package/vendor/ink/build/components/Box.js.map +1 -0
  497. package/vendor/ink/build/components/Color.js +62 -0
  498. package/vendor/ink/build/components/Cursor.d.ts +83 -0
  499. package/vendor/ink/build/components/Cursor.js +53 -0
  500. package/vendor/ink/build/components/Cursor.js.map +1 -0
  501. package/vendor/ink/build/components/CursorContext.d.ts +11 -0
  502. package/vendor/ink/build/components/CursorContext.js +8 -0
  503. package/vendor/ink/build/components/CursorContext.js.map +1 -0
  504. package/vendor/ink/build/components/ErrorBoundary.d.ts +18 -0
  505. package/vendor/ink/build/components/ErrorBoundary.js +23 -0
  506. package/vendor/ink/build/components/ErrorBoundary.js.map +1 -0
  507. package/vendor/ink/build/components/ErrorOverview.d.ts +6 -0
  508. package/vendor/ink/build/components/ErrorOverview.js +84 -0
  509. package/vendor/ink/build/components/ErrorOverview.js.map +1 -0
  510. package/vendor/ink/build/components/FocusContext.d.ts +16 -0
  511. package/vendor/ink/build/components/FocusContext.js +17 -0
  512. package/vendor/ink/build/components/FocusContext.js.map +1 -0
  513. package/vendor/ink/build/components/Newline.d.ts +13 -0
  514. package/vendor/ink/build/components/Newline.js +8 -0
  515. package/vendor/ink/build/components/Newline.js.map +1 -0
  516. package/vendor/ink/build/components/Spacer.d.ts +7 -0
  517. package/vendor/ink/build/components/Spacer.js +11 -0
  518. package/vendor/ink/build/components/Spacer.js.map +1 -0
  519. package/vendor/ink/build/components/Static.d.ts +24 -0
  520. package/vendor/ink/build/components/Static.js +28 -0
  521. package/vendor/ink/build/components/Static.js.map +1 -0
  522. package/vendor/ink/build/components/StderrContext.d.ts +15 -0
  523. package/vendor/ink/build/components/StderrContext.js +13 -0
  524. package/vendor/ink/build/components/StderrContext.js.map +1 -0
  525. package/vendor/ink/build/components/StdinContext.d.ts +22 -0
  526. package/vendor/ink/build/components/StdinContext.js +19 -0
  527. package/vendor/ink/build/components/StdinContext.js.map +1 -0
  528. package/vendor/ink/build/components/StdoutContext.d.ts +15 -0
  529. package/vendor/ink/build/components/StdoutContext.js +13 -0
  530. package/vendor/ink/build/components/StdoutContext.js.map +1 -0
  531. package/vendor/ink/build/components/Text.d.ts +55 -0
  532. package/vendor/ink/build/components/Text.js +50 -0
  533. package/vendor/ink/build/components/Text.js.map +1 -0
  534. package/vendor/ink/build/components/Transform.d.ts +16 -0
  535. package/vendor/ink/build/components/Transform.js +15 -0
  536. package/vendor/ink/build/components/Transform.js.map +1 -0
  537. package/vendor/ink/build/cursor-helpers.d.ts +38 -0
  538. package/vendor/ink/build/cursor-helpers.js +56 -0
  539. package/vendor/ink/build/cursor-helpers.js.map +1 -0
  540. package/vendor/ink/build/devtools-window-polyfill.d.ts +1 -0
  541. package/vendor/ink/build/devtools-window-polyfill.js +65 -0
  542. package/vendor/ink/build/devtools-window-polyfill.js.map +1 -0
  543. package/vendor/ink/build/devtools.d.ts +1 -0
  544. package/vendor/ink/build/devtools.js +11 -0
  545. package/vendor/ink/build/devtools.js.map +1 -0
  546. package/vendor/ink/build/dom.d.ts +56 -0
  547. package/vendor/ink/build/dom.js +124 -0
  548. package/vendor/ink/build/dom.js.map +1 -0
  549. package/vendor/ink/build/experimental/apply-style.js +140 -0
  550. package/vendor/ink/build/experimental/dom.js +123 -0
  551. package/vendor/ink/build/experimental/output.js +91 -0
  552. package/vendor/ink/build/experimental/reconciler.js +141 -0
  553. package/vendor/ink/build/experimental/renderer.js +81 -0
  554. package/vendor/ink/build/get-max-width.d.ts +3 -0
  555. package/vendor/ink/build/get-max-width.js +10 -0
  556. package/vendor/ink/build/get-max-width.js.map +1 -0
  557. package/vendor/ink/build/hooks/use-app.d.ts +5 -0
  558. package/vendor/ink/build/hooks/use-app.js +8 -0
  559. package/vendor/ink/build/hooks/use-app.js.map +1 -0
  560. package/vendor/ink/build/hooks/use-cursor.d.ts +12 -0
  561. package/vendor/ink/build/hooks/use-cursor.js +29 -0
  562. package/vendor/ink/build/hooks/use-cursor.js.map +1 -0
  563. package/vendor/ink/build/hooks/use-focus-manager.d.ts +28 -0
  564. package/vendor/ink/build/hooks/use-focus-manager.js +17 -0
  565. package/vendor/ink/build/hooks/use-focus-manager.js.map +1 -0
  566. package/vendor/ink/build/hooks/use-focus.d.ts +29 -0
  567. package/vendor/ink/build/hooks/use-focus.js +42 -0
  568. package/vendor/ink/build/hooks/use-focus.js.map +1 -0
  569. package/vendor/ink/build/hooks/use-input.d.ts +131 -0
  570. package/vendor/ink/build/hooks/use-input.js +124 -0
  571. package/vendor/ink/build/hooks/use-input.js.map +1 -0
  572. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.d.ts +5 -0
  573. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.js +11 -0
  574. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.js.map +1 -0
  575. package/vendor/ink/build/hooks/use-stderr.d.ts +5 -0
  576. package/vendor/ink/build/hooks/use-stderr.js +8 -0
  577. package/vendor/ink/build/hooks/use-stderr.js.map +1 -0
  578. package/vendor/ink/build/hooks/use-stdin.d.ts +5 -0
  579. package/vendor/ink/build/hooks/use-stdin.js +8 -0
  580. package/vendor/ink/build/hooks/use-stdin.js.map +1 -0
  581. package/vendor/ink/build/hooks/use-stdout.d.ts +5 -0
  582. package/vendor/ink/build/hooks/use-stdout.js +8 -0
  583. package/vendor/ink/build/hooks/use-stdout.js.map +1 -0
  584. package/vendor/ink/build/hooks/useInput.js +38 -0
  585. package/vendor/ink/build/index.d.ts +34 -0
  586. package/vendor/ink/build/index.js +20 -0
  587. package/vendor/ink/build/index.js.map +1 -0
  588. package/vendor/ink/build/ink.d.ts +90 -0
  589. package/vendor/ink/build/ink.js +654 -0
  590. package/vendor/ink/build/ink.js.map +1 -0
  591. package/vendor/ink/build/input-parser.d.ts +7 -0
  592. package/vendor/ink/build/input-parser.js +154 -0
  593. package/vendor/ink/build/input-parser.js.map +1 -0
  594. package/vendor/ink/build/instance.js +205 -0
  595. package/vendor/ink/build/instances.d.ts +3 -0
  596. package/vendor/ink/build/instances.js +8 -0
  597. package/vendor/ink/build/instances.js.map +1 -0
  598. package/vendor/ink/build/kitty-keyboard.d.ts +23 -0
  599. package/vendor/ink/build/kitty-keyboard.js +32 -0
  600. package/vendor/ink/build/kitty-keyboard.js.map +1 -0
  601. package/vendor/ink/build/layout.d.ts +7 -0
  602. package/vendor/ink/build/layout.js +33 -0
  603. package/vendor/ink/build/layout.js.map +1 -0
  604. package/vendor/ink/build/log-update.d.ts +19 -0
  605. package/vendor/ink/build/log-update.js +243 -0
  606. package/vendor/ink/build/log-update.js.map +1 -0
  607. package/vendor/ink/build/measure-element.d.ts +16 -0
  608. package/vendor/ink/build/measure-element.js +9 -0
  609. package/vendor/ink/build/measure-element.js.map +1 -0
  610. package/vendor/ink/build/measure-text.d.ts +6 -0
  611. package/vendor/ink/build/measure-text.js +21 -0
  612. package/vendor/ink/build/measure-text.js.map +1 -0
  613. package/vendor/ink/build/options.d.ts +52 -0
  614. package/vendor/ink/build/options.js +2 -0
  615. package/vendor/ink/build/options.js.map +1 -0
  616. package/vendor/ink/build/output.d.ts +35 -0
  617. package/vendor/ink/build/output.js +183 -0
  618. package/vendor/ink/build/output.js.map +1 -0
  619. package/vendor/ink/build/parse-keypress.d.ts +22 -0
  620. package/vendor/ink/build/parse-keypress.js +493 -0
  621. package/vendor/ink/build/parse-keypress.js.map +1 -0
  622. package/vendor/ink/build/reconciler.d.ts +4 -0
  623. package/vendor/ink/build/reconciler.js +274 -0
  624. package/vendor/ink/build/reconciler.js.map +1 -0
  625. package/vendor/ink/build/render-background.d.ts +4 -0
  626. package/vendor/ink/build/render-background.js +25 -0
  627. package/vendor/ink/build/render-background.js.map +1 -0
  628. package/vendor/ink/build/render-border.d.ts +4 -0
  629. package/vendor/ink/build/render-border.js +73 -0
  630. package/vendor/ink/build/render-border.js.map +1 -0
  631. package/vendor/ink/build/render-node-to-output.d.ts +14 -0
  632. package/vendor/ink/build/render-node-to-output.js +147 -0
  633. package/vendor/ink/build/render-node-to-output.js.map +1 -0
  634. package/vendor/ink/build/render-to-string.d.ts +38 -0
  635. package/vendor/ink/build/render-to-string.js +115 -0
  636. package/vendor/ink/build/render-to-string.js.map +1 -0
  637. package/vendor/ink/build/render.d.ts +121 -0
  638. package/vendor/ink/build/render.js +55 -0
  639. package/vendor/ink/build/render.js.map +1 -0
  640. package/vendor/ink/build/renderer.d.ts +8 -0
  641. package/vendor/ink/build/renderer.js +55 -0
  642. package/vendor/ink/build/renderer.js.map +1 -0
  643. package/vendor/ink/build/sanitize-ansi.d.ts +2 -0
  644. package/vendor/ink/build/sanitize-ansi.js +27 -0
  645. package/vendor/ink/build/sanitize-ansi.js.map +1 -0
  646. package/vendor/ink/build/screen-reader-update.d.ts +13 -0
  647. package/vendor/ink/build/screen-reader-update.js +38 -0
  648. package/vendor/ink/build/screen-reader-update.js.map +1 -0
  649. package/vendor/ink/build/squash-text-nodes.d.ts +3 -0
  650. package/vendor/ink/build/squash-text-nodes.js +36 -0
  651. package/vendor/ink/build/squash-text-nodes.js.map +1 -0
  652. package/vendor/ink/build/styles.d.ts +240 -0
  653. package/vendor/ink/build/styles.js +232 -0
  654. package/vendor/ink/build/styles.js.map +1 -0
  655. package/vendor/ink/build/utils.d.ts +2 -0
  656. package/vendor/ink/build/utils.js +4 -0
  657. package/vendor/ink/build/utils.js.map +1 -0
  658. package/vendor/ink/build/wrap-text.d.ts +3 -0
  659. package/vendor/ink/build/wrap-text.js +31 -0
  660. package/vendor/ink/build/wrap-text.js.map +1 -0
  661. package/vendor/ink/build/write-synchronized.d.ts +4 -0
  662. package/vendor/ink/build/write-synchronized.js +7 -0
  663. package/vendor/ink/build/write-synchronized.js.map +1 -0
  664. package/vendor/ink/license +10 -0
  665. package/vendor/ink/node_modules/@types/node/LICENSE +21 -0
  666. package/vendor/ink/node_modules/@types/node/README.md +15 -0
  667. package/vendor/ink/node_modules/@types/node/assert/strict.d.ts +105 -0
  668. package/vendor/ink/node_modules/@types/node/assert.d.ts +955 -0
  669. package/vendor/ink/node_modules/@types/node/async_hooks.d.ts +623 -0
  670. package/vendor/ink/node_modules/@types/node/buffer.buffer.d.ts +466 -0
  671. package/vendor/ink/node_modules/@types/node/buffer.d.ts +1810 -0
  672. package/vendor/ink/node_modules/@types/node/child_process.d.ts +1428 -0
  673. package/vendor/ink/node_modules/@types/node/cluster.d.ts +486 -0
  674. package/vendor/ink/node_modules/@types/node/compatibility/iterators.d.ts +21 -0
  675. package/vendor/ink/node_modules/@types/node/console.d.ts +151 -0
  676. package/vendor/ink/node_modules/@types/node/constants.d.ts +20 -0
  677. package/vendor/ink/node_modules/@types/node/crypto.d.ts +4065 -0
  678. package/vendor/ink/node_modules/@types/node/dgram.d.ts +564 -0
  679. package/vendor/ink/node_modules/@types/node/diagnostics_channel.d.ts +576 -0
  680. package/vendor/ink/node_modules/@types/node/dns/promises.d.ts +503 -0
  681. package/vendor/ink/node_modules/@types/node/dns.d.ts +922 -0
  682. package/vendor/ink/node_modules/@types/node/domain.d.ts +166 -0
  683. package/vendor/ink/node_modules/@types/node/events.d.ts +1054 -0
  684. package/vendor/ink/node_modules/@types/node/fs/promises.d.ts +1329 -0
  685. package/vendor/ink/node_modules/@types/node/fs.d.ts +4676 -0
  686. package/vendor/ink/node_modules/@types/node/globals.d.ts +150 -0
  687. package/vendor/ink/node_modules/@types/node/globals.typedarray.d.ts +101 -0
  688. package/vendor/ink/node_modules/@types/node/http.d.ts +2167 -0
  689. package/vendor/ink/node_modules/@types/node/http2.d.ts +2480 -0
  690. package/vendor/ink/node_modules/@types/node/https.d.ts +405 -0
  691. package/vendor/ink/node_modules/@types/node/index.d.ts +115 -0
  692. package/vendor/ink/node_modules/@types/node/inspector/promises.d.ts +41 -0
  693. package/vendor/ink/node_modules/@types/node/inspector.d.ts +224 -0
  694. package/vendor/ink/node_modules/@types/node/inspector.generated.d.ts +4226 -0
  695. package/vendor/ink/node_modules/@types/node/module.d.ts +819 -0
  696. package/vendor/ink/node_modules/@types/node/net.d.ts +933 -0
  697. package/vendor/ink/node_modules/@types/node/os.d.ts +507 -0
  698. package/vendor/ink/node_modules/@types/node/package.json +155 -0
  699. package/vendor/ink/node_modules/@types/node/path/posix.d.ts +8 -0
  700. package/vendor/ink/node_modules/@types/node/path/win32.d.ts +8 -0
  701. package/vendor/ink/node_modules/@types/node/path.d.ts +187 -0
  702. package/vendor/ink/node_modules/@types/node/perf_hooks.d.ts +643 -0
  703. package/vendor/ink/node_modules/@types/node/process.d.ts +2156 -0
  704. package/vendor/ink/node_modules/@types/node/punycode.d.ts +117 -0
  705. package/vendor/ink/node_modules/@types/node/querystring.d.ts +152 -0
  706. package/vendor/ink/node_modules/@types/node/quic.d.ts +910 -0
  707. package/vendor/ink/node_modules/@types/node/readline/promises.d.ts +161 -0
  708. package/vendor/ink/node_modules/@types/node/readline.d.ts +541 -0
  709. package/vendor/ink/node_modules/@types/node/repl.d.ts +415 -0
  710. package/vendor/ink/node_modules/@types/node/sea.d.ts +162 -0
  711. package/vendor/ink/node_modules/@types/node/sqlite.d.ts +955 -0
  712. package/vendor/ink/node_modules/@types/node/stream/consumers.d.ts +38 -0
  713. package/vendor/ink/node_modules/@types/node/stream/promises.d.ts +211 -0
  714. package/vendor/ink/node_modules/@types/node/stream/web.d.ts +296 -0
  715. package/vendor/ink/node_modules/@types/node/stream.d.ts +1760 -0
  716. package/vendor/ink/node_modules/@types/node/string_decoder.d.ts +67 -0
  717. package/vendor/ink/node_modules/@types/node/test/reporters.d.ts +96 -0
  718. package/vendor/ink/node_modules/@types/node/test.d.ts +2240 -0
  719. package/vendor/ink/node_modules/@types/node/timers/promises.d.ts +108 -0
  720. package/vendor/ink/node_modules/@types/node/timers.d.ts +159 -0
  721. package/vendor/ink/node_modules/@types/node/tls.d.ts +1198 -0
  722. package/vendor/ink/node_modules/@types/node/trace_events.d.ts +197 -0
  723. package/vendor/ink/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +462 -0
  724. package/vendor/ink/node_modules/@types/node/ts5.6/compatibility/float16array.d.ts +71 -0
  725. package/vendor/ink/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +36 -0
  726. package/vendor/ink/node_modules/@types/node/ts5.6/index.d.ts +117 -0
  727. package/vendor/ink/node_modules/@types/node/ts5.7/compatibility/float16array.d.ts +72 -0
  728. package/vendor/ink/node_modules/@types/node/ts5.7/index.d.ts +117 -0
  729. package/vendor/ink/node_modules/@types/node/tty.d.ts +250 -0
  730. package/vendor/ink/node_modules/@types/node/url.d.ts +519 -0
  731. package/vendor/ink/node_modules/@types/node/util/types.d.ts +558 -0
  732. package/vendor/ink/node_modules/@types/node/util.d.ts +1662 -0
  733. package/vendor/ink/node_modules/@types/node/v8.d.ts +983 -0
  734. package/vendor/ink/node_modules/@types/node/vm.d.ts +1208 -0
  735. package/vendor/ink/node_modules/@types/node/wasi.d.ts +202 -0
  736. package/vendor/ink/node_modules/@types/node/web-globals/abortcontroller.d.ts +59 -0
  737. package/vendor/ink/node_modules/@types/node/web-globals/blob.d.ts +23 -0
  738. package/vendor/ink/node_modules/@types/node/web-globals/console.d.ts +9 -0
  739. package/vendor/ink/node_modules/@types/node/web-globals/crypto.d.ts +39 -0
  740. package/vendor/ink/node_modules/@types/node/web-globals/domexception.d.ts +68 -0
  741. package/vendor/ink/node_modules/@types/node/web-globals/encoding.d.ts +11 -0
  742. package/vendor/ink/node_modules/@types/node/web-globals/events.d.ts +106 -0
  743. package/vendor/ink/node_modules/@types/node/web-globals/fetch.d.ts +69 -0
  744. package/vendor/ink/node_modules/@types/node/web-globals/importmeta.d.ts +13 -0
  745. package/vendor/ink/node_modules/@types/node/web-globals/messaging.d.ts +23 -0
  746. package/vendor/ink/node_modules/@types/node/web-globals/navigator.d.ts +25 -0
  747. package/vendor/ink/node_modules/@types/node/web-globals/performance.d.ts +45 -0
  748. package/vendor/ink/node_modules/@types/node/web-globals/storage.d.ts +24 -0
  749. package/vendor/ink/node_modules/@types/node/web-globals/streams.d.ts +115 -0
  750. package/vendor/ink/node_modules/@types/node/web-globals/timers.d.ts +44 -0
  751. package/vendor/ink/node_modules/@types/node/web-globals/url.d.ts +24 -0
  752. package/vendor/ink/node_modules/@types/node/worker_threads.d.ts +717 -0
  753. package/vendor/ink/node_modules/@types/node/zlib.d.ts +618 -0
  754. package/vendor/ink/node_modules/node-pty/LICENSE +69 -0
  755. package/vendor/ink/node_modules/node-pty/README.md +164 -0
  756. package/vendor/ink/node_modules/node-pty/binding.gyp +150 -0
  757. package/vendor/ink/node_modules/node-pty/lib/conpty_console_list_agent.js +25 -0
  758. package/vendor/ink/node_modules/node-pty/lib/eventEmitter2.js +47 -0
  759. package/vendor/ink/node_modules/node-pty/lib/index.js +52 -0
  760. package/vendor/ink/node_modules/node-pty/lib/interfaces.js +7 -0
  761. package/vendor/ink/node_modules/node-pty/lib/shared/conout.js +11 -0
  762. package/vendor/ink/node_modules/node-pty/lib/terminal.js +190 -0
  763. package/vendor/ink/node_modules/node-pty/lib/types.js +7 -0
  764. package/vendor/ink/node_modules/node-pty/lib/unixTerminal.js +349 -0
  765. package/vendor/ink/node_modules/node-pty/lib/utils.js +39 -0
  766. package/vendor/ink/node_modules/node-pty/lib/windowsConoutConnection.js +125 -0
  767. package/vendor/ink/node_modules/node-pty/lib/windowsPtyAgent.js +287 -0
  768. package/vendor/ink/node_modules/node-pty/lib/windowsTerminal.js +201 -0
  769. package/vendor/ink/node_modules/node-pty/lib/worker/conoutSocketWorker.js +22 -0
  770. package/vendor/ink/node_modules/node-pty/package.json +65 -0
  771. package/vendor/ink/node_modules/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
  772. package/vendor/ink/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
  773. package/vendor/ink/node_modules/node-pty/prebuilds/darwin-x64/pty.node +0 -0
  774. package/vendor/ink/node_modules/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
  775. package/vendor/ink/node_modules/node-pty/prebuilds/linux-arm64/pty.node +0 -0
  776. package/vendor/ink/node_modules/node-pty/prebuilds/linux-x64/pty.node +0 -0
  777. package/vendor/ink/node_modules/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
  778. package/vendor/ink/node_modules/node-pty/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
  779. package/vendor/ink/node_modules/node-pty/prebuilds/win32-arm64/conpty.node +0 -0
  780. package/vendor/ink/node_modules/node-pty/prebuilds/win32-arm64/conpty.pdb +0 -0
  781. package/vendor/ink/node_modules/node-pty/prebuilds/win32-arm64/conpty_console_list.node +0 -0
  782. package/vendor/ink/node_modules/node-pty/prebuilds/win32-arm64/conpty_console_list.pdb +0 -0
  783. package/vendor/ink/node_modules/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
  784. package/vendor/ink/node_modules/node-pty/prebuilds/win32-x64/conpty/conpty.dll +0 -0
  785. package/vendor/ink/node_modules/node-pty/prebuilds/win32-x64/conpty.node +0 -0
  786. package/vendor/ink/node_modules/node-pty/prebuilds/win32-x64/conpty.pdb +0 -0
  787. package/vendor/ink/node_modules/node-pty/prebuilds/win32-x64/conpty_console_list.node +0 -0
  788. package/vendor/ink/node_modules/node-pty/prebuilds/win32-x64/conpty_console_list.pdb +0 -0
  789. package/vendor/ink/node_modules/node-pty/scripts/post-install.js +76 -0
  790. package/vendor/ink/node_modules/node-pty/scripts/prebuild.js +34 -0
  791. package/vendor/ink/node_modules/node-pty/src/unix/pty.cc +875 -0
  792. package/vendor/ink/node_modules/node-pty/src/unix/spawn-helper.cc +23 -0
  793. package/vendor/ink/node_modules/node-pty/src/win/conpty.cc +582 -0
  794. package/vendor/ink/node_modules/node-pty/src/win/conpty.h +41 -0
  795. package/vendor/ink/node_modules/node-pty/src/win/conpty_console_list.cc +44 -0
  796. package/vendor/ink/node_modules/node-pty/src/win/path_util.cc +95 -0
  797. package/vendor/ink/node_modules/node-pty/src/win/path_util.h +26 -0
  798. package/vendor/ink/node_modules/node-pty/third_party/conpty/1.23.251008001/win10-arm64/OpenConsole.exe +0 -0
  799. package/vendor/ink/node_modules/node-pty/third_party/conpty/1.23.251008001/win10-arm64/conpty.dll +0 -0
  800. package/vendor/ink/node_modules/node-pty/third_party/conpty/1.23.251008001/win10-x64/OpenConsole.exe +0 -0
  801. package/vendor/ink/node_modules/node-pty/third_party/conpty/1.23.251008001/win10-x64/conpty.dll +0 -0
  802. package/vendor/ink/node_modules/node-pty/typings/node-pty.d.ts +215 -0
  803. package/vendor/ink/node_modules/undici-types/LICENSE +21 -0
  804. package/vendor/ink/node_modules/undici-types/README.md +6 -0
  805. package/vendor/ink/node_modules/undici-types/agent.d.ts +32 -0
  806. package/vendor/ink/node_modules/undici-types/api.d.ts +43 -0
  807. package/vendor/ink/node_modules/undici-types/balanced-pool.d.ts +30 -0
  808. package/vendor/ink/node_modules/undici-types/cache-interceptor.d.ts +173 -0
  809. package/vendor/ink/node_modules/undici-types/cache.d.ts +36 -0
  810. package/vendor/ink/node_modules/undici-types/client-stats.d.ts +15 -0
  811. package/vendor/ink/node_modules/undici-types/client.d.ts +108 -0
  812. package/vendor/ink/node_modules/undici-types/connector.d.ts +34 -0
  813. package/vendor/ink/node_modules/undici-types/content-type.d.ts +21 -0
  814. package/vendor/ink/node_modules/undici-types/cookies.d.ts +30 -0
  815. package/vendor/ink/node_modules/undici-types/diagnostics-channel.d.ts +74 -0
  816. package/vendor/ink/node_modules/undici-types/dispatcher.d.ts +276 -0
  817. package/vendor/ink/node_modules/undici-types/env-http-proxy-agent.d.ts +22 -0
  818. package/vendor/ink/node_modules/undici-types/errors.d.ts +161 -0
  819. package/vendor/ink/node_modules/undici-types/eventsource.d.ts +66 -0
  820. package/vendor/ink/node_modules/undici-types/fetch.d.ts +211 -0
  821. package/vendor/ink/node_modules/undici-types/formdata.d.ts +108 -0
  822. package/vendor/ink/node_modules/undici-types/global-dispatcher.d.ts +9 -0
  823. package/vendor/ink/node_modules/undici-types/global-origin.d.ts +7 -0
  824. package/vendor/ink/node_modules/undici-types/h2c-client.d.ts +73 -0
  825. package/vendor/ink/node_modules/undici-types/handlers.d.ts +15 -0
  826. package/vendor/ink/node_modules/undici-types/header.d.ts +160 -0
  827. package/vendor/ink/node_modules/undici-types/index.d.ts +88 -0
  828. package/vendor/ink/node_modules/undici-types/interceptors.d.ts +73 -0
  829. package/vendor/ink/node_modules/undici-types/mock-agent.d.ts +68 -0
  830. package/vendor/ink/node_modules/undici-types/mock-call-history.d.ts +111 -0
  831. package/vendor/ink/node_modules/undici-types/mock-client.d.ts +27 -0
  832. package/vendor/ink/node_modules/undici-types/mock-errors.d.ts +12 -0
  833. package/vendor/ink/node_modules/undici-types/mock-interceptor.d.ts +94 -0
  834. package/vendor/ink/node_modules/undici-types/mock-pool.d.ts +27 -0
  835. package/vendor/ink/node_modules/undici-types/package.json +55 -0
  836. package/vendor/ink/node_modules/undici-types/patch.d.ts +29 -0
  837. package/vendor/ink/node_modules/undici-types/pool-stats.d.ts +19 -0
  838. package/vendor/ink/node_modules/undici-types/pool.d.ts +41 -0
  839. package/vendor/ink/node_modules/undici-types/proxy-agent.d.ts +29 -0
  840. package/vendor/ink/node_modules/undici-types/readable.d.ts +68 -0
  841. package/vendor/ink/node_modules/undici-types/retry-agent.d.ts +8 -0
  842. package/vendor/ink/node_modules/undici-types/retry-handler.d.ts +125 -0
  843. package/vendor/ink/node_modules/undici-types/round-robin-pool.d.ts +41 -0
  844. package/vendor/ink/node_modules/undici-types/snapshot-agent.d.ts +109 -0
  845. package/vendor/ink/node_modules/undici-types/util.d.ts +18 -0
  846. package/vendor/ink/node_modules/undici-types/utility.d.ts +7 -0
  847. package/vendor/ink/node_modules/undici-types/webidl.d.ts +341 -0
  848. package/vendor/ink/node_modules/undici-types/websocket.d.ts +186 -0
  849. package/vendor/ink/package.json +201 -0
  850. package/vendor/ink/readme.md +2636 -0
  851. package/bin/swag-agent.js +0 -9
  852. package/dist/server/lib/pg-rate-limiter.d.ts +0 -21
  853. package/dist/server/lib/pg-rate-limiter.js +0 -86
@@ -1,4 +1,3 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
1
  /**
3
2
  * ChatInput — Custom input with bracketed paste + image attachments
4
3
  *
@@ -15,6 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
14
  * - Slash command menu preserved
16
15
  * - Multi-line input with ⎸ continuation markers
17
16
  */
17
+
18
18
  import { useState, useEffect, useRef } from "react";
19
19
  import { Box, Text, useStdin } from "ink";
20
20
  import { readFileSync, existsSync, statSync } from "fs";
@@ -22,832 +22,1150 @@ import { basename, extname } from "path";
22
22
  import { colors } from "../shared/Theme.js";
23
23
  import { loadKeybindings, bindingToControlChar } from "../services/keybinding-manager.js";
24
24
  import { SlashMenu, SLASH_COMMANDS as _SLASH_COMMANDS, getFilteredSelectableCount, getCommandAtIndex, getCategoryCount, getCategoryCommandCount, getCategoryCommand, getCategoryLabel, getCategoryColor } from "./SlashMenu.js";
25
+
26
+ // ── Types ──
27
+
28
+ // Re-export for consumers (e.g. useSlashCommands)
29
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
25
30
  export const SLASH_COMMANDS = _SLASH_COMMANDS;
31
+
26
32
  // ── Constants ──
33
+
27
34
  const IMAGE_EXTENSIONS = {
28
- ".png": "image/png",
29
- ".jpg": "image/jpeg",
30
- ".jpeg": "image/jpeg",
31
- ".gif": "image/gif",
32
- ".webp": "image/webp",
35
+ ".png": "image/png",
36
+ ".jpg": "image/jpeg",
37
+ ".jpeg": "image/jpeg",
38
+ ".gif": "image/gif",
39
+ ".webp": "image/webp"
33
40
  };
34
41
  const MAX_IMAGE_SIZE = 20 * 1024 * 1024; // 20MB
35
42
  const MAX_DISPLAY_LINES = 8;
43
+
36
44
  // ── Helpers ──
45
+
37
46
  const CONTEXT_TYPES = ["product", "customer", "order", "inventory", "media", "document", "location"];
38
47
  const CONTEXT_COLORS = {
39
- product: "blue", customer: "#a855f7", order: "green",
40
- inventory: "#f97316", media: "#ec4899", document: "cyan", location: "yellow",
48
+ product: "blue",
49
+ customer: "#a855f7",
50
+ order: "green",
51
+ inventory: "#f97316",
52
+ media: "#ec4899",
53
+ document: "cyan",
54
+ location: "yellow"
41
55
  };
56
+
42
57
  /** Detect if pasted text is a valid WhaleContextItem JSON object */
43
58
  function parseWhaleContext(text) {
44
- const trimmed = text.trim();
45
- if (!trimmed.startsWith("{") || !trimmed.endsWith("}"))
46
- return null;
47
- try {
48
- const obj = JSON.parse(trimmed);
49
- if (obj && typeof obj.id === "string" && typeof obj.type === "string" &&
50
- CONTEXT_TYPES.includes(obj.type) && typeof obj.title === "string" &&
51
- typeof obj.sourceApp === "string") {
52
- return obj;
53
- }
59
+ const trimmed = text.trim();
60
+ if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return null;
61
+ try {
62
+ const obj = JSON.parse(trimmed);
63
+ if (obj && typeof obj.id === "string" && typeof obj.type === "string" && CONTEXT_TYPES.includes(obj.type) && typeof obj.title === "string" && typeof obj.sourceApp === "string") {
64
+ return obj;
54
65
  }
55
- catch { /* not JSON */ }
56
- return null;
66
+ } catch {/* not JSON */}
67
+ return null;
57
68
  }
69
+
58
70
  /** Format a context attachment into an XML context block for AI prompt */
59
71
  function contextToBlock(ctx) {
60
- const lines = [];
61
- lines.push(`<context type="${ctx.type}" source="${ctx.sourceApp}">`);
62
- lines.push(` ${ctx.title}`);
63
- if (ctx.subtitle)
64
- lines.push(` ${ctx.subtitle}`);
65
- if (ctx.data) {
66
- for (const [key, value] of Object.entries(ctx.data).sort(([a], [b]) => a.localeCompare(b))) {
67
- lines.push(` ${key}: ${value}`);
68
- }
72
+ const lines = [];
73
+ lines.push(`<context type="${ctx.type}" source="${ctx.sourceApp}">`);
74
+ lines.push(` ${ctx.title}`);
75
+ if (ctx.subtitle) lines.push(` ${ctx.subtitle}`);
76
+ if (ctx.data) {
77
+ for (const [key, value] of Object.entries(ctx.data).sort(([a], [b]) => a.localeCompare(b))) {
78
+ lines.push(` ${key}: ${value}`);
69
79
  }
70
- lines.push("</context>");
71
- return lines.join("\n");
80
+ }
81
+ lines.push("</context>");
82
+ return lines.join("\n");
72
83
  }
73
84
  function isImagePath(text) {
74
- const p = normalizePath(text);
75
- if (!p || p.includes("\n"))
76
- return false;
77
- const ext = extname(p).toLowerCase();
78
- return ext in IMAGE_EXTENSIONS && existsSync(p);
85
+ const p = normalizePath(text);
86
+ if (!p || p.includes("\n")) return false;
87
+ const ext = extname(p).toLowerCase();
88
+ return ext in IMAGE_EXTENSIONS && existsSync(p);
79
89
  }
90
+
80
91
  /** Normalize a terminal-pasted path (handles escapes, file:// URLs, quotes) */
81
92
  function normalizePath(raw) {
82
- let p = raw.trim();
83
- // Strip file:// URL prefix + percent-decode
84
- if (p.startsWith("file://")) {
85
- p = decodeURIComponent(p.replace(/^file:\/\//, ""));
86
- }
87
- // Strip surrounding quotes
88
- p = p.replace(/^['"]|['"]$/g, "");
89
- // Unescape backslash-escaped characters (spaces, parens, etc.)
90
- p = p.replace(/\\(.)/g, "$1");
91
- return p;
93
+ let p = raw.trim();
94
+ // Strip file:// URL prefix + percent-decode
95
+ if (p.startsWith("file://")) {
96
+ p = decodeURIComponent(p.replace(/^file:\/\//, ""));
97
+ }
98
+ // Strip surrounding quotes
99
+ p = p.replace(/^['"]|['"]$/g, "");
100
+ // Unescape backslash-escaped characters (spaces, parens, etc.)
101
+ p = p.replace(/\\(.)/g, "$1");
102
+ return p;
92
103
  }
93
104
  function isFilePath(text) {
94
- const p = normalizePath(text);
95
- if (!p || p.includes("\n"))
96
- return false;
97
- // Skip image paths — handled separately
98
- const ext = extname(p).toLowerCase();
99
- if (ext in IMAGE_EXTENSIONS)
100
- return false;
101
- try {
102
- return existsSync(p) && statSync(p).isFile();
103
- }
104
- catch {
105
- return false;
106
- }
105
+ const p = normalizePath(text);
106
+ if (!p || p.includes("\n")) return false;
107
+ // Skip image paths — handled separately
108
+ const ext = extname(p).toLowerCase();
109
+ if (ext in IMAGE_EXTENSIONS) return false;
110
+ try {
111
+ return existsSync(p) && statSync(p).isFile();
112
+ } catch {
113
+ return false;
114
+ }
107
115
  }
108
116
  function loadImage(filePath) {
109
- try {
110
- const p = normalizePath(filePath);
111
- const ext = extname(p).toLowerCase();
112
- const mediaType = IMAGE_EXTENSIONS[ext];
113
- if (!mediaType)
114
- return null;
115
- const stat = statSync(p);
116
- if (stat.size > MAX_IMAGE_SIZE)
117
- return null;
118
- const data = readFileSync(p);
119
- return { path: p, name: basename(p), base64: data.toString("base64"), mediaType };
120
- }
121
- catch {
122
- return null;
123
- }
117
+ try {
118
+ const p = normalizePath(filePath);
119
+ const ext = extname(p).toLowerCase();
120
+ const mediaType = IMAGE_EXTENSIONS[ext];
121
+ if (!mediaType) return null;
122
+ const stat = statSync(p);
123
+ if (stat.size > MAX_IMAGE_SIZE) return null;
124
+ const data = readFileSync(p);
125
+ return {
126
+ path: p,
127
+ name: basename(p),
128
+ base64: data.toString("base64"),
129
+ mediaType
130
+ };
131
+ } catch {
132
+ return null;
133
+ }
124
134
  }
125
135
  function getCursorLineCol(text, cursor) {
126
- let pos = 0;
127
- const lines = text.split("\n");
128
- for (let i = 0; i < lines.length; i++) {
129
- if (cursor <= pos + lines[i].length) {
130
- return { line: i, col: cursor - pos };
131
- }
132
- pos += lines[i].length + 1;
136
+ let pos = 0;
137
+ const lines = text.split("\n");
138
+ for (let i = 0; i < lines.length; i++) {
139
+ if (cursor <= pos + lines[i].length) {
140
+ return {
141
+ line: i,
142
+ col: cursor - pos
143
+ };
133
144
  }
134
- return { line: lines.length - 1, col: lines[lines.length - 1].length };
145
+ pos += lines[i].length + 1;
146
+ }
147
+ return {
148
+ line: lines.length - 1,
149
+ col: lines[lines.length - 1].length
150
+ };
135
151
  }
152
+
153
+ // ── Props ──
154
+
136
155
  // ── Component ──
137
- export function ChatInput({ onSubmit, onCommand, disabled, agentName, onInterrupt }) {
138
- const { stdin } = useStdin();
139
- // Input state — ref for synchronous handler access, state for render
140
- const inputRef = useRef({ value: "", cursor: 0 });
141
- const [displayValue, setDisplayValue] = useState("");
142
- const [displayCursor, setDisplayCursor] = useState(0);
143
- // Stable refs for callbacks — prevents stale closures in stdin handler
144
- const onSubmitRef = useRef(onSubmit);
145
- const onCommandRef = useRef(onCommand);
146
- const onInterruptRef = useRef(onInterrupt);
147
- const disabledRef = useRef(disabled);
148
- useEffect(() => { onSubmitRef.current = onSubmit; }, [onSubmit]);
149
- useEffect(() => { onCommandRef.current = onCommand; }, [onCommand]);
150
- useEffect(() => { onInterruptRef.current = onInterrupt; }, [onInterrupt]);
151
- useEffect(() => { disabledRef.current = disabled; }, [disabled]);
152
- // Queued message — typed during streaming, auto-submitted when streaming ends
153
- const [queued, setQueued] = useState(false);
154
- const queuedRef = useRef(false);
155
- // Auto-submit queued input when streaming ends (disabled goes from true → false)
156
- const prevDisabledRef = useRef(disabled);
157
- useEffect(() => {
158
- if (prevDisabledRef.current && !disabled && queuedRef.current) {
159
- // Streaming just ended and we have queued input — submit it
160
- queuedRef.current = false;
161
- setQueued(false);
162
- handleSubmit();
163
- }
164
- prevDisabledRef.current = disabled;
165
- }, [disabled]);
166
- // Mode & attachments
167
- const [menuMode, setMenuMode] = useState(false);
168
- const menuModeRef = useRef(false);
169
- const [menuFilter, setMenuFilter] = useState("");
170
- const menuFilterRef = useRef("");
171
- const [menuIndex, setMenuIndex] = useState(0);
172
- const menuIndexRef = useRef(0);
173
- const [images, setImages] = useState([]);
174
- const imagesRef = useRef([]);
175
- const [files, setFiles] = useState([]);
176
- const filesRef = useRef([]);
177
- const [contextItems, setContextItems] = useState([]);
178
- const contextItemsRef = useRef([]);
179
- // Two-level menu: null = Level 1 (categories), number = Level 2 (drilled into that category)
180
- const [selectedCategory, setSelectedCategory] = useState(null);
181
- const selectedCategoryRef = useRef(null);
182
- function setSelectedCategorySync(val) {
183
- selectedCategoryRef.current = val;
184
- setSelectedCategory(val);
156
+
157
+ export function ChatInput({
158
+ onSubmit,
159
+ onCommand,
160
+ disabled,
161
+ agentName,
162
+ onInterrupt
163
+ }) {
164
+ const {
165
+ stdin
166
+ } = useStdin();
167
+
168
+ // Input state ref for synchronous handler access, state for render
169
+ const inputRef = useRef({
170
+ value: "",
171
+ cursor: 0
172
+ });
173
+ const [displayValue, setDisplayValue] = useState("");
174
+ const [displayCursor, setDisplayCursor] = useState(0);
175
+
176
+ // Stable refs for callbacks — prevents stale closures in stdin handler
177
+ const onSubmitRef = useRef(onSubmit);
178
+ const onCommandRef = useRef(onCommand);
179
+ const onInterruptRef = useRef(onInterrupt);
180
+ const disabledRef = useRef(disabled);
181
+ useEffect(() => {
182
+ onSubmitRef.current = onSubmit;
183
+ }, [onSubmit]);
184
+ useEffect(() => {
185
+ onCommandRef.current = onCommand;
186
+ }, [onCommand]);
187
+ useEffect(() => {
188
+ onInterruptRef.current = onInterrupt;
189
+ }, [onInterrupt]);
190
+ useEffect(() => {
191
+ disabledRef.current = disabled;
192
+ }, [disabled]);
193
+
194
+ // Queued message — typed during streaming, auto-submitted when streaming ends
195
+ const [queued, setQueued] = useState(false);
196
+ const queuedRef = useRef(false);
197
+
198
+ // Auto-submit queued input when streaming ends (disabled goes from true false)
199
+ const prevDisabledRef = useRef(disabled);
200
+ useEffect(() => {
201
+ if (prevDisabledRef.current && !disabled && queuedRef.current) {
202
+ // Streaming just ended and we have queued input — submit it
203
+ queuedRef.current = false;
204
+ setQueued(false);
205
+ handleSubmit();
185
206
  }
186
- // Input history (up/down arrow recall)
187
- const historyRef = useRef([]);
188
- const historyIndexRef = useRef(-1);
189
- const savedInputRef = useRef("");
190
- const MAX_HISTORY = 50;
191
- // Paste tracking
192
- const isPasting = useRef(false);
193
- const pasteBuffer = useRef("");
194
- // Multi-line paste — stored separately, displayed as chip
195
- const [pastedText, setPastedText] = useState(null);
196
- const pastedTextRef = useRef(null);
197
- // Sync refs
198
- useEffect(() => { imagesRef.current = images; }, [images]);
199
- useEffect(() => { filesRef.current = files; }, [files]);
200
- useEffect(() => { contextItemsRef.current = contextItems; }, [contextItems]);
201
- useEffect(() => { pastedTextRef.current = pastedText; }, [pastedText]);
202
- // ── Sync helpers: update ref THEN state (fixes race conditions) ──
203
- function setMenuModeSync(val) {
204
- menuModeRef.current = val;
205
- setMenuMode(val);
207
+ prevDisabledRef.current = disabled;
208
+ }, [disabled]);
209
+
210
+ // Mode & attachments
211
+ const [menuMode, setMenuMode] = useState(false);
212
+ const menuModeRef = useRef(false);
213
+ const [menuFilter, setMenuFilter] = useState("");
214
+ const menuFilterRef = useRef("");
215
+ const [menuIndex, setMenuIndex] = useState(0);
216
+ const menuIndexRef = useRef(0);
217
+ const [images, setImages] = useState([]);
218
+ const imagesRef = useRef([]);
219
+ const [files, setFiles] = useState([]);
220
+ const filesRef = useRef([]);
221
+ const [contextItems, setContextItems] = useState([]);
222
+ const contextItemsRef = useRef([]);
223
+
224
+ // Two-level menu: null = Level 1 (categories), number = Level 2 (drilled into that category)
225
+ const [selectedCategory, setSelectedCategory] = useState(null);
226
+ const selectedCategoryRef = useRef(null);
227
+ function setSelectedCategorySync(val) {
228
+ selectedCategoryRef.current = val;
229
+ setSelectedCategory(val);
230
+ }
231
+
232
+ // Input history (up/down arrow recall)
233
+ const historyRef = useRef([]);
234
+ const historyIndexRef = useRef(-1);
235
+ const savedInputRef = useRef("");
236
+ const MAX_HISTORY = 50;
237
+
238
+ // Paste tracking
239
+ const isPasting = useRef(false);
240
+ const pasteBuffer = useRef("");
241
+
242
+ // Multi-line paste — stored separately, displayed as chip
243
+ const [pastedText, setPastedText] = useState(null);
244
+ const pastedTextRef = useRef(null);
245
+
246
+ // Sync refs
247
+ useEffect(() => {
248
+ imagesRef.current = images;
249
+ }, [images]);
250
+ useEffect(() => {
251
+ filesRef.current = files;
252
+ }, [files]);
253
+ useEffect(() => {
254
+ contextItemsRef.current = contextItems;
255
+ }, [contextItems]);
256
+ useEffect(() => {
257
+ pastedTextRef.current = pastedText;
258
+ }, [pastedText]);
259
+ // ── Sync helpers: update ref THEN state (fixes race conditions) ──
260
+ function setMenuModeSync(val_0) {
261
+ menuModeRef.current = val_0;
262
+ setMenuMode(val_0);
263
+ }
264
+ function setMenuFilterSync(val_1) {
265
+ menuFilterRef.current = val_1;
266
+ setMenuFilter(val_1);
267
+ }
268
+ function setMenuIndexSync(val_2) {
269
+ menuIndexRef.current = val_2;
270
+ setMenuIndex(val_2);
271
+ }
272
+
273
+ // ── Enable bracketed paste mode ──
274
+ useEffect(() => {
275
+ process.stdout.write("\x1b[?2004h");
276
+ return () => {
277
+ process.stdout.write("\x1b[?2004l");
278
+ };
279
+ }, []);
280
+
281
+ // ── Update helper ──
282
+ function update(value, cursor) {
283
+ inputRef.current = {
284
+ value,
285
+ cursor
286
+ };
287
+ setDisplayValue(value);
288
+ setDisplayCursor(cursor);
289
+ }
290
+
291
+ // ── Process paste content ──
292
+ function processPaste(text) {
293
+ const clean = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
294
+
295
+ // Check for WhaleContext JSON → attach as context chip
296
+ const ctx = parseWhaleContext(clean);
297
+ if (ctx) {
298
+ // Avoid duplicates
299
+ if (!contextItemsRef.current.some(c => c.id === ctx.id)) {
300
+ setContextItems(prev => [...prev, ctx]);
301
+ }
302
+ return;
206
303
  }
207
- function setMenuFilterSync(val) {
208
- menuFilterRef.current = val;
209
- setMenuFilter(val);
304
+
305
+ // Check if ALL non-empty lines are image paths → attach them
306
+ const lines = clean.split("\n").map(l => l.trim()).filter(Boolean);
307
+ if (lines.length > 0 && lines.every(l_0 => isImagePath(l_0))) {
308
+ const newImages = [];
309
+ for (const line of lines) {
310
+ const img = loadImage(line);
311
+ if (img) newImages.push(img);
312
+ }
313
+ if (newImages.length > 0) {
314
+ setImages(prev_0 => [...prev_0, ...newImages]);
315
+ return;
316
+ }
210
317
  }
211
- function setMenuIndexSync(val) {
212
- menuIndexRef.current = val;
213
- setMenuIndex(val);
318
+
319
+ // Check for non-image file paths → attach as file chips
320
+ if (lines.length > 0 && lines.length <= 4 && lines.every(l_2 => isFilePath(l_2))) {
321
+ const newFiles = lines.map(l_1 => {
322
+ const p = normalizePath(l_1);
323
+ return {
324
+ path: p,
325
+ name: basename(p)
326
+ };
327
+ });
328
+ setFiles(prev_1 => [...prev_1, ...newFiles]);
329
+ return;
214
330
  }
215
- // ── Enable bracketed paste mode ──
216
- useEffect(() => {
217
- process.stdout.write("\x1b[?2004h");
218
- return () => { process.stdout.write("\x1b[?2004l"); };
219
- }, []);
220
- // ── Update helper ──
221
- function update(value, cursor) {
222
- inputRef.current = { value, cursor };
223
- setDisplayValue(value);
224
- setDisplayCursor(cursor);
331
+
332
+ // Slash command pasted directly (must look like /word, not an absolute path)
333
+ if (clean.startsWith("/") && !clean.includes("\n") && !clean.includes("/", 1) && inputRef.current.value === "") {
334
+ onCommandRef.current(clean.trim());
335
+ update("", 0);
336
+ return;
225
337
  }
226
- // ── Process paste content ──
227
- function processPaste(text) {
228
- const clean = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
229
- // Check for WhaleContext JSON → attach as context chip
230
- const ctx = parseWhaleContext(clean);
231
- if (ctx) {
232
- // Avoid duplicates
233
- if (!contextItemsRef.current.some(c => c.id === ctx.id)) {
234
- setContextItems(prev => [...prev, ctx]);
235
- }
236
- return;
338
+
339
+ // Multi-line paste → store as chip, don't spam input
340
+ const pasteLines = clean.split("\n");
341
+ if (pasteLines.length >= 4) {
342
+ setPastedText(clean);
343
+ return;
344
+ }
345
+
346
+ // Short paste — insert inline as before
347
+ const {
348
+ value: val_3,
349
+ cursor: cur
350
+ } = inputRef.current;
351
+ const newValue = val_3.slice(0, cur) + clean + val_3.slice(cur);
352
+ update(newValue, cur + clean.length);
353
+ }
354
+
355
+ // ── Handle submit ──
356
+ function handleSubmit() {
357
+ const typed = inputRef.current.value.trim();
358
+ const paste = pastedTextRef.current;
359
+ const imgs = imagesRef.current;
360
+ const fls = filesRef.current;
361
+ const ctxs = contextItemsRef.current;
362
+ if (!typed && !paste && imgs.length === 0 && fls.length === 0 && ctxs.length === 0) return;
363
+
364
+ // Slash command — must start with / and NOT contain a second /
365
+ // (prevents file paths like /Users/whale/file.txt from being misidentified)
366
+ if (typed.startsWith("/") && !typed.includes("/", 1) && !paste && imgs.length === 0 && fls.length === 0 && ctxs.length === 0) {
367
+ update("", 0);
368
+ onCommandRef.current(typed);
369
+ return;
370
+ }
371
+
372
+ // Build context blocks prefix
373
+ const contextPrefix = ctxs.length > 0 ? ctxs.map(c_0 => contextToBlock(c_0)).join("\n\n") + "\n\n" : "";
374
+
375
+ // Build file context prefix
376
+ const filePrefix = fls.length > 0 ? fls.map(f => f.path).join("\n") + "\n\n" : "";
377
+
378
+ // Combine context + file prefix + typed text + pasted content
379
+ const body = paste ? typed ? `${typed}\n\n${paste}` : paste : typed || (ctxs.length > 0 ? "Tell me about this" : "");
380
+ const fullText = contextPrefix + filePrefix + body;
381
+
382
+ // Record in history
383
+ if (fullText.trim()) {
384
+ historyRef.current.push(fullText.trim());
385
+ if (historyRef.current.length > MAX_HISTORY) historyRef.current.shift();
386
+ }
387
+ historyIndexRef.current = -1;
388
+ onSubmitRef.current(fullText, imgs.length > 0 ? imgs : undefined);
389
+ update("", 0);
390
+ setImages([]);
391
+ setFiles([]);
392
+ setContextItems([]);
393
+ setPastedText(null);
394
+ }
395
+
396
+ // ── Raw stdin handler (handles both normal input and menu mode) ──
397
+ useEffect(() => {
398
+ if (!stdin) return;
399
+ const onData = data => {
400
+ const str = data.toString("utf-8");
401
+
402
+ // ══════════════════════════════════════════════
403
+ // DISABLED MODE — accept typing, block commands, handle Esc
404
+ // ══════════════════════════════════════════════
405
+ if (disabledRef.current) {
406
+ // Escape → interrupt streaming
407
+ if (str === "\x1b") {
408
+ onInterruptRef.current?.();
409
+ return;
237
410
  }
238
- // Check if ALL non-empty lines are image paths → attach them
239
- const lines = clean.split("\n").map(l => l.trim()).filter(Boolean);
240
- if (lines.length > 0 && lines.every(l => isImagePath(l))) {
241
- const newImages = [];
242
- for (const line of lines) {
243
- const img = loadImage(line);
244
- if (img)
245
- newImages.push(img);
411
+
412
+ // Control chars handled by ChatApp (exit, expand, thinking toggle)
413
+ const _kb = loadKeybindings();
414
+ const exitChar = bindingToControlChar(_kb.exit);
415
+ const expandChar = bindingToControlChar(_kb.toggle_expand);
416
+ const thinkingChar = bindingToControlChar(_kb.toggle_thinking);
417
+ if (str === exitChar || str === expandChar || str === thinkingChar) return;
418
+
419
+ // Arrow keys — cursor movement within buffer while disabled
420
+ if (str.startsWith("\x1b[")) {
421
+ const {
422
+ value: val_4,
423
+ cursor: cur_0
424
+ } = inputRef.current;
425
+ if (str === "\x1b[C") {
426
+ // Right
427
+ update(val_4, Math.min(cur_0 + 1, val_4.length));
428
+ } else if (str === "\x1b[D") {
429
+ // Left
430
+ update(val_4, Math.max(cur_0 - 1, 0));
431
+ } else if (str === "\x1b[A" || str === "\x1b[B") {
432
+ // Up/Down
433
+ const lines_0 = val_4.split("\n");
434
+ if (lines_0.length > 1) {
435
+ const {
436
+ line: curLine,
437
+ col: curCol
438
+ } = getCursorLineCol(val_4, cur_0);
439
+ const targetLine = str === "\x1b[A" ? Math.max(0, curLine - 1) : Math.min(lines_0.length - 1, curLine + 1);
440
+ if (targetLine !== curLine) {
441
+ const targetCol = Math.min(curCol, lines_0[targetLine].length);
442
+ let newCursor = 0;
443
+ for (let i = 0; i < targetLine; i++) newCursor += lines_0[i].length + 1;
444
+ newCursor += targetCol;
445
+ update(val_4, newCursor);
446
+ }
246
447
  }
247
- if (newImages.length > 0) {
248
- setImages(prev => [...prev, ...newImages]);
249
- return;
448
+ } else if (str === "\x1b[H" || str === "\x1b[1~") {
449
+ // Home
450
+ update(val_4, 0);
451
+ } else if (str === "\x1b[F" || str === "\x1b[4~") {
452
+ // End
453
+ update(val_4, val_4.length);
454
+ }
455
+ return;
456
+ }
457
+
458
+ // Bracketed paste while disabled — accept into buffer
459
+ if (str.includes("\x1b[200~")) {
460
+ isPasting.current = true;
461
+ let text_0 = str.replace(/\x1b\[200~/g, "").replace(/\x1b\[201~/g, "");
462
+ pasteBuffer.current += text_0;
463
+ if (str.includes("\x1b[201~")) {
464
+ isPasting.current = false;
465
+ const paste_0 = pasteBuffer.current;
466
+ pasteBuffer.current = "";
467
+ processPaste(paste_0);
468
+ }
469
+ return;
470
+ }
471
+ if (isPasting.current) {
472
+ if (str.includes("\x1b[201~")) {
473
+ isPasting.current = false;
474
+ pasteBuffer.current += str.replace(/\x1b\[201~/g, "");
475
+ const paste_1 = pasteBuffer.current;
476
+ pasteBuffer.current = "";
477
+ processPaste(paste_1);
478
+ } else {
479
+ pasteBuffer.current += str;
480
+ }
481
+ return;
482
+ }
483
+
484
+ // Enter → queue the current input for auto-submit
485
+ if (str === "\r" || str === "\n") {
486
+ const typed_0 = inputRef.current.value.trim();
487
+ if (typed_0) {
488
+ queuedRef.current = true;
489
+ setQueued(true);
490
+ }
491
+ return;
492
+ }
493
+
494
+ // Backspace
495
+ if (str === "\x7f" || str === "\b") {
496
+ const {
497
+ value: val_5,
498
+ cursor: cur_1
499
+ } = inputRef.current;
500
+ if (cur_1 > 0) {
501
+ update(val_5.slice(0, cur_1 - 1) + val_5.slice(cur_1), cur_1 - 1);
502
+ // Un-queue if editing
503
+ if (queuedRef.current) {
504
+ queuedRef.current = false;
505
+ setQueued(false);
250
506
  }
507
+ }
508
+ return;
251
509
  }
252
- // Check for non-image file paths → attach as file chips
253
- if (lines.length > 0 && lines.length <= 4 && lines.every(l => isFilePath(l))) {
254
- const newFiles = lines.map(l => {
255
- const p = normalizePath(l);
256
- return { path: p, name: basename(p) };
257
- });
258
- setFiles(prev => [...prev, ...newFiles]);
259
- return;
510
+
511
+ // Slash block menu mode while streaming
512
+ if (str === "/" && inputRef.current.value === "") return;
513
+
514
+ // Printable char type into buffer
515
+ if (str.length === 1 && str.charCodeAt(0) >= 0x20) {
516
+ const {
517
+ value: val_6,
518
+ cursor: cur_2
519
+ } = inputRef.current;
520
+ update(val_6.slice(0, cur_2) + str + val_6.slice(cur_2), cur_2 + 1);
521
+ // Un-queue if editing after queuing
522
+ if (queuedRef.current) {
523
+ queuedRef.current = false;
524
+ setQueued(false);
525
+ }
526
+ return;
260
527
  }
261
- // Slash command pasted directly (must look like /word, not an absolute path)
262
- if (clean.startsWith("/") && !clean.includes("\n") && !clean.includes("/", 1) && inputRef.current.value === "") {
263
- onCommandRef.current(clean.trim());
264
- update("", 0);
265
- return;
528
+
529
+ // Multi-character non-escape (unbracketted paste)
530
+ if (str.length > 1 && !str.startsWith("\x1b")) {
531
+ const codePoints = [...str];
532
+ if (codePoints.length === 1) {
533
+ const {
534
+ value: val_7,
535
+ cursor: cur_3
536
+ } = inputRef.current;
537
+ update(val_7.slice(0, cur_3) + str + val_7.slice(cur_3), cur_3 + str.length);
538
+ } else {
539
+ processPaste(str);
540
+ }
541
+ if (queuedRef.current) {
542
+ queuedRef.current = false;
543
+ setQueued(false);
544
+ }
545
+ return;
546
+ }
547
+ return;
548
+ }
549
+
550
+ // ══════════════════════════════════════════════
551
+ // MENU MODE — two-level navigation
552
+ // ══════════════════════════════════════════════
553
+ if (menuModeRef.current) {
554
+ const hasFilter = menuFilterRef.current.length > 0;
555
+ const inCategory = selectedCategoryRef.current !== null;
556
+
557
+ // Escape or Ctrl+C → dismiss menu entirely
558
+ if (str === "\x1b" || str === "\x03") {
559
+ setMenuModeSync(false);
560
+ setMenuFilterSync("");
561
+ setMenuIndexSync(0);
562
+ setSelectedCategorySync(null);
563
+ update("", 0);
564
+ return;
266
565
  }
267
- // Multi-line paste → store as chip, don't spam input
268
- const pasteLines = clean.split("\n");
269
- if (pasteLines.length >= 4) {
270
- setPastedText(clean);
566
+
567
+ // Tab no-op
568
+ if (str === "\t") return;
569
+
570
+ // ── Filter mode (has filter text) ──
571
+ if (hasFilter) {
572
+ if (str === "\r" || str === "\n") {
573
+ const cmd = getCommandAtIndex(menuFilterRef.current, menuIndexRef.current);
574
+ if (cmd) {
575
+ setMenuModeSync(false);
576
+ setMenuFilterSync("");
577
+ setMenuIndexSync(0);
578
+ setSelectedCategorySync(null);
579
+ update("", 0);
580
+ onCommandRef.current(cmd.name);
581
+ }
582
+ return;
583
+ }
584
+ if (str === "\x1b[A") {
585
+ setMenuIndexSync(Math.max(0, menuIndexRef.current - 1));
586
+ return;
587
+ }
588
+ if (str === "\x1b[B") {
589
+ const count = getFilteredSelectableCount(menuFilterRef.current);
590
+ setMenuIndexSync(Math.min(count - 1, menuIndexRef.current + 1));
591
+ return;
592
+ }
593
+ if (str === "\x7f" || str === "\b") {
594
+ const f_0 = menuFilterRef.current;
595
+ if (f_0.length > 1) {
596
+ setMenuFilterSync(f_0.slice(0, -1));
597
+ setMenuIndexSync(0);
598
+ } else {
599
+ // Clearing filter → back to Level 1
600
+ setMenuFilterSync("");
601
+ setMenuIndexSync(0);
602
+ setSelectedCategorySync(null);
603
+ }
271
604
  return;
605
+ }
606
+ if (str.startsWith("\x1b")) return;
607
+ if (str.length === 1 && str.charCodeAt(0) >= 0x20) {
608
+ setMenuFilterSync(menuFilterRef.current + str);
609
+ setMenuIndexSync(0);
610
+ return;
611
+ }
612
+ return;
272
613
  }
273
- // Short paste — insert inline as before
274
- const { value: val, cursor: cur } = inputRef.current;
275
- const newValue = val.slice(0, cur) + clean + val.slice(cur);
276
- update(newValue, cur + clean.length);
277
- }
278
- // ── Handle submit ──
279
- function handleSubmit() {
280
- const typed = inputRef.current.value.trim();
281
- const paste = pastedTextRef.current;
282
- const imgs = imagesRef.current;
283
- const fls = filesRef.current;
284
- const ctxs = contextItemsRef.current;
285
- if (!typed && !paste && imgs.length === 0 && fls.length === 0 && ctxs.length === 0)
614
+
615
+ // ── Level 2: inside a category ──
616
+ if (inCategory) {
617
+ const catIdx = selectedCategoryRef.current;
618
+ if (str === "\r" || str === "\n") {
619
+ const cmd_0 = getCategoryCommand(catIdx, menuIndexRef.current);
620
+ if (cmd_0) {
621
+ setMenuModeSync(false);
622
+ setMenuFilterSync("");
623
+ setMenuIndexSync(0);
624
+ setSelectedCategorySync(null);
625
+ update("", 0);
626
+ onCommandRef.current(cmd_0.name);
627
+ }
628
+ return;
629
+ }
630
+ if (str === "\x1b[A") {
631
+ setMenuIndexSync(Math.max(0, menuIndexRef.current - 1));
286
632
  return;
287
- // Slash command — must start with / and NOT contain a second /
288
- // (prevents file paths like /Users/whale/file.txt from being misidentified)
289
- if (typed.startsWith("/") && !typed.includes("/", 1) && !paste && imgs.length === 0 && fls.length === 0 && ctxs.length === 0) {
290
- update("", 0);
291
- onCommandRef.current(typed);
633
+ }
634
+ if (str === "\x1b[B") {
635
+ const count_0 = getCategoryCommandCount(catIdx);
636
+ setMenuIndexSync(Math.min(count_0 - 1, menuIndexRef.current + 1));
292
637
  return;
638
+ }
639
+ // Left arrow or Backspace → back to Level 1
640
+ if (str === "\x1b[D" || str === "\x7f" || str === "\b") {
641
+ setMenuIndexSync(catIdx); // restore highlight to the category we were in
642
+ setSelectedCategorySync(null);
643
+ return;
644
+ }
645
+ if (str.startsWith("\x1b")) return;
646
+ // Printable → switch to filter mode
647
+ if (str.length === 1 && str.charCodeAt(0) >= 0x20) {
648
+ setSelectedCategorySync(null);
649
+ setMenuFilterSync(str);
650
+ setMenuIndexSync(0);
651
+ return;
652
+ }
653
+ return;
654
+ }
655
+
656
+ // ── Level 1: category list ──
657
+ if (str === "\r" || str === "\n" || str === "\x1b[C") {
658
+ // Enter or Right → drill into selected category
659
+ const catIdx_0 = menuIndexRef.current;
660
+ if (catIdx_0 >= 0 && catIdx_0 < getCategoryCount()) {
661
+ setSelectedCategorySync(catIdx_0);
662
+ setMenuIndexSync(0);
663
+ }
664
+ return;
665
+ }
666
+ if (str === "\x1b[A") {
667
+ setMenuIndexSync(Math.max(0, menuIndexRef.current - 1));
668
+ return;
669
+ }
670
+ if (str === "\x1b[B") {
671
+ setMenuIndexSync(Math.min(getCategoryCount() - 1, menuIndexRef.current + 1));
672
+ return;
673
+ }
674
+ if (str === "\x7f" || str === "\b") {
675
+ // Backspace on Level 1 → dismiss menu
676
+ setMenuModeSync(false);
677
+ setMenuFilterSync("");
678
+ setMenuIndexSync(0);
679
+ setSelectedCategorySync(null);
680
+ update("", 0);
681
+ return;
682
+ }
683
+ if (str.startsWith("\x1b")) return;
684
+ // Printable → switch to filter mode
685
+ if (str.length === 1 && str.charCodeAt(0) >= 0x20) {
686
+ setMenuFilterSync(str);
687
+ setMenuIndexSync(0);
688
+ return;
689
+ }
690
+ return;
691
+ }
692
+
693
+ // ══════════════════════════════════════════════
694
+ // NORMAL INPUT MODE
695
+ // ══════════════════════════════════════════════
696
+
697
+ // ── Bracketed paste detection ──
698
+ if (str.includes("\x1b[200~")) {
699
+ isPasting.current = true;
700
+ let text_1 = str.replace(/\x1b\[200~/g, "").replace(/\x1b\[201~/g, "");
701
+ pasteBuffer.current += text_1;
702
+ if (str.includes("\x1b[201~")) {
703
+ isPasting.current = false;
704
+ const paste_2 = pasteBuffer.current;
705
+ pasteBuffer.current = "";
706
+ processPaste(paste_2);
293
707
  }
294
- // Build context blocks prefix
295
- const contextPrefix = ctxs.length > 0
296
- ? ctxs.map(c => contextToBlock(c)).join("\n\n") + "\n\n"
297
- : "";
298
- // Build file context prefix
299
- const filePrefix = fls.length > 0
300
- ? fls.map(f => f.path).join("\n") + "\n\n"
301
- : "";
302
- // Combine context + file prefix + typed text + pasted content
303
- const body = paste
304
- ? (typed ? `${typed}\n\n${paste}` : paste)
305
- : (typed || (ctxs.length > 0 ? "Tell me about this" : ""));
306
- const fullText = contextPrefix + filePrefix + body;
307
- // Record in history
308
- if (fullText.trim()) {
309
- historyRef.current.push(fullText.trim());
310
- if (historyRef.current.length > MAX_HISTORY)
311
- historyRef.current.shift();
708
+ return;
709
+ }
710
+ if (isPasting.current) {
711
+ if (str.includes("\x1b[201~")) {
712
+ isPasting.current = false;
713
+ pasteBuffer.current += str.replace(/\x1b\[201~/g, "");
714
+ const paste_3 = pasteBuffer.current;
715
+ pasteBuffer.current = "";
716
+ processPaste(paste_3);
717
+ } else {
718
+ pasteBuffer.current += str;
312
719
  }
313
- historyIndexRef.current = -1;
314
- onSubmitRef.current(fullText, imgs.length > 0 ? imgs : undefined);
720
+ return;
721
+ }
722
+
723
+ // ── Control chars handled by ChatApp (keybinding-aware) ──
724
+ const _kb_0 = loadKeybindings();
725
+ const exitChar_0 = bindingToControlChar(_kb_0.exit);
726
+ const expandChar_0 = bindingToControlChar(_kb_0.toggle_expand);
727
+ const thinkingChar_0 = bindingToControlChar(_kb_0.toggle_thinking);
728
+ if (str === exitChar_0 || str === expandChar_0 || str === thinkingChar_0) return;
729
+
730
+ // ── Enter ──
731
+ if (str === "\r" || str === "\n") {
732
+ handleSubmit();
733
+ return;
734
+ }
735
+
736
+ // ── Tab ── (no-op in input)
737
+ if (str === "\t") return;
738
+
739
+ // ── Backspace ──
740
+ if (str === "\x7f" || str === "\b") {
741
+ const {
742
+ value: val_8,
743
+ cursor: cur_4
744
+ } = inputRef.current;
745
+ if (cur_4 > 0) {
746
+ update(val_8.slice(0, cur_4 - 1) + val_8.slice(cur_4), cur_4 - 1);
747
+ } else if (val_8 === "") {
748
+ // Empty input + backspace → remove paste chip, then context, then files, then images
749
+ if (pastedTextRef.current) {
750
+ setPastedText(null);
751
+ } else if (contextItemsRef.current.length > 0) {
752
+ setContextItems(prev_2 => prev_2.slice(0, -1));
753
+ } else if (filesRef.current.length > 0) {
754
+ setFiles(prev_3 => prev_3.slice(0, -1));
755
+ } else if (imagesRef.current.length > 0) {
756
+ setImages(prev_4 => prev_4.slice(0, -1));
757
+ }
758
+ }
759
+ return;
760
+ }
761
+
762
+ // ── Clear line (default: Ctrl+U) ──
763
+ if (str === bindingToControlChar(_kb_0.clear_line)) {
315
764
  update("", 0);
316
765
  setImages([]);
317
766
  setFiles([]);
318
- setContextItems([]);
319
767
  setPastedText(null);
320
- }
321
- // ── Raw stdin handler (handles both normal input and menu mode) ──
322
- useEffect(() => {
323
- if (!stdin)
324
- return;
325
- const onData = (data) => {
326
- const str = data.toString("utf-8");
327
- // ══════════════════════════════════════════════
328
- // DISABLED MODE — accept typing, block commands, handle Esc
329
- // ══════════════════════════════════════════════
330
- if (disabledRef.current) {
331
- // Escape → interrupt streaming
332
- if (str === "\x1b") {
333
- onInterruptRef.current?.();
334
- return;
335
- }
336
- // Control chars handled by ChatApp (exit, expand, thinking toggle)
337
- const _kb = loadKeybindings();
338
- const exitChar = bindingToControlChar(_kb.exit);
339
- const expandChar = bindingToControlChar(_kb.toggle_expand);
340
- const thinkingChar = bindingToControlChar(_kb.toggle_thinking);
341
- if (str === exitChar || str === expandChar || str === thinkingChar)
342
- return;
343
- // Arrow keys — cursor movement within buffer while disabled
344
- if (str.startsWith("\x1b[")) {
345
- const { value: val, cursor: cur } = inputRef.current;
346
- if (str === "\x1b[C") { // Right
347
- update(val, Math.min(cur + 1, val.length));
348
- }
349
- else if (str === "\x1b[D") { // Left
350
- update(val, Math.max(cur - 1, 0));
351
- }
352
- else if (str === "\x1b[A" || str === "\x1b[B") { // Up/Down
353
- const lines = val.split("\n");
354
- if (lines.length > 1) {
355
- const { line: curLine, col: curCol } = getCursorLineCol(val, cur);
356
- const targetLine = str === "\x1b[A"
357
- ? Math.max(0, curLine - 1)
358
- : Math.min(lines.length - 1, curLine + 1);
359
- if (targetLine !== curLine) {
360
- const targetCol = Math.min(curCol, lines[targetLine].length);
361
- let newCursor = 0;
362
- for (let i = 0; i < targetLine; i++)
363
- newCursor += lines[i].length + 1;
364
- newCursor += targetCol;
365
- update(val, newCursor);
366
- }
367
- }
368
- }
369
- else if (str === "\x1b[H" || str === "\x1b[1~") { // Home
370
- update(val, 0);
371
- }
372
- else if (str === "\x1b[F" || str === "\x1b[4~") { // End
373
- update(val, val.length);
374
- }
375
- return;
376
- }
377
- // Bracketed paste while disabled — accept into buffer
378
- if (str.includes("\x1b[200~")) {
379
- isPasting.current = true;
380
- let text = str.replace(/\x1b\[200~/g, "").replace(/\x1b\[201~/g, "");
381
- pasteBuffer.current += text;
382
- if (str.includes("\x1b[201~")) {
383
- isPasting.current = false;
384
- const paste = pasteBuffer.current;
385
- pasteBuffer.current = "";
386
- processPaste(paste);
387
- }
388
- return;
389
- }
390
- if (isPasting.current) {
391
- if (str.includes("\x1b[201~")) {
392
- isPasting.current = false;
393
- pasteBuffer.current += str.replace(/\x1b\[201~/g, "");
394
- const paste = pasteBuffer.current;
395
- pasteBuffer.current = "";
396
- processPaste(paste);
397
- }
398
- else {
399
- pasteBuffer.current += str;
400
- }
401
- return;
402
- }
403
- // Enter → queue the current input for auto-submit
404
- if (str === "\r" || str === "\n") {
405
- const typed = inputRef.current.value.trim();
406
- if (typed) {
407
- queuedRef.current = true;
408
- setQueued(true);
409
- }
410
- return;
411
- }
412
- // Backspace
413
- if (str === "\x7f" || str === "\b") {
414
- const { value: val, cursor: cur } = inputRef.current;
415
- if (cur > 0) {
416
- update(val.slice(0, cur - 1) + val.slice(cur), cur - 1);
417
- // Un-queue if editing
418
- if (queuedRef.current) {
419
- queuedRef.current = false;
420
- setQueued(false);
421
- }
422
- }
423
- return;
424
- }
425
- // Slash — block menu mode while streaming
426
- if (str === "/" && inputRef.current.value === "")
427
- return;
428
- // Printable char — type into buffer
429
- if (str.length === 1 && str.charCodeAt(0) >= 0x20) {
430
- const { value: val, cursor: cur } = inputRef.current;
431
- update(val.slice(0, cur) + str + val.slice(cur), cur + 1);
432
- // Un-queue if editing after queuing
433
- if (queuedRef.current) {
434
- queuedRef.current = false;
435
- setQueued(false);
436
- }
437
- return;
438
- }
439
- // Multi-character non-escape (unbracketted paste)
440
- if (str.length > 1 && !str.startsWith("\x1b")) {
441
- const codePoints = [...str];
442
- if (codePoints.length === 1) {
443
- const { value: val, cursor: cur } = inputRef.current;
444
- update(val.slice(0, cur) + str + val.slice(cur), cur + str.length);
445
- }
446
- else {
447
- processPaste(str);
448
- }
449
- if (queuedRef.current) {
450
- queuedRef.current = false;
451
- setQueued(false);
452
- }
453
- return;
454
- }
455
- return;
456
- }
457
- // ══════════════════════════════════════════════
458
- // MENU MODE — two-level navigation
459
- // ══════════════════════════════════════════════
460
- if (menuModeRef.current) {
461
- const hasFilter = menuFilterRef.current.length > 0;
462
- const inCategory = selectedCategoryRef.current !== null;
463
- // Escape or Ctrl+C → dismiss menu entirely
464
- if (str === "\x1b" || str === "\x03") {
465
- setMenuModeSync(false);
466
- setMenuFilterSync("");
467
- setMenuIndexSync(0);
468
- setSelectedCategorySync(null);
469
- update("", 0);
470
- return;
471
- }
472
- // Tab → no-op
473
- if (str === "\t")
474
- return;
475
- // ── Filter mode (has filter text) ──
476
- if (hasFilter) {
477
- if (str === "\r" || str === "\n") {
478
- const cmd = getCommandAtIndex(menuFilterRef.current, menuIndexRef.current);
479
- if (cmd) {
480
- setMenuModeSync(false);
481
- setMenuFilterSync("");
482
- setMenuIndexSync(0);
483
- setSelectedCategorySync(null);
484
- update("", 0);
485
- onCommandRef.current(cmd.name);
486
- }
487
- return;
488
- }
489
- if (str === "\x1b[A") {
490
- setMenuIndexSync(Math.max(0, menuIndexRef.current - 1));
491
- return;
492
- }
493
- if (str === "\x1b[B") {
494
- const count = getFilteredSelectableCount(menuFilterRef.current);
495
- setMenuIndexSync(Math.min(count - 1, menuIndexRef.current + 1));
496
- return;
497
- }
498
- if (str === "\x7f" || str === "\b") {
499
- const f = menuFilterRef.current;
500
- if (f.length > 1) {
501
- setMenuFilterSync(f.slice(0, -1));
502
- setMenuIndexSync(0);
503
- }
504
- else {
505
- // Clearing filter → back to Level 1
506
- setMenuFilterSync("");
507
- setMenuIndexSync(0);
508
- setSelectedCategorySync(null);
509
- }
510
- return;
511
- }
512
- if (str.startsWith("\x1b"))
513
- return;
514
- if (str.length === 1 && str.charCodeAt(0) >= 0x20) {
515
- setMenuFilterSync(menuFilterRef.current + str);
516
- setMenuIndexSync(0);
517
- return;
518
- }
519
- return;
520
- }
521
- // ── Level 2: inside a category ──
522
- if (inCategory) {
523
- const catIdx = selectedCategoryRef.current;
524
- if (str === "\r" || str === "\n") {
525
- const cmd = getCategoryCommand(catIdx, menuIndexRef.current);
526
- if (cmd) {
527
- setMenuModeSync(false);
528
- setMenuFilterSync("");
529
- setMenuIndexSync(0);
530
- setSelectedCategorySync(null);
531
- update("", 0);
532
- onCommandRef.current(cmd.name);
533
- }
534
- return;
535
- }
536
- if (str === "\x1b[A") {
537
- setMenuIndexSync(Math.max(0, menuIndexRef.current - 1));
538
- return;
539
- }
540
- if (str === "\x1b[B") {
541
- const count = getCategoryCommandCount(catIdx);
542
- setMenuIndexSync(Math.min(count - 1, menuIndexRef.current + 1));
543
- return;
544
- }
545
- // Left arrow or Backspace → back to Level 1
546
- if (str === "\x1b[D" || str === "\x7f" || str === "\b") {
547
- setMenuIndexSync(catIdx); // restore highlight to the category we were in
548
- setSelectedCategorySync(null);
549
- return;
550
- }
551
- if (str.startsWith("\x1b"))
552
- return;
553
- // Printable → switch to filter mode
554
- if (str.length === 1 && str.charCodeAt(0) >= 0x20) {
555
- setSelectedCategorySync(null);
556
- setMenuFilterSync(str);
557
- setMenuIndexSync(0);
558
- return;
559
- }
560
- return;
561
- }
562
- // ── Level 1: category list ──
563
- if (str === "\r" || str === "\n" || str === "\x1b[C") {
564
- // Enter or Right → drill into selected category
565
- const catIdx = menuIndexRef.current;
566
- if (catIdx >= 0 && catIdx < getCategoryCount()) {
567
- setSelectedCategorySync(catIdx);
568
- setMenuIndexSync(0);
569
- }
570
- return;
571
- }
572
- if (str === "\x1b[A") {
573
- setMenuIndexSync(Math.max(0, menuIndexRef.current - 1));
574
- return;
575
- }
576
- if (str === "\x1b[B") {
577
- setMenuIndexSync(Math.min(getCategoryCount() - 1, menuIndexRef.current + 1));
578
- return;
579
- }
580
- if (str === "\x7f" || str === "\b") {
581
- // Backspace on Level 1 → dismiss menu
582
- setMenuModeSync(false);
583
- setMenuFilterSync("");
584
- setMenuIndexSync(0);
585
- setSelectedCategorySync(null);
586
- update("", 0);
587
- return;
588
- }
589
- if (str.startsWith("\x1b"))
590
- return;
591
- // Printable → switch to filter mode
592
- if (str.length === 1 && str.charCodeAt(0) >= 0x20) {
593
- setMenuFilterSync(str);
594
- setMenuIndexSync(0);
595
- return;
596
- }
597
- return;
598
- }
599
- // ══════════════════════════════════════════════
600
- // NORMAL INPUT MODE
601
- // ══════════════════════════════════════════════
602
- // ── Bracketed paste detection ──
603
- if (str.includes("\x1b[200~")) {
604
- isPasting.current = true;
605
- let text = str.replace(/\x1b\[200~/g, "").replace(/\x1b\[201~/g, "");
606
- pasteBuffer.current += text;
607
- if (str.includes("\x1b[201~")) {
608
- isPasting.current = false;
609
- const paste = pasteBuffer.current;
610
- pasteBuffer.current = "";
611
- processPaste(paste);
612
- }
613
- return;
614
- }
615
- if (isPasting.current) {
616
- if (str.includes("\x1b[201~")) {
617
- isPasting.current = false;
618
- pasteBuffer.current += str.replace(/\x1b\[201~/g, "");
619
- const paste = pasteBuffer.current;
620
- pasteBuffer.current = "";
621
- processPaste(paste);
622
- }
623
- else {
624
- pasteBuffer.current += str;
625
- }
626
- return;
627
- }
628
- // ── Control chars handled by ChatApp (keybinding-aware) ──
629
- const _kb = loadKeybindings();
630
- const exitChar = bindingToControlChar(_kb.exit);
631
- const expandChar = bindingToControlChar(_kb.toggle_expand);
632
- const thinkingChar = bindingToControlChar(_kb.toggle_thinking);
633
- if (str === exitChar || str === expandChar || str === thinkingChar)
634
- return;
635
- // ── Enter ──
636
- if (str === "\r" || str === "\n") {
637
- handleSubmit();
638
- return;
639
- }
640
- // ── Tab ── (no-op in input)
641
- if (str === "\t")
642
- return;
643
- // ── Backspace ──
644
- if (str === "\x7f" || str === "\b") {
645
- const { value: val, cursor: cur } = inputRef.current;
646
- if (cur > 0) {
647
- update(val.slice(0, cur - 1) + val.slice(cur), cur - 1);
648
- }
649
- else if (val === "") {
650
- // Empty input + backspace → remove paste chip, then context, then files, then images
651
- if (pastedTextRef.current) {
652
- setPastedText(null);
653
- }
654
- else if (contextItemsRef.current.length > 0) {
655
- setContextItems(prev => prev.slice(0, -1));
656
- }
657
- else if (filesRef.current.length > 0) {
658
- setFiles(prev => prev.slice(0, -1));
659
- }
660
- else if (imagesRef.current.length > 0) {
661
- setImages(prev => prev.slice(0, -1));
662
- }
663
- }
664
- return;
665
- }
666
- // ── Clear line (default: Ctrl+U) ──
667
- if (str === bindingToControlChar(_kb.clear_line)) {
668
- update("", 0);
669
- setImages([]);
670
- setFiles([]);
671
- setPastedText(null);
672
- return;
673
- }
674
- // ── Delete word (default: Ctrl+W) ──
675
- if (str === bindingToControlChar(_kb.delete_word)) {
676
- const { value: val, cursor: cur } = inputRef.current;
677
- const before = val.slice(0, cur);
678
- const match = before.match(/\S+\s*$/);
679
- if (match) {
680
- const len = match[0].length;
681
- update(val.slice(0, cur - len) + val.slice(cur), cur - len);
682
- }
683
- return;
684
- }
685
- // ── Home (default: Ctrl+A) ──
686
- if (str === bindingToControlChar(_kb.home)) {
687
- update(inputRef.current.value, 0);
688
- return;
689
- }
690
- // ── Escape sequences ──
691
- if (str.startsWith("\x1b[")) {
692
- const { value: val, cursor: cur } = inputRef.current;
693
- if (str === "\x1b[C") { // Right
694
- update(val, Math.min(cur + 1, val.length));
695
- }
696
- else if (str === "\x1b[D") { // Left
697
- update(val, Math.max(cur - 1, 0));
698
- }
699
- else if (str === "\x1b[H" || str === "\x1b[1~") { // Home
700
- update(val, 0);
701
- }
702
- else if (str === "\x1b[F" || str === "\x1b[4~") { // End
703
- update(val, val.length);
704
- }
705
- else if (str === "\x1b[3~") { // Delete
706
- if (cur < val.length) {
707
- update(val.slice(0, cur) + val.slice(cur + 1), cur);
708
- }
709
- }
710
- else if (str === "\x1b[A" || str === "\x1b[B") { // Up/Down
711
- const lines = val.split("\n");
712
- if (lines.length > 1) {
713
- // Multi-line: navigate lines
714
- const { line: curLine, col: curCol } = getCursorLineCol(val, cur);
715
- const targetLine = str === "\x1b[A"
716
- ? Math.max(0, curLine - 1)
717
- : Math.min(lines.length - 1, curLine + 1);
718
- if (targetLine !== curLine) {
719
- const targetCol = Math.min(curCol, lines[targetLine].length);
720
- let newCursor = 0;
721
- for (let i = 0; i < targetLine; i++)
722
- newCursor += lines[i].length + 1;
723
- newCursor += targetCol;
724
- update(val, newCursor);
725
- }
726
- }
727
- else if (str === "\x1b[A") {
728
- // Up arrow — history recall
729
- if (historyRef.current.length === 0) { /* no history */ }
730
- else {
731
- if (historyIndexRef.current === -1)
732
- savedInputRef.current = val;
733
- const newIdx = Math.min(historyIndexRef.current + 1, historyRef.current.length - 1);
734
- historyIndexRef.current = newIdx;
735
- const hist = historyRef.current[historyRef.current.length - 1 - newIdx];
736
- update(hist, hist.length);
737
- }
738
- }
739
- else {
740
- // Down arrow — forward in history
741
- if (historyIndexRef.current <= -1) { /* already at current */ }
742
- else {
743
- const newIdx = historyIndexRef.current - 1;
744
- historyIndexRef.current = newIdx;
745
- if (newIdx < 0) {
746
- update(savedInputRef.current, savedInputRef.current.length);
747
- }
748
- else {
749
- const hist = historyRef.current[historyRef.current.length - 1 - newIdx];
750
- update(hist, hist.length);
751
- }
752
- }
753
- }
754
- }
755
- // Ignore unrecognized escape sequences
756
- return;
757
- }
758
- // ── Standalone escape — clear input if non-empty ──
759
- if (str === "\x1b") {
760
- const { value } = inputRef.current;
761
- if (value || filesRef.current.length > 0) {
762
- update("", 0);
763
- setImages([]);
764
- setFiles([]);
765
- setPastedText(null);
766
- }
767
- return;
768
+ return;
769
+ }
770
+
771
+ // ── Delete word (default: Ctrl+W) ──
772
+ if (str === bindingToControlChar(_kb_0.delete_word)) {
773
+ const {
774
+ value: val_9,
775
+ cursor: cur_5
776
+ } = inputRef.current;
777
+ const before = val_9.slice(0, cur_5);
778
+ const match = before.match(/\S+\s*$/);
779
+ if (match) {
780
+ const len = match[0].length;
781
+ update(val_9.slice(0, cur_5 - len) + val_9.slice(cur_5), cur_5 - len);
782
+ }
783
+ return;
784
+ }
785
+
786
+ // ── Home (default: Ctrl+A) ──
787
+ if (str === bindingToControlChar(_kb_0.home)) {
788
+ update(inputRef.current.value, 0);
789
+ return;
790
+ }
791
+
792
+ // ── Escape sequences ──
793
+ if (str.startsWith("\x1b[")) {
794
+ const {
795
+ value: val_10,
796
+ cursor: cur_6
797
+ } = inputRef.current;
798
+ if (str === "\x1b[C") {
799
+ // Right
800
+ update(val_10, Math.min(cur_6 + 1, val_10.length));
801
+ } else if (str === "\x1b[D") {
802
+ // Left
803
+ update(val_10, Math.max(cur_6 - 1, 0));
804
+ } else if (str === "\x1b[H" || str === "\x1b[1~") {
805
+ // Home
806
+ update(val_10, 0);
807
+ } else if (str === "\x1b[F" || str === "\x1b[4~") {
808
+ // End
809
+ update(val_10, val_10.length);
810
+ } else if (str === "\x1b[3~") {
811
+ // Delete
812
+ if (cur_6 < val_10.length) {
813
+ update(val_10.slice(0, cur_6) + val_10.slice(cur_6 + 1), cur_6);
814
+ }
815
+ } else if (str === "\x1b[A" || str === "\x1b[B") {
816
+ // Up/Down
817
+ const lines_1 = val_10.split("\n");
818
+ if (lines_1.length > 1) {
819
+ // Multi-line: navigate lines
820
+ const {
821
+ line: curLine_0,
822
+ col: curCol_0
823
+ } = getCursorLineCol(val_10, cur_6);
824
+ const targetLine_0 = str === "\x1b[A" ? Math.max(0, curLine_0 - 1) : Math.min(lines_1.length - 1, curLine_0 + 1);
825
+ if (targetLine_0 !== curLine_0) {
826
+ const targetCol_0 = Math.min(curCol_0, lines_1[targetLine_0].length);
827
+ let newCursor_0 = 0;
828
+ for (let i_0 = 0; i_0 < targetLine_0; i_0++) newCursor_0 += lines_1[i_0].length + 1;
829
+ newCursor_0 += targetCol_0;
830
+ update(val_10, newCursor_0);
768
831
  }
769
- // ── Multi-character non-escape = paste without brackets ──
770
- if (str.length > 1 && !str.startsWith("\x1b")) {
771
- const codePoints = [...str];
772
- if (codePoints.length === 1) {
773
- // Single code point (emoji etc.)
774
- const { value: val, cursor: cur } = inputRef.current;
775
- update(val.slice(0, cur) + str + val.slice(cur), cur + str.length);
776
- }
777
- else {
778
- processPaste(str);
779
- }
780
- return;
832
+ } else if (str === "\x1b[A") {
833
+ // Up arrow history recall
834
+ if (historyRef.current.length === 0) {/* no history */} else {
835
+ if (historyIndexRef.current === -1) savedInputRef.current = val_10;
836
+ const newIdx = Math.min(historyIndexRef.current + 1, historyRef.current.length - 1);
837
+ historyIndexRef.current = newIdx;
838
+ const hist = historyRef.current[historyRef.current.length - 1 - newIdx];
839
+ update(hist, hist.length);
781
840
  }
782
- // ── Single printable character ──
783
- if (str.length === 1 && str.charCodeAt(0) >= 0x20) {
784
- const { value: val, cursor: cur } = inputRef.current;
785
- // Slash menu trigger
786
- if (str === "/" && val === "") {
787
- setMenuModeSync(true);
788
- setMenuFilterSync("");
789
- setMenuIndexSync(0);
790
- return;
791
- }
792
- update(val.slice(0, cur) + str + val.slice(cur), cur + 1);
841
+ } else {
842
+ // Down arrow forward in history
843
+ if (historyIndexRef.current <= -1) {/* already at current */} else {
844
+ const newIdx_0 = historyIndexRef.current - 1;
845
+ historyIndexRef.current = newIdx_0;
846
+ if (newIdx_0 < 0) {
847
+ update(savedInputRef.current, savedInputRef.current.length);
848
+ } else {
849
+ const hist_0 = historyRef.current[historyRef.current.length - 1 - newIdx_0];
850
+ update(hist_0, hist_0.length);
851
+ }
793
852
  }
794
- };
795
- stdin.on("data", onData);
796
- return () => { stdin.off("data", onData); };
797
- }, [stdin]); // eslint-disable-line react-hooks/exhaustive-deps
798
- // ── Render ──
799
- // Disabled — dimmed but visible input (accept typing to queue)
800
- if (disabled) {
801
- const disabledLines = displayValue.split("\n");
802
- const { line: dCursorLine, col: dCursorCol } = displayValue
803
- ? getCursorLineCol(displayValue, displayCursor)
804
- : { line: 0, col: 0 };
805
- return (_jsx(Box, { flexDirection: "column", children: disabledLines.length <= 1 ? (_jsxs(Box, { children: [_jsx(Text, { color: colors.quaternary, bold: true, children: "❯ " }), !displayValue ? (_jsx(Text, { color: colors.quaternary, wrap: "truncate", children: queued ? "queued — will send when done" : "type to queue a message..." })) : (_jsxs(Box, { children: [_jsxs(Text, { color: colors.dim, children: [displayValue.slice(0, displayCursor), _jsx(Text, { inverse: true, children: displayCursor < displayValue.length ? displayValue[displayCursor] : " " }), displayCursor < displayValue.length ? displayValue.slice(displayCursor + 1) : ""] }), queued && _jsx(Text, { color: colors.warning, children: " [queued]" })] }))] })) : (
806
- /* Multi-line disabled */
807
- _jsxs(Box, { flexDirection: "column", children: [disabledLines.map((line, i) => {
808
- const isCursorOnLine = i === dCursorLine;
809
- return (_jsxs(Box, { children: [_jsx(Text, { color: colors.quaternary, bold: true, children: i === 0 ? "❯ " : "⎸ " }), isCursorOnLine ? (_jsxs(Text, { color: colors.dim, children: [line.slice(0, dCursorCol), _jsx(Text, { inverse: true, children: dCursorCol < line.length ? line[dCursorCol] : " " }), dCursorCol < line.length ? line.slice(dCursorCol + 1) : ""] })) : (_jsx(Text, { color: colors.dim, children: line }))] }, i));
810
- }), queued && (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: colors.warning, children: "[queued]" }) }))] })) }));
811
- }
812
- // Slash command menu — two-level categorized via SlashMenu component
813
- if (menuMode) {
814
- // Prompt line varies by level
815
- let promptLine;
816
- if (menuFilter) {
817
- // Filter mode
818
- promptLine = (_jsxs(Box, { children: [_jsx(Text, { color: colors.brand, bold: true, children: "/ " }), _jsxs(Text, { children: [menuFilter, _jsx(Text, { inverse: true, children: " " })] })] }));
853
+ }
854
+ }
855
+ // Ignore unrecognized escape sequences
856
+ return;
857
+ }
858
+
859
+ // ── Standalone escape — clear input if non-empty ──
860
+ if (str === "\x1b") {
861
+ const {
862
+ value: value_0
863
+ } = inputRef.current;
864
+ if (value_0 || filesRef.current.length > 0) {
865
+ update("", 0);
866
+ setImages([]);
867
+ setFiles([]);
868
+ setPastedText(null);
819
869
  }
820
- else if (selectedCategory !== null) {
821
- // Level 2: show category name
822
- const catLabel = getCategoryLabel(selectedCategory);
823
- const catColor = getCategoryColor(selectedCategory);
824
- promptLine = (_jsxs(Box, { children: [_jsx(Text, { color: colors.brand, bold: true, children: "/ " }), _jsx(Text, { color: catColor, bold: true, children: catLabel }), _jsx(Text, { color: colors.tertiary, children: " > " }), _jsx(Text, { inverse: true, children: " " })] }));
870
+ return;
871
+ }
872
+
873
+ // ── Multi-character non-escape = paste without brackets ──
874
+ if (str.length > 1 && !str.startsWith("\x1b")) {
875
+ const codePoints_0 = [...str];
876
+ if (codePoints_0.length === 1) {
877
+ // Single code point (emoji etc.)
878
+ const {
879
+ value: val_11,
880
+ cursor: cur_7
881
+ } = inputRef.current;
882
+ update(val_11.slice(0, cur_7) + str + val_11.slice(cur_7), cur_7 + str.length);
883
+ } else {
884
+ processPaste(str);
825
885
  }
826
- else {
827
- // Level 1
828
- promptLine = (_jsxs(Box, { children: [_jsx(Text, { color: colors.brand, bold: true, children: "/ " }), _jsxs(Text, { children: [_jsx(Text, { inverse: true, children: " " }), _jsx(Text, { color: colors.dim, children: " type to filter" })] })] }));
886
+ return;
887
+ }
888
+
889
+ // ── Single printable character ──
890
+ if (str.length === 1 && str.charCodeAt(0) >= 0x20) {
891
+ const {
892
+ value: val_12,
893
+ cursor: cur_8
894
+ } = inputRef.current;
895
+
896
+ // Slash menu trigger
897
+ if (str === "/" && val_12 === "") {
898
+ setMenuModeSync(true);
899
+ setMenuFilterSync("");
900
+ setMenuIndexSync(0);
901
+ return;
829
902
  }
830
- // Hint line varies by level
831
- const hint = selectedCategory !== null && !menuFilter
832
- ? " \u2190 back esc dismiss"
833
- : " esc to dismiss";
834
- return (_jsxs(Box, { flexDirection: "column", children: [promptLine, _jsx(SlashMenu, { filter: menuFilter, selectedIndex: menuIndex, selectedCategory: selectedCategory }), _jsx(Text, { color: colors.quaternary, children: hint })] }));
903
+ update(val_12.slice(0, cur_8) + str + val_12.slice(cur_8), cur_8 + 1);
904
+ }
905
+ };
906
+ stdin.on("data", onData);
907
+ return () => {
908
+ stdin.off("data", onData);
909
+ };
910
+ }, [stdin]); // eslint-disable-line react-hooks/exhaustive-deps
911
+
912
+ // ── Render ──
913
+
914
+ // Disabled — dimmed but visible input (accept typing to queue)
915
+ if (disabled) {
916
+ const disabledLines = displayValue.split("\n");
917
+ const {
918
+ line: dCursorLine,
919
+ col: dCursorCol
920
+ } = displayValue ? getCursorLineCol(displayValue, displayCursor) : {
921
+ line: 0,
922
+ col: 0
923
+ };
924
+ return /*#__PURE__*/_jsx(Box, {
925
+ flexDirection: "column",
926
+ children: disabledLines.length <= 1 ? /*#__PURE__*/_jsxs(Box, {
927
+ children: [/*#__PURE__*/_jsx(Text, {
928
+ color: colors.quaternary,
929
+ bold: true,
930
+ children: "❯ "
931
+ }), !displayValue ? /*#__PURE__*/_jsx(Text, {
932
+ color: colors.quaternary,
933
+ wrap: "truncate",
934
+ children: queued ? "queued — will send when done" : "type to queue a message..."
935
+ }) : /*#__PURE__*/_jsxs(Box, {
936
+ children: [/*#__PURE__*/_jsxs(Text, {
937
+ color: colors.dim,
938
+ children: [displayValue.slice(0, displayCursor), /*#__PURE__*/_jsx(Text, {
939
+ inverse: true,
940
+ children: displayCursor < displayValue.length ? displayValue[displayCursor] : " "
941
+ }), displayCursor < displayValue.length ? displayValue.slice(displayCursor + 1) : ""]
942
+ }), queued && /*#__PURE__*/_jsx(Text, {
943
+ color: colors.warning,
944
+ children: " [queued]"
945
+ })]
946
+ })]
947
+ }) :
948
+ /*#__PURE__*/
949
+ /* Multi-line disabled */
950
+ _jsxs(Box, {
951
+ flexDirection: "column",
952
+ children: [disabledLines.map((line_0, i_1) => {
953
+ const isCursorOnLine = i_1 === dCursorLine;
954
+ return /*#__PURE__*/_jsxs(Box, {
955
+ children: [/*#__PURE__*/_jsx(Text, {
956
+ color: colors.quaternary,
957
+ bold: true,
958
+ children: i_1 === 0 ? "❯ " : "⎸ "
959
+ }), isCursorOnLine ? /*#__PURE__*/_jsxs(Text, {
960
+ color: colors.dim,
961
+ children: [line_0.slice(0, dCursorCol), /*#__PURE__*/_jsx(Text, {
962
+ inverse: true,
963
+ children: dCursorCol < line_0.length ? line_0[dCursorCol] : " "
964
+ }), dCursorCol < line_0.length ? line_0.slice(dCursorCol + 1) : ""]
965
+ }) : /*#__PURE__*/_jsx(Text, {
966
+ color: colors.dim,
967
+ children: line_0
968
+ })]
969
+ }, i_1);
970
+ }), queued && /*#__PURE__*/_jsx(Box, {
971
+ marginLeft: 2,
972
+ children: /*#__PURE__*/_jsx(Text, {
973
+ color: colors.warning,
974
+ children: "[queued]"
975
+ })
976
+ })]
977
+ })
978
+ });
979
+ }
980
+
981
+ // Slash command menu — two-level categorized via SlashMenu component
982
+ if (menuMode) {
983
+ // Prompt line varies by level
984
+ let promptLine;
985
+ if (menuFilter) {
986
+ // Filter mode
987
+ promptLine = /*#__PURE__*/_jsxs(Box, {
988
+ children: [/*#__PURE__*/_jsx(Text, {
989
+ color: colors.brand,
990
+ bold: true,
991
+ children: "/ "
992
+ }), /*#__PURE__*/_jsxs(Text, {
993
+ children: [menuFilter, /*#__PURE__*/_jsx(Text, {
994
+ inverse: true,
995
+ children: " "
996
+ })]
997
+ })]
998
+ });
999
+ } else if (selectedCategory !== null) {
1000
+ // Level 2: show category name
1001
+ const catLabel = getCategoryLabel(selectedCategory);
1002
+ const catColor = getCategoryColor(selectedCategory);
1003
+ promptLine = /*#__PURE__*/_jsxs(Box, {
1004
+ children: [/*#__PURE__*/_jsx(Text, {
1005
+ color: colors.brand,
1006
+ bold: true,
1007
+ children: "/ "
1008
+ }), /*#__PURE__*/_jsx(Text, {
1009
+ color: catColor,
1010
+ bold: true,
1011
+ children: catLabel
1012
+ }), /*#__PURE__*/_jsx(Text, {
1013
+ color: colors.tertiary,
1014
+ children: " > "
1015
+ }), /*#__PURE__*/_jsx(Text, {
1016
+ inverse: true,
1017
+ children: " "
1018
+ })]
1019
+ });
1020
+ } else {
1021
+ // Level 1
1022
+ promptLine = /*#__PURE__*/_jsxs(Box, {
1023
+ children: [/*#__PURE__*/_jsx(Text, {
1024
+ color: colors.brand,
1025
+ bold: true,
1026
+ children: "/ "
1027
+ }), /*#__PURE__*/_jsxs(Text, {
1028
+ children: [/*#__PURE__*/_jsx(Text, {
1029
+ inverse: true,
1030
+ children: " "
1031
+ }), /*#__PURE__*/_jsx(Text, {
1032
+ color: colors.dim,
1033
+ children: " type to filter"
1034
+ })]
1035
+ })]
1036
+ });
835
1037
  }
836
- // ── Normal input rendering ──
837
- const lines = displayValue.split("\n");
838
- const { line: cursorLine, col: cursorCol } = displayValue
839
- ? getCursorLineCol(displayValue, displayCursor)
840
- : { line: 0, col: 0 };
841
- // Truncate display for very long pastes
842
- const isTruncated = lines.length > MAX_DISPLAY_LINES;
843
- const visibleLines = isTruncated
844
- ? [...lines.slice(0, MAX_DISPLAY_LINES - 1), `… ${lines.length - MAX_DISPLAY_LINES + 1} more lines`]
845
- : lines;
846
- return (_jsxs(Box, { flexDirection: "column", children: [(images.length > 0 || files.length > 0 || contextItems.length > 0 || pastedText) && (_jsxs(Box, { children: [_jsx(Text, { children: " " }), contextItems.map((ctx, i) => (_jsxs(Text, { children: [_jsx(Text, { color: CONTEXT_COLORS[ctx.type] || "cyan", children: "[" }), _jsxs(Text, { color: colors.secondary, children: [ctx.type, ": ", ctx.title] }), _jsx(Text, { color: CONTEXT_COLORS[ctx.type] || "cyan", children: "]" }), _jsx(Text, { children: " " })] }, `ctx-${i}`))), images.map((img, i) => (_jsxs(Text, { children: [_jsx(Text, { color: colors.indigo, children: "[" }), _jsx(Text, { color: colors.secondary, children: img.name }), _jsx(Text, { color: colors.indigo, children: "]" }), _jsx(Text, { children: " " })] }, `img-${i}`))), files.map((f, i) => (_jsxs(Text, { children: [_jsx(Text, { color: colors.purple, children: "[" }), _jsx(Text, { color: colors.secondary, children: f.name }), _jsx(Text, { color: colors.purple, children: "]" }), _jsx(Text, { children: " " })] }, `file-${i}`))), pastedText && (_jsxs(Text, { children: [_jsx(Text, { color: colors.indigo, children: "[" }), _jsxs(Text, { color: colors.secondary, children: ["pasted ", pastedText.split("\n").length, " lines"] }), _jsx(Text, { color: colors.indigo, children: "]" })] }))] })), lines.length <= 1 ? (_jsxs(Box, { children: [_jsx(Text, { color: colors.brand, bold: true, children: "❯ " }), !displayValue ? (_jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { inverse: true, children: " " }), _jsx(Text, { color: colors.dim, children: `Message ${agentName || "whale"}, or type / for commands` })] })) : (_jsxs(Text, { children: [displayValue.slice(0, displayCursor), _jsx(Text, { inverse: true, children: displayCursor < displayValue.length ? displayValue[displayCursor] : " " }), displayCursor < displayValue.length ? displayValue.slice(displayCursor + 1) : ""] }))] })) : (
847
- /* Multi-line input */
848
- _jsx(Box, { flexDirection: "column", children: visibleLines.map((line, i) => {
849
- const isRealLine = !isTruncated || i < MAX_DISPLAY_LINES - 1;
850
- const isCursorOnLine = isRealLine && i === cursorLine;
851
- return (_jsxs(Box, { children: [_jsx(Text, { color: colors.brand, bold: true, children: i === 0 ? "❯ " : "⎸ " }), isCursorOnLine ? (_jsxs(Text, { children: [line.slice(0, cursorCol), _jsx(Text, { inverse: true, children: cursorCol < line.length ? line[cursorCol] : " " }), cursorCol < line.length ? line.slice(cursorCol + 1) : ""] })) : (_jsx(Text, { color: isRealLine ? undefined : colors.tertiary, children: line }))] }, i));
852
- }) }))] }));
1038
+
1039
+ // Hint line varies by level
1040
+ const hint = selectedCategory !== null && !menuFilter ? " \u2190 back esc dismiss" : " esc to dismiss";
1041
+ return /*#__PURE__*/_jsxs(Box, {
1042
+ flexDirection: "column",
1043
+ children: [promptLine, /*#__PURE__*/_jsx(SlashMenu, {
1044
+ filter: menuFilter,
1045
+ selectedIndex: menuIndex,
1046
+ selectedCategory: selectedCategory
1047
+ }), /*#__PURE__*/_jsx(Text, {
1048
+ color: colors.quaternary,
1049
+ children: hint
1050
+ })]
1051
+ });
1052
+ }
1053
+
1054
+ // ── Normal input rendering ──
1055
+
1056
+ const lines_2 = displayValue.split("\n");
1057
+ const {
1058
+ line: cursorLine,
1059
+ col: cursorCol
1060
+ } = displayValue ? getCursorLineCol(displayValue, displayCursor) : {
1061
+ line: 0,
1062
+ col: 0
1063
+ };
1064
+
1065
+ // Truncate display for very long pastes
1066
+ const isTruncated = lines_2.length > MAX_DISPLAY_LINES;
1067
+ const visibleLines = isTruncated ? [...lines_2.slice(0, MAX_DISPLAY_LINES - 1), `… ${lines_2.length - MAX_DISPLAY_LINES + 1} more lines`] : lines_2;
1068
+ return /*#__PURE__*/_jsxs(Box, {
1069
+ flexDirection: "column",
1070
+ children: [(images.length > 0 || files.length > 0 || contextItems.length > 0 || pastedText) && /*#__PURE__*/_jsxs(Box, {
1071
+ children: [/*#__PURE__*/_jsx(Text, {
1072
+ children: " "
1073
+ }), contextItems.map((ctx_0, i_2) => /*#__PURE__*/_jsxs(Text, {
1074
+ children: [/*#__PURE__*/_jsx(Text, {
1075
+ color: CONTEXT_COLORS[ctx_0.type] || "cyan",
1076
+ children: "["
1077
+ }), /*#__PURE__*/_jsxs(Text, {
1078
+ color: colors.secondary,
1079
+ children: [ctx_0.type, ": ", ctx_0.title]
1080
+ }), /*#__PURE__*/_jsx(Text, {
1081
+ color: CONTEXT_COLORS[ctx_0.type] || "cyan",
1082
+ children: "]"
1083
+ }), /*#__PURE__*/_jsx(Text, {
1084
+ children: " "
1085
+ })]
1086
+ }, `ctx-${i_2}`)), images.map((img_0, i_3) => /*#__PURE__*/_jsxs(Text, {
1087
+ children: [/*#__PURE__*/_jsx(Text, {
1088
+ color: colors.indigo,
1089
+ children: "["
1090
+ }), /*#__PURE__*/_jsx(Text, {
1091
+ color: colors.secondary,
1092
+ children: img_0.name
1093
+ }), /*#__PURE__*/_jsx(Text, {
1094
+ color: colors.indigo,
1095
+ children: "]"
1096
+ }), /*#__PURE__*/_jsx(Text, {
1097
+ children: " "
1098
+ })]
1099
+ }, `img-${i_3}`)), files.map((f_1, i_4) => /*#__PURE__*/_jsxs(Text, {
1100
+ children: [/*#__PURE__*/_jsx(Text, {
1101
+ color: colors.purple,
1102
+ children: "["
1103
+ }), /*#__PURE__*/_jsx(Text, {
1104
+ color: colors.secondary,
1105
+ children: f_1.name
1106
+ }), /*#__PURE__*/_jsx(Text, {
1107
+ color: colors.purple,
1108
+ children: "]"
1109
+ }), /*#__PURE__*/_jsx(Text, {
1110
+ children: " "
1111
+ })]
1112
+ }, `file-${i_4}`)), pastedText && /*#__PURE__*/_jsxs(Text, {
1113
+ children: [/*#__PURE__*/_jsx(Text, {
1114
+ color: colors.indigo,
1115
+ children: "["
1116
+ }), /*#__PURE__*/_jsxs(Text, {
1117
+ color: colors.secondary,
1118
+ children: ["pasted ", pastedText.split("\n").length, " lines"]
1119
+ }), /*#__PURE__*/_jsx(Text, {
1120
+ color: colors.indigo,
1121
+ children: "]"
1122
+ })]
1123
+ })]
1124
+ }), lines_2.length <= 1 ? /*#__PURE__*/_jsxs(Box, {
1125
+ children: [/*#__PURE__*/_jsx(Text, {
1126
+ color: colors.brand,
1127
+ bold: true,
1128
+ children: "❯ "
1129
+ }), !displayValue ? /*#__PURE__*/_jsxs(Text, {
1130
+ wrap: "truncate",
1131
+ children: [/*#__PURE__*/_jsx(Text, {
1132
+ inverse: true,
1133
+ children: " "
1134
+ }), /*#__PURE__*/_jsx(Text, {
1135
+ color: colors.dim,
1136
+ children: `Message ${agentName || "whale"}, or type / for commands`
1137
+ })]
1138
+ }) : /*#__PURE__*/_jsxs(Text, {
1139
+ children: [displayValue.slice(0, displayCursor), /*#__PURE__*/_jsx(Text, {
1140
+ inverse: true,
1141
+ children: displayCursor < displayValue.length ? displayValue[displayCursor] : " "
1142
+ }), displayCursor < displayValue.length ? displayValue.slice(displayCursor + 1) : ""]
1143
+ })]
1144
+ }) :
1145
+ /*#__PURE__*/
1146
+ /* Multi-line input */
1147
+ _jsx(Box, {
1148
+ flexDirection: "column",
1149
+ children: visibleLines.map((line_1, i_5) => {
1150
+ const isRealLine = !isTruncated || i_5 < MAX_DISPLAY_LINES - 1;
1151
+ const isCursorOnLine_0 = isRealLine && i_5 === cursorLine;
1152
+ return /*#__PURE__*/_jsxs(Box, {
1153
+ children: [/*#__PURE__*/_jsx(Text, {
1154
+ color: colors.brand,
1155
+ bold: true,
1156
+ children: i_5 === 0 ? "❯ " : "⎸ "
1157
+ }), isCursorOnLine_0 ? /*#__PURE__*/_jsxs(Text, {
1158
+ children: [line_1.slice(0, cursorCol), /*#__PURE__*/_jsx(Text, {
1159
+ inverse: true,
1160
+ children: cursorCol < line_1.length ? line_1[cursorCol] : " "
1161
+ }), cursorCol < line_1.length ? line_1.slice(cursorCol + 1) : ""]
1162
+ }) : /*#__PURE__*/_jsx(Text, {
1163
+ color: isRealLine ? undefined : colors.tertiary,
1164
+ children: line_1
1165
+ })]
1166
+ }, i_5);
1167
+ })
1168
+ })]
1169
+ });
853
1170
  }
1171
+ //# sourceMappingURL=ChatInput.js.map