cli-jaw 2.2.15 → 2.2.17

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 (425) hide show
  1. package/README.md +23 -2
  2. package/dist/bin/cli-jaw.js +39 -0
  3. package/dist/bin/cli-jaw.js.map +1 -1
  4. package/dist/bin/commands/_shared/dashboard-client.js +42 -0
  5. package/dist/bin/commands/_shared/dashboard-client.js.map +1 -0
  6. package/dist/bin/commands/_shared/search-format.js +40 -0
  7. package/dist/bin/commands/_shared/search-format.js.map +1 -0
  8. package/dist/bin/commands/chat-search.js +5 -3
  9. package/dist/bin/commands/chat-search.js.map +1 -1
  10. package/dist/bin/commands/dashboard-chat.js +14 -31
  11. package/dist/bin/commands/dashboard-chat.js.map +1 -1
  12. package/dist/bin/commands/dashboard-memory.js +12 -49
  13. package/dist/bin/commands/dashboard-memory.js.map +1 -1
  14. package/dist/bin/commands/doctor.js +56 -11
  15. package/dist/bin/commands/doctor.js.map +1 -1
  16. package/dist/bin/commands/init.js +31 -1
  17. package/dist/bin/commands/init.js.map +1 -1
  18. package/dist/bin/commands/memory.js +5 -1
  19. package/dist/bin/commands/memory.js.map +1 -1
  20. package/dist/bin/commands/provider.js +23 -0
  21. package/dist/bin/commands/provider.js.map +1 -1
  22. package/dist/bin/commands/slack.js +57 -3
  23. package/dist/bin/commands/slack.js.map +1 -1
  24. package/dist/bin/commands/tui/input-handler.js +1 -1
  25. package/dist/bin/commands/tui/input-handler.js.map +1 -1
  26. package/dist/bin/commands/tui/slash-command-runner.js +4 -5
  27. package/dist/bin/commands/tui/slash-command-runner.js.map +1 -1
  28. package/dist/bin/postinstall.js +16 -18
  29. package/dist/bin/postinstall.js.map +1 -1
  30. package/dist/lib/media-kind.js +28 -0
  31. package/dist/lib/media-kind.js.map +1 -0
  32. package/dist/lib/upload.js +27 -10
  33. package/dist/lib/upload.js.map +1 -1
  34. package/dist/server.js +35 -51
  35. package/dist/server.js.map +1 -1
  36. package/dist/src/agent/args.js +34 -0
  37. package/dist/src/agent/args.js.map +1 -1
  38. package/dist/src/agent/codex-app-client.js +1016 -135
  39. package/dist/src/agent/codex-app-client.js.map +1 -1
  40. package/dist/src/agent/codex-app-events.js +72 -1
  41. package/dist/src/agent/codex-app-events.js.map +1 -1
  42. package/dist/src/agent/codex-host-pool.js +617 -0
  43. package/dist/src/agent/codex-host-pool.js.map +1 -0
  44. package/dist/src/agent/jwc-runtime.js +12 -4
  45. package/dist/src/agent/jwc-runtime.js.map +1 -1
  46. package/dist/src/agent/kiro-auth.js +10 -4
  47. package/dist/src/agent/kiro-auth.js.map +1 -1
  48. package/dist/src/agent/kiro-runtime.js +45 -8
  49. package/dist/src/agent/kiro-runtime.js.map +1 -1
  50. package/dist/src/agent/lifecycle-handler.js +75 -52
  51. package/dist/src/agent/lifecycle-handler.js.map +1 -1
  52. package/dist/src/agent/memory-flush-controller.js +241 -33
  53. package/dist/src/agent/memory-flush-controller.js.map +1 -1
  54. package/dist/src/agent/prompt-context.js +4 -0
  55. package/dist/src/agent/prompt-context.js.map +1 -1
  56. package/dist/src/agent/runtime-pool.js +64 -34
  57. package/dist/src/agent/runtime-pool.js.map +1 -1
  58. package/dist/src/agent/session-persistence.js +71 -15
  59. package/dist/src/agent/session-persistence.js.map +1 -1
  60. package/dist/src/agent/spawn/queue.js +319 -162
  61. package/dist/src/agent/spawn/queue.js.map +1 -1
  62. package/dist/src/agent/spawn.js +581 -268
  63. package/dist/src/agent/spawn.js.map +1 -1
  64. package/dist/src/bgtask/types.js +1 -1
  65. package/dist/src/bgtask/types.js.map +1 -1
  66. package/dist/src/browser/adaptive-fetch/safety.js +25 -0
  67. package/dist/src/browser/adaptive-fetch/safety.js.map +1 -1
  68. package/dist/src/browser/connection.js +2 -8
  69. package/dist/src/browser/connection.js.map +1 -1
  70. package/dist/src/cli/capability-probe-worker.js +172 -0
  71. package/dist/src/cli/capability-probe-worker.js.map +1 -0
  72. package/dist/src/cli/capability-probe.js +83 -0
  73. package/dist/src/cli/capability-probe.js.map +1 -0
  74. package/dist/src/cli/claude-models.js +2 -2
  75. package/dist/src/cli/claude-models.js.map +1 -1
  76. package/dist/src/cli/cli-status-worker.js +296 -0
  77. package/dist/src/cli/cli-status-worker.js.map +1 -0
  78. package/dist/src/cli/cli-status.js +81 -0
  79. package/dist/src/cli/cli-status.js.map +1 -0
  80. package/dist/src/cli/command-context.js +17 -5
  81. package/dist/src/cli/command-context.js.map +1 -1
  82. package/dist/src/cli/compact.js +56 -19
  83. package/dist/src/cli/compact.js.map +1 -1
  84. package/dist/src/cli/handlers-runtime.js +6 -4
  85. package/dist/src/cli/handlers-runtime.js.map +1 -1
  86. package/dist/src/cli/handlers.js +7 -2
  87. package/dist/src/cli/handlers.js.map +1 -1
  88. package/dist/src/cli/opencodex-models.js +5 -32
  89. package/dist/src/cli/opencodex-models.js.map +1 -1
  90. package/dist/src/cli/opencodex-runtime.js +82 -0
  91. package/dist/src/cli/opencodex-runtime.js.map +1 -0
  92. package/dist/src/cli/readiness.js +35 -18
  93. package/dist/src/cli/readiness.js.map +1 -1
  94. package/dist/src/cli/registry-live.js +8 -2
  95. package/dist/src/cli/registry-live.js.map +1 -1
  96. package/dist/src/cli/registry.js +1 -1
  97. package/dist/src/cli/registry.js.map +1 -1
  98. package/dist/src/core/browser-open-default.js +6 -4
  99. package/dist/src/core/browser-open-default.js.map +1 -1
  100. package/dist/src/core/browser-open.js +11 -15
  101. package/dist/src/core/browser-open.js.map +1 -1
  102. package/dist/src/core/bus.js +7 -1
  103. package/dist/src/core/bus.js.map +1 -1
  104. package/dist/src/core/chat-sessions.js +121 -12
  105. package/dist/src/core/chat-sessions.js.map +1 -1
  106. package/dist/src/core/cli-detect.js +148 -4
  107. package/dist/src/core/cli-detect.js.map +1 -1
  108. package/dist/src/core/codex-config.js +21 -0
  109. package/dist/src/core/codex-config.js.map +1 -1
  110. package/dist/src/core/compact.js +73 -21
  111. package/dist/src/core/compact.js.map +1 -1
  112. package/dist/src/core/config.js +449 -20
  113. package/dist/src/core/config.js.map +1 -1
  114. package/dist/src/core/db.js +225 -0
  115. package/dist/src/core/db.js.map +1 -1
  116. package/dist/src/core/event-bus.js +49 -4
  117. package/dist/src/core/event-bus.js.map +1 -1
  118. package/dist/src/core/event-scope.js +100 -0
  119. package/dist/src/core/event-scope.js.map +1 -0
  120. package/dist/src/core/install-integrity.js +176 -0
  121. package/dist/src/core/install-integrity.js.map +1 -0
  122. package/dist/src/core/main-session.js +64 -21
  123. package/dist/src/core/main-session.js.map +1 -1
  124. package/dist/src/core/message-summary.js +12 -2
  125. package/dist/src/core/message-summary.js.map +1 -1
  126. package/dist/src/core/platform-kind.js +139 -0
  127. package/dist/src/core/platform-kind.js.map +1 -0
  128. package/dist/src/core/rate-limit.js +156 -0
  129. package/dist/src/core/rate-limit.js.map +1 -0
  130. package/dist/src/core/runtime-settings.js +165 -17
  131. package/dist/src/core/runtime-settings.js.map +1 -1
  132. package/dist/src/core/session-context.js +7 -0
  133. package/dist/src/core/session-context.js.map +1 -0
  134. package/dist/src/core/session-ops.js +53 -9
  135. package/dist/src/core/session-ops.js.map +1 -1
  136. package/dist/src/core/settings-merge.js +105 -3
  137. package/dist/src/core/settings-merge.js.map +1 -1
  138. package/dist/src/core/settings-watch.js +29 -5
  139. package/dist/src/core/settings-watch.js.map +1 -1
  140. package/dist/src/core/widget-watcher.js +15 -2
  141. package/dist/src/core/widget-watcher.js.map +1 -1
  142. package/dist/src/discord/bot.js +5 -2
  143. package/dist/src/discord/bot.js.map +1 -1
  144. package/dist/src/discord/commands.js +4 -4
  145. package/dist/src/discord/commands.js.map +1 -1
  146. package/dist/src/goal/heartbeat.js +1 -1
  147. package/dist/src/goal/heartbeat.js.map +1 -1
  148. package/dist/src/goal/runtime.js +1 -1
  149. package/dist/src/goal/runtime.js.map +1 -1
  150. package/dist/src/goal-run/controller.js +2 -2
  151. package/dist/src/goal-run/controller.js.map +1 -1
  152. package/dist/src/manager/internal-fetch.js +15 -0
  153. package/dist/src/manager/internal-fetch.js.map +1 -0
  154. package/dist/src/manager/logs.js +2 -1
  155. package/dist/src/manager/logs.js.map +1 -1
  156. package/dist/src/manager/memory/chat-federation.js +104 -11
  157. package/dist/src/manager/memory/chat-federation.js.map +1 -1
  158. package/dist/src/manager/memory/federation.js +100 -0
  159. package/dist/src/manager/memory/federation.js.map +1 -1
  160. package/dist/src/manager/metadata.js +4 -1
  161. package/dist/src/manager/metadata.js.map +1 -1
  162. package/dist/src/manager/notes/assets.js +1 -1
  163. package/dist/src/manager/notes/assets.js.map +1 -1
  164. package/dist/src/manager/notes/frontmatter.js +5 -49
  165. package/dist/src/manager/notes/frontmatter.js.map +1 -1
  166. package/dist/src/manager/notes/link-resolver.js +2 -2
  167. package/dist/src/manager/notes/link-resolver.js.map +1 -1
  168. package/dist/src/manager/notes/remote-assets.js +1 -1
  169. package/dist/src/manager/notes/remote-assets.js.map +1 -1
  170. package/dist/src/manager/notes/routes.js +2 -2
  171. package/dist/src/manager/notes/routes.js.map +1 -1
  172. package/dist/src/manager/notes/store.js +2 -2
  173. package/dist/src/manager/notes/store.js.map +1 -1
  174. package/dist/src/manager/notes/trash.js +1 -1
  175. package/dist/src/manager/notes/trash.js.map +1 -1
  176. package/dist/src/manager/notes/vault-index.js +2 -2
  177. package/dist/src/manager/notes/vault-index.js.map +1 -1
  178. package/dist/src/manager/notes/wiki-links.js +1 -1
  179. package/dist/src/manager/notes/wiki-links.js.map +1 -1
  180. package/dist/src/manager/notes/wiki-routes.js +84 -0
  181. package/dist/src/manager/notes/wiki-routes.js.map +1 -0
  182. package/dist/src/manager/proxy.js +1 -0
  183. package/dist/src/manager/proxy.js.map +1 -1
  184. package/dist/src/manager/routes/dashboard-memory.js +24 -1
  185. package/dist/src/manager/routes/dashboard-memory.js.map +1 -1
  186. package/dist/src/manager/routes/runtime-monitor.js +1 -1
  187. package/dist/src/manager/routes/runtime-monitor.js.map +1 -1
  188. package/dist/src/manager/scan.js +5 -3
  189. package/dist/src/manager/scan.js.map +1 -1
  190. package/dist/src/manager/server.js +20 -6
  191. package/dist/src/manager/server.js.map +1 -1
  192. package/dist/src/manager/telegram-hub/hub-bot.js +4 -3
  193. package/dist/src/manager/telegram-hub/hub-bot.js.map +1 -1
  194. package/dist/src/manager/worker-events.js +2 -1
  195. package/dist/src/manager/worker-events.js.map +1 -1
  196. package/dist/src/memory/heartbeat.js +6 -5
  197. package/dist/src/memory/heartbeat.js.map +1 -1
  198. package/dist/src/memory/indexing.js +162 -45
  199. package/dist/src/memory/indexing.js.map +1 -1
  200. package/dist/src/messaging/channel-validate.js +14 -1
  201. package/dist/src/messaging/channel-validate.js.map +1 -1
  202. package/dist/src/messaging/redact.js +2 -1
  203. package/dist/src/messaging/redact.js.map +1 -1
  204. package/dist/src/messaging/session-key.js +21 -2
  205. package/dist/src/messaging/session-key.js.map +1 -1
  206. package/dist/src/notes/constants.js.map +1 -0
  207. package/dist/src/notes/frontmatter.js +59 -0
  208. package/dist/src/notes/frontmatter.js.map +1 -0
  209. package/dist/src/notes/path-guards.js.map +1 -0
  210. package/dist/src/notes/search.js.map +1 -0
  211. package/dist/src/orchestrator/attestation.js +1 -1
  212. package/dist/src/orchestrator/attestation.js.map +1 -1
  213. package/dist/src/orchestrator/gateway.js +135 -31
  214. package/dist/src/orchestrator/gateway.js.map +1 -1
  215. package/dist/src/orchestrator/pipeline.js +53 -21
  216. package/dist/src/orchestrator/pipeline.js.map +1 -1
  217. package/dist/src/orchestrator/scope.js +36 -2
  218. package/dist/src/orchestrator/scope.js.map +1 -1
  219. package/dist/src/orchestrator/session-lanes.js +87 -0
  220. package/dist/src/orchestrator/session-lanes.js.map +1 -0
  221. package/dist/src/orchestrator/session-work.js +25 -0
  222. package/dist/src/orchestrator/session-work.js.map +1 -0
  223. package/dist/src/orchestrator/state-machine.js +1 -5
  224. package/dist/src/orchestrator/state-machine.js.map +1 -1
  225. package/dist/src/orchestrator/worker-registry.js +20 -10
  226. package/dist/src/orchestrator/worker-registry.js.map +1 -1
  227. package/dist/src/prompt/builder.js +40 -2
  228. package/dist/src/prompt/builder.js.map +1 -1
  229. package/dist/src/prompt/templates/a1-system.md +8 -1
  230. package/dist/src/routes/agent-control.js +44 -8
  231. package/dist/src/routes/agent-control.js.map +1 -1
  232. package/dist/src/routes/chat-sessions.js +34 -6
  233. package/dist/src/routes/chat-sessions.js.map +1 -1
  234. package/dist/src/routes/command.js +63 -8
  235. package/dist/src/routes/command.js.map +1 -1
  236. package/dist/src/routes/events.js +11 -2
  237. package/dist/src/routes/events.js.map +1 -1
  238. package/dist/src/routes/link-preview.js +1 -27
  239. package/dist/src/routes/link-preview.js.map +1 -1
  240. package/dist/src/routes/messages.js +6 -5
  241. package/dist/src/routes/messages.js.map +1 -1
  242. package/dist/src/routes/messaging.js +45 -1
  243. package/dist/src/routes/messaging.js.map +1 -1
  244. package/dist/src/routes/orchestrate.js +53 -33
  245. package/dist/src/routes/orchestrate.js.map +1 -1
  246. package/dist/src/routes/search.js +84 -0
  247. package/dist/src/routes/search.js.map +1 -0
  248. package/dist/src/routes/session-request.js +29 -0
  249. package/dist/src/routes/session-request.js.map +1 -0
  250. package/dist/src/routes/settings.js +110 -3
  251. package/dist/src/routes/settings.js.map +1 -1
  252. package/dist/src/routes/static.js +30 -8
  253. package/dist/src/routes/static.js.map +1 -1
  254. package/dist/src/routes/system.js +1 -1
  255. package/dist/src/routes/system.js.map +1 -1
  256. package/dist/src/routes/wiki.js +122 -0
  257. package/dist/src/routes/wiki.js.map +1 -0
  258. package/dist/src/search/contract.js +2 -0
  259. package/dist/src/search/contract.js.map +1 -0
  260. package/dist/src/search/coordinator.js +153 -0
  261. package/dist/src/search/coordinator.js.map +1 -0
  262. package/dist/src/search/provider.js +40 -0
  263. package/dist/src/search/provider.js.map +1 -0
  264. package/dist/src/search/providers/chat.js +115 -0
  265. package/dist/src/search/providers/chat.js.map +1 -0
  266. package/dist/src/search/providers/memory.js +53 -0
  267. package/dist/src/search/providers/memory.js.map +1 -0
  268. package/dist/src/search/providers/wiki.js +80 -0
  269. package/dist/src/search/providers/wiki.js.map +1 -0
  270. package/dist/src/slack/api.js +10 -1
  271. package/dist/src/slack/api.js.map +1 -1
  272. package/dist/src/slack/attachment-recovery.js +42 -0
  273. package/dist/src/slack/attachment-recovery.js.map +1 -0
  274. package/dist/src/slack/bot.js +120 -41
  275. package/dist/src/slack/bot.js.map +1 -1
  276. package/dist/src/slack/commands.js +24 -8
  277. package/dist/src/slack/commands.js.map +1 -1
  278. package/dist/src/slack/events.js +12 -1
  279. package/dist/src/slack/events.js.map +1 -1
  280. package/dist/src/slack/history.js +108 -0
  281. package/dist/src/slack/history.js.map +1 -0
  282. package/dist/src/slack/inbound-file.js +242 -0
  283. package/dist/src/slack/inbound-file.js.map +1 -0
  284. package/dist/src/slack/inbound-url.js +39 -0
  285. package/dist/src/slack/inbound-url.js.map +1 -0
  286. package/dist/src/slack/ingress.js +140 -0
  287. package/dist/src/slack/ingress.js.map +1 -0
  288. package/dist/src/slack/manifest.js +3 -0
  289. package/dist/src/slack/manifest.js.map +1 -1
  290. package/dist/src/slack/socket.js +1 -1
  291. package/dist/src/slack/socket.js.map +1 -1
  292. package/dist/src/slack/thread-tracker.js +86 -0
  293. package/dist/src/slack/thread-tracker.js.map +1 -0
  294. package/dist/src/telegram/bot.js +21 -11
  295. package/dist/src/telegram/bot.js.map +1 -1
  296. package/dist/src/wiki/config.js +193 -0
  297. package/dist/src/wiki/config.js.map +1 -0
  298. package/dist/src/wiki/entities.js +61 -0
  299. package/dist/src/wiki/entities.js.map +1 -0
  300. package/dist/src/wiki/ontology.js +72 -0
  301. package/dist/src/wiki/ontology.js.map +1 -0
  302. package/dist/src/wiki/prompt.js +172 -0
  303. package/dist/src/wiki/prompt.js.map +1 -0
  304. package/dist/src/wiki/safe-traversal.js +198 -0
  305. package/dist/src/wiki/safe-traversal.js.map +1 -0
  306. package/dist/src/wiki/scaffold.js +77 -0
  307. package/dist/src/wiki/scaffold.js.map +1 -0
  308. package/package.json +6 -5
  309. package/public/css/session-hub.css +266 -0
  310. package/public/dist/assets/AdvancedExport-DYGcCAUq.js +1 -0
  311. package/public/dist/assets/Agent-CNNQ7KkJ.js +1 -0
  312. package/public/dist/assets/{DesignPanel-BGMPhZSx.js → DesignPanel-DcL369nB.js} +1 -1
  313. package/public/dist/assets/{DocPanel-Dz-J_l5M.js → DocPanel-K8A2pm_1.js} +1 -1
  314. package/public/dist/assets/{FolderPanel-DbPhzzvi.js → FolderPanel-CV52EzwG.js} +1 -1
  315. package/public/dist/assets/{MilkdownWysiwygEditor-Em7iC6y9.js → MilkdownWysiwygEditor-BKOCDI4Z.js} +1 -1
  316. package/public/dist/assets/api-B25zNNFJ.js +1 -0
  317. package/public/dist/assets/{app-DX4e6isX.css → app-DjgGeXhJ.css} +1 -1
  318. package/public/dist/assets/app-Dsgp4xpU.js +55 -0
  319. package/public/dist/assets/bgtask-badge-C6h5C-o4.js +1 -0
  320. package/public/dist/assets/{bgtask-badge-qMkKOZx0.js → bgtask-badge-CpavV0Jc.js} +4 -4
  321. package/public/dist/assets/{employees-BnobWM_2.js → employees-BJyEgim7.js} +4 -4
  322. package/public/dist/assets/idb-cache-B5PX63p7.js +1 -0
  323. package/public/dist/assets/{idb-cache-BzzftUET.js → idb-cache-lOntwU3S.js} +1 -1
  324. package/public/dist/assets/{iframe-renderer-C5vRbbl9.js → iframe-renderer-B8k3AvTv.js} +12 -12
  325. package/public/dist/assets/iframe-renderer-BO_GgCTR.js +1 -0
  326. package/public/dist/assets/manager-LkGs6qEJ.css +1 -0
  327. package/public/dist/assets/manager-kXYQyMhR.js +13 -0
  328. package/public/dist/assets/{memory-DF4kPwwC.js → memory-D2RbBVO-.js} +11 -11
  329. package/public/dist/assets/memory-amsvg4Ey.js +1 -0
  330. package/public/dist/assets/message-history-BGHZxvgV.js +1 -0
  331. package/public/dist/assets/message-history-Bw2E_b8L.js +1 -0
  332. package/public/dist/assets/pi-settings-5HVg1sXJ.js +1 -0
  333. package/public/dist/assets/render-BkGcpH7z.js +157 -0
  334. package/public/dist/assets/settings-B1FPORO2.js +56 -0
  335. package/public/dist/assets/settings-CMN-zN4Q.js +1 -0
  336. package/public/dist/assets/settings-core-B_OjGYVN.js +1 -0
  337. package/public/dist/assets/settings-core-CxRwqx86.js +144 -0
  338. package/public/dist/assets/sidebar-DW78DANx.js +49 -0
  339. package/public/dist/assets/skills-Dz-9Yrxr.js +1 -0
  340. package/public/dist/assets/{skills-IGquen_j.js → skills-lWi0ZFiV.js} +6 -6
  341. package/public/dist/assets/{slash-commands-ZRDOL99E.js → slash-commands-DYOI6nKj.js} +1 -1
  342. package/public/dist/assets/slash-commands-xJFCdTTM.js +1 -0
  343. package/public/dist/assets/{trace-drawer-D8To8fYt.js → trace-drawer-DBX9xzmt.js} +1 -1
  344. package/public/dist/assets/{ui-Hx_mNoDf.js → ui-Bx7eqnXt.js} +1 -1
  345. package/public/dist/assets/ui-JOcrYeG-.js +1 -0
  346. package/public/dist/assets/{wiki-link-suggestions-DZQc78gj.js → wiki-link-suggestions-Dp6kBQVu.js} +1 -1
  347. package/public/dist/index.html +4 -4
  348. package/public/dist/manager/index.html +2 -2
  349. package/public/index.html +3 -2
  350. package/public/js/event-channel.ts +25 -0
  351. package/public/js/features/channel-onboarding-flow.ts +16 -1
  352. package/public/js/features/channel-onboarding.ts +20 -2
  353. package/public/js/features/chat-messages.ts +3 -1
  354. package/public/js/features/chat-search.ts +2 -1
  355. package/public/js/features/chat.ts +56 -18
  356. package/public/js/features/elicitation.ts +15 -0
  357. package/public/js/features/idb-cache.ts +34 -0
  358. package/public/js/features/message-history.ts +2 -1
  359. package/public/js/features/session-hub.ts +334 -0
  360. package/public/js/features/settings-cli-status-render.ts +1 -1
  361. package/public/js/features/settings-cli-status.ts +25 -11
  362. package/public/js/features/settings-core.ts +72 -3
  363. package/public/js/features/settings-types.ts +32 -0
  364. package/public/js/features/ui-status.ts +2 -1
  365. package/public/js/main.ts +20 -0
  366. package/public/js/render/compose-block.ts +7 -0
  367. package/public/js/ws.ts +22 -2
  368. package/public/locales/en.json +46 -4
  369. package/public/locales/ja.json +46 -4
  370. package/public/locales/ko.json +46 -4
  371. package/public/locales/zh.json +46 -4
  372. package/public/manager/src/App.tsx +1 -1
  373. package/public/manager/src/InstancePreview.tsx +5 -1
  374. package/public/manager/src/api.ts +2 -1
  375. package/public/manager/src/components/InstanceGroups.tsx +10 -0
  376. package/public/manager/src/components/InstanceListContent.tsx +24 -1
  377. package/public/manager/src/components/InstanceRow.tsx +33 -3
  378. package/public/manager/src/components/InstanceSessionList.tsx +132 -0
  379. package/public/manager/src/hooks/useActiveSessionDisclosure.ts +45 -0
  380. package/public/manager/src/lib/resilient-fetch.ts +93 -0
  381. package/public/manager/src/lib/session-store.ts +216 -0
  382. package/public/manager/src/manager-components.css +97 -0
  383. package/public/manager/src/settings/pages/AdvancedExport.tsx +12 -2
  384. package/public/manager/src/settings/pages/Agent.tsx +113 -1
  385. package/public/manager/src/settings/pages/components/agent/agent-meta.ts +1 -1
  386. package/scripts/bundle-sidecar.sh +50 -2
  387. package/scripts/burst-scenario.mjs +72 -0
  388. package/scripts/check-redaction-sinks.mjs +1 -0
  389. package/scripts/check-strict-baseline.mjs +1 -1
  390. package/scripts/ensure-native-modules.cjs +50 -3
  391. package/scripts/install-risk-gate.mjs +8 -0
  392. package/scripts/install-wsl.sh +27 -2
  393. package/scripts/install.ps1 +121 -0
  394. package/scripts/install.sh +28 -2
  395. package/scripts/postinstall-guard.cjs +44 -2
  396. package/scripts/release-gates.mjs +12 -0
  397. package/dist/src/manager/notes/constants.js.map +0 -1
  398. package/dist/src/manager/notes/path-guards.js.map +0 -1
  399. package/dist/src/manager/notes/search.js.map +0 -1
  400. package/public/dist/assets/AdvancedExport-DsDpqXoH.js +0 -1
  401. package/public/dist/assets/Agent-BjbZVZ9p.js +0 -1
  402. package/public/dist/assets/api-C9dOxby7.js +0 -1
  403. package/public/dist/assets/app-BzD_ZLvt.js +0 -55
  404. package/public/dist/assets/bgtask-badge-Dqbp-Y4e.js +0 -1
  405. package/public/dist/assets/idb-cache-ZH6lOebk.js +0 -1
  406. package/public/dist/assets/iframe-renderer-Df4KIVyx.js +0 -1
  407. package/public/dist/assets/manager-C4eNDNFt.css +0 -1
  408. package/public/dist/assets/manager-DaN201mN.js +0 -13
  409. package/public/dist/assets/memory-D-bULTHl.js +0 -1
  410. package/public/dist/assets/message-history-C4WOisGx.js +0 -1
  411. package/public/dist/assets/message-history-ZBoza6ew.js +0 -1
  412. package/public/dist/assets/pi-settings-CBUGDAuT.js +0 -1
  413. package/public/dist/assets/render-DYX6GRQ0.js +0 -122
  414. package/public/dist/assets/settings-DbJpGoaX.js +0 -55
  415. package/public/dist/assets/settings-SreihUje.js +0 -1
  416. package/public/dist/assets/settings-core-B1Y5Nb4Z.js +0 -1
  417. package/public/dist/assets/settings-core-DobZWIZ7.js +0 -140
  418. package/public/dist/assets/sidebar-Cp22g0Gv.js +0 -49
  419. package/public/dist/assets/skills-DwPIP08e.js +0 -1
  420. package/public/dist/assets/slash-commands-BBVfwcnW.js +0 -1
  421. package/public/dist/assets/ui-CAicpUZc.js +0 -1
  422. /package/dist/src/{manager/notes → notes}/constants.js +0 -0
  423. /package/dist/src/{manager/notes → notes}/path-guards.js +0 -0
  424. /package/dist/src/{manager/notes → notes}/search.js +0 -0
  425. /package/public/dist/assets/{pi-settings-Dh5lgb87.js → pi-settings-DEuKgvIj.js} +0 -0
@@ -2,24 +2,44 @@
2
2
  import crypto from 'node:crypto';
3
3
  import { groupQueueKey } from '../../messaging/session-key.js';
4
4
  import { stripUndefined } from '../../core/strip-undefined.js';
5
+ import { withSessionScope } from '../../core/session-context.js';
6
+ import { sessionLanes } from '../../orchestrator/session-lanes.js';
7
+ import { scopeForChatSession } from '../../orchestrator/scope.js';
5
8
  export const FALLBACK_MAX_RETRIES = 3;
6
- export function createQueueController(deps) {
9
+ export function createQueueController(deps, lanes = sessionLanes) {
10
+ const multiSessionEnabled = deps.isMultiSessionEnabled();
11
+ const localSessionScopeEnabled = deps.isLocalSessionScopeEnabled?.() === true;
12
+ if (multiSessionEnabled)
13
+ deps.migrateQueuedMessagesV1ToV2();
7
14
  function normalizeQueueItem(row) {
8
15
  try {
9
16
  const parsed = JSON.parse(row.payload);
10
17
  if (typeof parsed?.id !== 'string' || typeof parsed?.prompt !== 'string' || typeof parsed?.source !== 'string') {
11
18
  return [];
12
19
  }
20
+ const chatSessionId = typeof parsed.chatSessionId === 'string' ? parsed.chatSessionId : 'default';
21
+ const remoteKey = typeof parsed.remoteKey === 'string' ? parsed.remoteKey : undefined;
22
+ const persistedScope = multiSessionEnabled && typeof parsed.scope === 'string' ? parsed.scope : 'default';
23
+ const scope = persistedScope === 'default'
24
+ ? scopeForChatSession(chatSessionId, remoteKey, localSessionScopeEnabled)
25
+ : persistedScope;
13
26
  return [stripUndefined({
27
+ ...(multiSessionEnabled ? { schemaVersion: 2 } : {}),
14
28
  id: parsed.id,
15
29
  prompt: parsed.prompt,
16
30
  source: parsed.source,
17
- scope: 'default',
31
+ scope,
32
+ ...(multiSessionEnabled ? {
33
+ chatSessionId,
34
+ ...(remoteKey ? { remoteKey } : {}),
35
+ } : {}),
18
36
  target: parsed.target,
19
37
  chatId: parsed.chatId,
20
38
  requestId: parsed.requestId,
21
39
  overrides: parsed.overrides,
22
40
  replyViaTarget: parsed.replyViaTarget === true,
41
+ ...(multiSessionEnabled && parsed.collect === true ? { collect: true } : {}),
42
+ ...(multiSessionEnabled && parsed.priority === 'head' ? { priority: 'head' } : {}),
23
43
  ts: typeof parsed.ts === 'number' ? parsed.ts : Date.now(),
24
44
  })];
25
45
  }
@@ -28,83 +48,133 @@ export function createQueueController(deps) {
28
48
  }
29
49
  }
30
50
  function loadPersistedQueue() {
31
- return deps.listQueuedMessages.all().flatMap(normalizeQueueItem);
51
+ const recovered = deps.listQueuedMessages.all().flatMap(normalizeQueueItem);
52
+ if (!multiSessionEnabled)
53
+ return recovered;
54
+ return [
55
+ ...recovered.filter(item => item.priority === 'head'),
56
+ ...recovered.filter(item => item.priority !== 'head'),
57
+ ];
32
58
  }
33
59
  const messageQueue = loadPersistedQueue();
34
60
  if (messageQueue.length > 0) {
35
61
  console.log(`[queue] recovered ${messageQueue.length} persisted message(s) from previous session`);
36
62
  }
37
- let queueProcessing = false;
38
- // ─── 429 Retry Timer State ──────────────────────────
39
- // INVARIANT: single-main — 동시에 1개의 main spawnAgent만 존재한다고 가정.
40
- // 멀티 main task 도입 시 request-id 키 맵으로 전환 필요.
41
- let retryPendingTimer = null;
42
- let retryPendingResolve = null;
43
- let retryPendingOrigin = null;
44
- let retryPendingIsEmployee = false;
45
- const fallbackState = new Map();
46
- let queueHoldId = null;
47
- let queueHoldTimer = null;
48
63
  const QUEUE_HOLD_TIMEOUT_MS = 10_000;
49
- function clearRetryTimer(resumeQueue = true) {
50
- if (retryPendingTimer) {
51
- clearTimeout(retryPendingTimer);
52
- retryPendingTimer = null;
53
- console.log('[jaw:retry] timer cancelled');
54
- if (retryPendingResolve) {
64
+ const drainingScopes = new Set();
65
+ const retryByScope = new Map();
66
+ const fallbackByScope = new Map();
67
+ const holdByScope = new Map();
68
+ const scheduledItemIds = new Set();
69
+ const normalizeScope = (scopeKey = 'default') => multiSessionEnabled ? scopeKey : 'default';
70
+ function retryEntry(scopeKey) {
71
+ const scope = normalizeScope(scopeKey);
72
+ let state = retryByScope.get(scope);
73
+ if (!state) {
74
+ state = { timer: null, resolve: null, origin: null, isEmployee: false };
75
+ retryByScope.set(scope, state);
76
+ }
77
+ return state;
78
+ }
79
+ function fallbackStateForScope(scopeKey) {
80
+ const scope = normalizeScope(scopeKey);
81
+ let state = fallbackByScope.get(scope);
82
+ if (!state) {
83
+ state = new Map();
84
+ fallbackByScope.set(scope, state);
85
+ }
86
+ return state;
87
+ }
88
+ function clearRetryTimer(scopeKeyOrResume = 'default', resumeQueue = true) {
89
+ const legacyCall = typeof scopeKeyOrResume === 'boolean';
90
+ const scope = normalizeScope(legacyCall ? 'default' : scopeKeyOrResume);
91
+ const shouldResume = legacyCall ? scopeKeyOrResume : resumeQueue;
92
+ const state = retryByScope.get(scope);
93
+ if (state?.timer) {
94
+ clearTimeout(state.timer);
95
+ state.timer = null;
96
+ console.log(`[jaw:retry] timer cancelled scope=${scope}`);
97
+ if (state.resolve) {
55
98
  deps.broadcast('agent_done', {
56
99
  text: '⏹️ 재시도 취소됨',
57
100
  error: true,
58
- origin: retryPendingOrigin || 'web',
59
- ...(retryPendingIsEmployee ? { isEmployee: true } : {}),
60
- }, retryPendingIsEmployee ? 'internal' : 'public');
61
- retryPendingResolve({ text: '', code: -1 });
62
- retryPendingResolve = null;
63
- retryPendingOrigin = null;
64
- retryPendingIsEmployee = false;
101
+ origin: state.origin || 'web',
102
+ ...(state.isEmployee ? { isEmployee: true } : {}),
103
+ }, state.isEmployee ? 'internal' : 'public');
104
+ state.resolve({ text: '', code: -1 });
105
+ state.resolve = null;
106
+ state.origin = null;
107
+ state.isEmployee = false;
65
108
  }
66
- if (resumeQueue)
67
- processQueue();
109
+ if (shouldResume)
110
+ void processQueue(scope);
68
111
  }
69
112
  }
70
- function resetFallbackState() {
71
- clearRetryTimer(true);
72
- fallbackState.clear();
113
+ function resetFallbackState(scopeKey = 'default') {
114
+ if (scopeKey === null) {
115
+ for (const scope of retryByScope.keys())
116
+ clearRetryTimer(scope, false);
117
+ retryByScope.clear();
118
+ fallbackByScope.clear();
119
+ }
120
+ else {
121
+ const scope = normalizeScope(scopeKey);
122
+ clearRetryTimer(scope, false);
123
+ retryByScope.delete(scope);
124
+ fallbackByScope.delete(scope);
125
+ }
73
126
  console.log('[jaw:fallback] state reset');
74
127
  }
75
- function getFallbackState() {
76
- return Object.fromEntries(fallbackState);
128
+ function getFallbackState(scopeKey = 'default') {
129
+ if (scopeKey === null) {
130
+ return Object.fromEntries([...fallbackByScope].map(([scope, state]) => [scope, Object.fromEntries(state)]));
131
+ }
132
+ return Object.fromEntries(fallbackStateForScope(scopeKey));
77
133
  }
78
- function setQueueHold(id, timeoutMs = QUEUE_HOLD_TIMEOUT_MS) {
79
- if (queueHoldId && queueHoldId !== id)
80
- clearQueueHold();
81
- queueHoldId = id;
82
- if (queueHoldTimer)
83
- clearTimeout(queueHoldTimer);
84
- const holdId = id;
85
- queueHoldTimer = setTimeout(() => {
86
- if (queueHoldId !== holdId)
134
+ function setQueueHold(scopeKey, idOrTimeout, timeoutMs = QUEUE_HOLD_TIMEOUT_MS) {
135
+ const legacyCall = typeof idOrTimeout !== 'string';
136
+ const scope = normalizeScope(legacyCall ? 'default' : scopeKey);
137
+ const id = legacyCall ? scopeKey : idOrTimeout;
138
+ const timeout = typeof idOrTimeout === 'number' ? idOrTimeout : timeoutMs;
139
+ const previous = holdByScope.get(scope);
140
+ if (previous)
141
+ clearTimeout(previous.timer);
142
+ const timer = setTimeout(() => {
143
+ if (holdByScope.get(scope)?.id !== id)
87
144
  return;
88
- console.warn(`[queue:hold] hold for ${holdId} expired after ${timeoutMs}ms`);
89
- clearQueueHold();
90
- }, timeoutMs);
91
- console.log(`[queue:hold] set for ${id}`);
145
+ console.warn(`[queue:hold] hold for ${id} expired after ${timeout}ms scope=${scope}`);
146
+ clearQueueHold(scope, id);
147
+ }, timeout);
148
+ holdByScope.set(scope, { id, timer });
149
+ console.log(`[queue:hold] set for ${id} scope=${scope}`);
92
150
  }
93
- function clearQueueHold(id, opts) {
94
- if (id && queueHoldId !== id)
151
+ function clearQueueHold(scopeKey = 'default', idOrOpts, opts) {
152
+ const legacyCall = typeof idOrOpts === 'object' || (arguments.length === 1 && typeof scopeKey === 'string');
153
+ const requestedId = legacyCall ? scopeKey ?? undefined : idOrOpts;
154
+ const clearOpts = typeof idOrOpts === 'object' ? idOrOpts : opts;
155
+ let scope;
156
+ if (scopeKey === null) {
157
+ if (!requestedId)
158
+ return;
159
+ const match = [...holdByScope].find(([, hold]) => hold.id === requestedId);
160
+ if (!match)
161
+ return;
162
+ scope = match[0];
163
+ }
164
+ else {
165
+ scope = normalizeScope(legacyCall ? 'default' : scopeKey);
166
+ }
167
+ const hold = holdByScope.get(scope);
168
+ if (!hold || (requestedId && hold.id !== requestedId))
95
169
  return;
96
- if (queueHoldTimer)
97
- clearTimeout(queueHoldTimer);
98
- queueHoldTimer = null;
99
- const hadHold = queueHoldId !== null;
100
- if (hadHold)
101
- console.log(`[queue:hold] cleared (was ${queueHoldId})`);
102
- queueHoldId = null;
103
- if (hadHold && (opts?.resume ?? true))
104
- queueMicrotask(() => processQueue());
170
+ clearTimeout(hold.timer);
171
+ holdByScope.delete(scope);
172
+ console.log(`[queue:hold] cleared (was ${hold.id}) scope=${scope}`);
173
+ if (clearOpts?.resume ?? true)
174
+ queueMicrotask(() => { void processQueue(scope); });
105
175
  }
106
- function getQueueHoldId() {
107
- return queueHoldId;
176
+ function getQueueHoldId(scopeKey = 'default') {
177
+ return holdByScope.get(normalizeScope(scopeKey))?.id ?? null;
108
178
  }
109
179
  function getQueuedMessageSnapshotForScope(scope) {
110
180
  return messageQueue
@@ -116,10 +186,11 @@ export function createQueueController(deps) {
116
186
  ts: item.ts,
117
187
  }));
118
188
  }
119
- function queueUpdatePayload() {
189
+ function queueUpdatePayload(scope = 'default') {
120
190
  return {
121
191
  pending: messageQueue.length,
122
- queued: getQueuedMessageSnapshotForScope('default'),
192
+ queued: getQueuedMessageSnapshotForScope(scope),
193
+ ...(multiSessionEnabled ? { scope } : {}),
123
194
  };
124
195
  }
125
196
  function drainHeartbeatPendingSoon() {
@@ -134,6 +205,7 @@ export function createQueueController(deps) {
134
205
  if (idx === -1)
135
206
  return { removed: null, pending: messageQueue.length };
136
207
  const [removed] = messageQueue.splice(idx, 1);
208
+ scheduledItemIds.delete(id);
137
209
  try {
138
210
  deps.deleteQueuedMessage.run(id);
139
211
  }
@@ -141,133 +213,206 @@ export function createQueueController(deps) {
141
213
  console.warn(`[queue] DB delete failed for ${id}:`, err.message);
142
214
  }
143
215
  console.log(`[queue] -1 (${messageQueue.length} pending) removed=${id}`);
144
- deps.broadcast('queue_update', queueUpdatePayload());
216
+ deps.broadcast('queue_update', queueUpdatePayload(removed.scope));
145
217
  return { removed: removed, pending: messageQueue.length };
146
218
  }
147
219
  function enqueueMessage(prompt, source, meta) {
148
220
  const item = stripUndefined({
221
+ ...(multiSessionEnabled ? { schemaVersion: 2 } : {}),
149
222
  id: crypto.randomUUID(),
150
223
  prompt,
151
224
  source,
152
- scope: meta?.scope || 'default',
225
+ scope: multiSessionEnabled ? (meta?.scope || 'default') : 'default',
226
+ ...(multiSessionEnabled ? {
227
+ chatSessionId: meta?.chatSessionId || deps.getActiveChatSession(),
228
+ ...(meta?.remoteKey ? { remoteKey: meta.remoteKey } : {}),
229
+ } : {}),
153
230
  target: meta?.target,
154
231
  chatId: meta?.chatId,
155
232
  requestId: meta?.requestId,
156
233
  overrides: meta?.overrides,
157
234
  replyViaTarget: meta?.replyViaTarget,
235
+ ...(multiSessionEnabled && meta?.collect === true ? { collect: true } : {}),
236
+ ...(multiSessionEnabled && meta?.front === true ? { priority: 'head' } : {}),
158
237
  ts: Date.now(),
159
238
  });
160
239
  deps.insertQueuedMessage.run(item.id, JSON.stringify(item));
161
- messageQueue.push(item);
240
+ if (multiSessionEnabled && meta?.front === true)
241
+ messageQueue.unshift(item);
242
+ else
243
+ messageQueue.push(item);
162
244
  console.log(`[queue] +1 (${messageQueue.length} pending)`);
163
- deps.broadcast('queue_update', queueUpdatePayload());
164
- processQueue();
245
+ deps.broadcast('queue_update', queueUpdatePayload(item.scope));
246
+ void processQueue(item.scope);
165
247
  return item.id;
166
248
  }
167
- // Queue policy: "fair" — batch head runs, tail goes after remaining
168
- async function processQueue() {
169
- if (queueProcessing)
170
- return;
171
- if (!deps.isSpawnBusy() && !deps.hasBlockingWorkers() && deps.hasPendingWorkerReplays()) {
249
+ // Queue policy: "fair" — batch head runs, tail goes after remaining.
250
+ // Pending worker replays are intentionally not gated here: orchestrate()
251
+ // drains them at entry, avoiding the documented processQueue deadlock.
252
+ async function processQueue(scopeKey = 'default') {
253
+ const requestedScope = normalizeScope(scopeKey);
254
+ if (!deps.isSpawnBusy(requestedScope)
255
+ && !deps.hasBlockingWorkers(requestedScope)
256
+ && deps.hasPendingWorkerReplays(requestedScope)) {
172
257
  queueMicrotask(() => {
173
258
  deps.importPipeline()
174
- .then(({ drainPendingReplays }) => drainPendingReplays({ origin: 'system' }))
259
+ .then(({ drainPendingReplays }) => drainPendingReplays(requestedScope, { origin: 'system' }))
175
260
  .catch(err => console.error('[processQueue:drain]', err.message));
176
261
  });
177
262
  }
178
- if (!deps.isSpawnBusy()
179
- && !deps.hasBlockingWorkers()
180
- && !deps.hasPendingWorkerReplays()
263
+ if (!deps.isSpawnBusy(requestedScope)
264
+ && !deps.hasBlockingWorkers(requestedScope)
265
+ && !deps.hasPendingWorkerReplays(requestedScope)
181
266
  && messageQueue.length === 0
182
- && !queueHoldId) {
267
+ && !holdByScope.has(requestedScope)) {
183
268
  drainHeartbeatPendingSoon();
184
269
  }
185
- if (deps.isSpawnBusy()
186
- || deps.hasBlockingWorkers()
187
- || messageQueue.length === 0
188
- || queueHoldId)
189
- return;
190
- // NOTE: hasPendingWorkerReplays() is intentionally NOT gated here —
191
- // orchestrate() drains pending replays at entry (pipeline.ts drainPendingReplays),
192
- // so the queued user message still arrives AFTER the worker result. Keeping this
193
- // gate caused a deadlock (see devlog/_plan/260417_message_duplication/02_*).
194
- queueProcessing = true;
195
- const first = messageQueue[0];
196
- const groupKey = groupQueueKey(first.source, first.target);
197
- const batch = [];
198
- const remaining = [];
199
- for (const m of messageQueue) {
200
- const key = groupQueueKey(m.source, m.target);
201
- if (key === groupKey)
202
- batch.push(m);
203
- else
204
- remaining.push(m);
205
- }
206
- messageQueue.length = 0;
207
- if (batch.length > 1) {
208
- messageQueue.push(...remaining, ...batch.slice(1));
209
- }
210
- else {
211
- messageQueue.push(...remaining);
270
+ let item;
271
+ for (const candidate of messageQueue) {
272
+ const candidateScope = normalizeScope(candidate.scope);
273
+ if (scheduledItemIds.has(candidate.id) || drainingScopes.has(candidateScope) || holdByScope.has(candidateScope))
274
+ continue;
275
+ if (deps.isSpawnBusy(candidateScope) || deps.hasBlockingWorkers(candidateScope))
276
+ continue;
277
+ if (deps.hasPendingWorkerReplays(candidateScope)) {
278
+ queueMicrotask(() => {
279
+ deps.importPipeline()
280
+ .then(({ drainPendingReplays }) => drainPendingReplays(candidateScope, { origin: 'system' }))
281
+ .catch(err => console.error('[processQueue:drain]', err.message));
282
+ });
283
+ continue;
284
+ }
285
+ item = candidate;
286
+ break;
212
287
  }
213
- const item = batch[0];
214
- const combined = item.prompt;
215
- const source = item.source;
216
- const target = item.target;
217
- const chatId = item.chatId;
218
- const requestId = item.requestId;
219
- const overrides = item.overrides;
220
- const replyViaTarget = item.replyViaTarget;
221
- const origin = source || 'web';
222
- console.log(`[queue] processing 1/${batch.length} message(s) for ${groupKey}, ${messageQueue.length} remaining`);
223
- let inserted = false;
224
- try {
225
- deps.insertMessage.run('user', combined, source, '', deps.getWorkingDir(), deps.getActiveChatSession());
226
- deps.deleteQueuedMessage.run(item.id);
227
- inserted = true;
228
- deps.broadcast('new_message', { role: 'user', content: combined, source, fromQueue: true });
229
- deps.broadcast('queue_update', queueUpdatePayload());
230
- const { orchestrate, orchestrateContinue, orchestrateReset, isContinueIntent, isResetIntent } = await deps.importPipeline();
231
- const task = isResetIntent(combined)
232
- ? orchestrateReset({ origin, target, chatId, requestId, overrides, replyViaTarget, _skipInsert: true })
233
- : isContinueIntent(combined)
234
- ? orchestrateContinue({ origin, target, chatId, requestId, overrides, replyViaTarget, _skipInsert: true })
235
- : orchestrate(combined, { origin, target, chatId, requestId, overrides, replyViaTarget, _skipInsert: true });
236
- try {
237
- await task;
288
+ if (!item)
289
+ return;
290
+ const itemScope = normalizeScope(item.scope);
291
+ drainingScopes.add(itemScope);
292
+ scheduledItemIds.add(item.id);
293
+ queueMicrotask(() => { void processQueue(itemScope); });
294
+ await lanes.runDetachedTurn(itemScope, async () => {
295
+ const liveIndex = messageQueue.findIndex(candidate => candidate.id === item.id);
296
+ if (liveIndex === -1) {
297
+ scheduledItemIds.delete(item.id);
298
+ drainingScopes.delete(itemScope);
299
+ queueMicrotask(() => { void processQueue(itemScope); });
300
+ return;
238
301
  }
239
- catch (err) {
240
- const msg = err.message;
241
- console.error('[queue:orchestrate]', msg);
242
- deps.broadcast('orchestrate_done', { text: `[error] ${msg}`, error: true, origin, chatId, target, requestId, replyViaTarget });
302
+ const collectedItems = multiSessionEnabled && item.collect
303
+ ? messageQueue.filter(candidate => normalizeScope(candidate.scope) === itemScope && candidate.collect === true && !scheduledItemIds.has(candidate.id))
304
+ : [];
305
+ const runItems = [item, ...collectedItems];
306
+ const runIds = new Set(runItems.map(candidate => candidate.id));
307
+ for (let index = messageQueue.length - 1; index >= 0; index--) {
308
+ if (runIds.has(messageQueue[index].id))
309
+ messageQueue.splice(index, 1);
243
310
  }
244
- }
245
- catch (setupErr) {
246
- console.error('[queue:setup]', setupErr);
247
- if (!inserted) {
248
- messageQueue.unshift(item);
311
+ for (const runItem of runItems)
312
+ scheduledItemIds.delete(runItem.id);
313
+ const groupKey = groupQueueKey(item.source, item.target);
314
+ const combined = runItems.map(candidate => candidate.prompt).join('\n\n');
315
+ const source = item.source;
316
+ const target = item.target;
317
+ const chatId = item.chatId;
318
+ const requestId = item.requestId;
319
+ const overrides = item.overrides;
320
+ const replyViaTarget = item.replyViaTarget;
321
+ const origin = source || 'web';
322
+ console.log(`[queue] processing message for ${groupKey}, ${messageQueue.length} remaining`);
323
+ const effectiveSessionId = multiSessionEnabled
324
+ ? (item.chatSessionId || deps.getActiveChatSession())
325
+ : deps.getActiveChatSession();
326
+ let inserted = false;
327
+ try {
328
+ const sessionScope = { scope: item.scope, chatSessionId: effectiveSessionId };
329
+ const eventScope = multiSessionEnabled
330
+ ? { scope: item.scope, sessionId: effectiveSessionId }
331
+ : undefined;
332
+ deps.insertMessage.run('user', combined, source, '', deps.getWorkingDir(), effectiveSessionId);
333
+ inserted = true;
334
+ for (const runItem of runItems) {
335
+ try {
336
+ deps.deleteQueuedMessage.run(runItem.id);
337
+ }
338
+ catch (err) {
339
+ console.warn(`[queue] DB delete failed for ${runItem.id}:`, err.message);
340
+ }
341
+ }
342
+ if (multiSessionEnabled) {
343
+ deps.broadcast('new_message', { role: 'user', content: combined, source, fromQueue: true, ...eventScope });
344
+ }
345
+ else {
346
+ deps.broadcast('new_message', { role: 'user', content: combined, source, fromQueue: true });
347
+ }
348
+ deps.broadcast('queue_update', queueUpdatePayload(item.scope));
349
+ await withSessionScope(sessionScope, async () => {
350
+ const { orchestrate, orchestrateContinue, orchestrateReset, isContinueIntent, isResetIntent } = await deps.importPipeline();
351
+ const scopedMeta = stripUndefined({
352
+ origin, target, chatId, requestId, scope: item.scope,
353
+ chatSessionId: effectiveSessionId,
354
+ ...(item.remoteKey ? { remoteKey: item.remoteKey } : {}),
355
+ overrides, replyViaTarget, _skipInsert: true,
356
+ });
357
+ const task = isResetIntent(combined)
358
+ ? orchestrateReset(scopedMeta)
359
+ : isContinueIntent(combined)
360
+ ? orchestrateContinue(scopedMeta)
361
+ : orchestrate(combined, scopedMeta);
362
+ try {
363
+ await task;
364
+ }
365
+ catch (err) {
366
+ const msg = err.message;
367
+ console.error('[queue:orchestrate]', msg);
368
+ deps.broadcast('orchestrate_done', { text: `[error] ${msg}`, error: true, origin, chatId, target, requestId, replyViaTarget, ...(eventScope || {}) });
369
+ }
370
+ });
249
371
  }
250
- else {
251
- deps.broadcast('orchestrate_done', { text: `[error] setup failed: ${setupErr.message}`, error: true, origin, chatId, target, requestId, replyViaTarget });
372
+ catch (setupErr) {
373
+ console.error('[queue:setup]', setupErr);
374
+ if (!inserted) {
375
+ messageQueue.unshift(...runItems);
376
+ }
377
+ else {
378
+ deps.broadcast('orchestrate_done', { text: `[error] setup failed: ${setupErr.message}`, error: true, origin, chatId, target, requestId, replyViaTarget,
379
+ ...(multiSessionEnabled ? { scope: item.scope, sessionId: effectiveSessionId } : {}) });
380
+ }
252
381
  }
253
- }
254
- finally {
255
- queueProcessing = false;
256
- queueMicrotask(() => processQueue());
257
- }
382
+ finally {
383
+ for (const runItem of runItems)
384
+ scheduledItemIds.delete(runItem.id);
385
+ drainingScopes.delete(itemScope);
386
+ queueMicrotask(() => { void processQueue(itemScope); });
387
+ }
388
+ });
258
389
  }
259
- function purgeQueueOnStop(reason) {
260
- if (messageQueue.length === 0)
390
+ function purgeQueueOnStop(scopeKey, reason) {
391
+ const scope = scopeKey === null ? null : normalizeScope(scopeKey);
392
+ const droppedItems = messageQueue.filter(item => scope === null || normalizeScope(item.scope) === scope);
393
+ if (droppedItems.length === 0)
261
394
  return;
262
- const dropped = messageQueue.length;
263
- for (const item of messageQueue.splice(0)) {
395
+ const droppedIds = new Set(droppedItems.map(item => item.id));
396
+ const scopes = new Set(droppedItems.map(item => item.scope));
397
+ for (let index = messageQueue.length - 1; index >= 0; index--) {
398
+ const item = messageQueue[index];
399
+ if (!droppedIds.has(item.id))
400
+ continue;
401
+ messageQueue.splice(index, 1);
402
+ scheduledItemIds.delete(item.id);
264
403
  try {
265
404
  deps.deleteQueuedMessage.run(item.id);
266
405
  }
267
406
  catch { /* best-effort */ }
268
407
  }
269
- console.log(`[jaw:stop] cleared ${dropped} pending message(s) (reason=${reason})`);
270
- deps.broadcast('queue_update', queueUpdatePayload());
408
+ console.log(`[jaw:stop] cleared ${droppedItems.length} pending message(s) (reason=${reason})`);
409
+ if (multiSessionEnabled) {
410
+ for (const scope of scopes)
411
+ deps.broadcast('queue_update', queueUpdatePayload(scope));
412
+ }
413
+ else {
414
+ deps.broadcast('queue_update', queueUpdatePayload());
415
+ }
271
416
  }
272
417
  return {
273
418
  enqueueMessage,
@@ -276,23 +421,35 @@ export function createQueueController(deps) {
276
421
  setQueueHold,
277
422
  clearQueueHold,
278
423
  getQueueHoldId,
424
+ // The gate is read once at construction, so every scope collapses onto 'default'
425
+ // for the whole process life when it is off. Callers that build a scope key must
426
+ // ask this instead of re-reading settings, or they look at a lane the queue never
427
+ // uses (a live-settings read made session deletion miss real work).
428
+ isScopedQueue: () => multiSessionEnabled,
279
429
  clearRetryTimer,
280
430
  resetFallbackState,
281
431
  getFallbackState,
282
432
  getQueuedMessageSnapshotForScope,
283
433
  messageQueue,
284
- fallbackState,
285
- isRetryPending: () => !!retryPendingTimer,
286
- isQueueBusy: () => queueProcessing,
434
+ fallbackStateForScope,
435
+ isRetryPending: (scopeKey = 'default') => scopeKey === null
436
+ ? [...retryByScope.values()].some(state => state.timer !== null)
437
+ : Boolean(retryByScope.get(normalizeScope(scopeKey))?.timer),
438
+ isQueueBusy: (scopeKey = 'default') => scopeKey === null
439
+ ? drainingScopes.size > 0
440
+ : drainingScopes.has(normalizeScope(scopeKey)),
287
441
  purgeQueueOnStop,
288
- retryState: {
289
- get timer() { return retryPendingTimer; },
290
- get resolve() { return retryPendingResolve; },
291
- get origin() { return retryPendingOrigin; },
292
- setTimer: (t) => { retryPendingTimer = t; },
293
- setResolve: (r) => { retryPendingResolve = r; },
294
- setOrigin: (o) => { retryPendingOrigin = o; },
295
- setIsEmployee: (v) => { retryPendingIsEmployee = v; },
442
+ retryStateForScope: (scopeKey) => {
443
+ const state = retryEntry(scopeKey);
444
+ return {
445
+ get timer() { return state.timer; },
446
+ get resolve() { return state.resolve; },
447
+ get origin() { return state.origin; },
448
+ setTimer: (t) => { state.timer = t; },
449
+ setResolve: (r) => { state.resolve = r; },
450
+ setOrigin: (o) => { state.origin = o; },
451
+ setIsEmployee: (v) => { state.isEmployee = v; },
452
+ };
296
453
  },
297
454
  };
298
455
  }