cli-jaw 2.2.16 → 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 (411) hide show
  1. package/README.md +10 -1
  2. package/dist/bin/commands/_shared/dashboard-client.js +42 -0
  3. package/dist/bin/commands/_shared/dashboard-client.js.map +1 -0
  4. package/dist/bin/commands/_shared/search-format.js +40 -0
  5. package/dist/bin/commands/_shared/search-format.js.map +1 -0
  6. package/dist/bin/commands/chat-search.js +5 -3
  7. package/dist/bin/commands/chat-search.js.map +1 -1
  8. package/dist/bin/commands/dashboard-chat.js +14 -31
  9. package/dist/bin/commands/dashboard-chat.js.map +1 -1
  10. package/dist/bin/commands/dashboard-memory.js +12 -49
  11. package/dist/bin/commands/dashboard-memory.js.map +1 -1
  12. package/dist/bin/commands/doctor.js +33 -10
  13. package/dist/bin/commands/doctor.js.map +1 -1
  14. package/dist/bin/commands/init.js +13 -1
  15. package/dist/bin/commands/init.js.map +1 -1
  16. package/dist/bin/commands/memory.js +5 -1
  17. package/dist/bin/commands/memory.js.map +1 -1
  18. package/dist/bin/commands/slack.js +57 -3
  19. package/dist/bin/commands/slack.js.map +1 -1
  20. package/dist/bin/commands/tui/input-handler.js +1 -1
  21. package/dist/bin/commands/tui/input-handler.js.map +1 -1
  22. package/dist/bin/commands/tui/slash-command-runner.js +4 -5
  23. package/dist/bin/commands/tui/slash-command-runner.js.map +1 -1
  24. package/dist/bin/postinstall.js +16 -18
  25. package/dist/bin/postinstall.js.map +1 -1
  26. package/dist/lib/media-kind.js +28 -0
  27. package/dist/lib/media-kind.js.map +1 -0
  28. package/dist/lib/upload.js +27 -10
  29. package/dist/lib/upload.js.map +1 -1
  30. package/dist/server.js +35 -51
  31. package/dist/server.js.map +1 -1
  32. package/dist/src/agent/args.js +34 -0
  33. package/dist/src/agent/args.js.map +1 -1
  34. package/dist/src/agent/codex-app-client.js +1016 -135
  35. package/dist/src/agent/codex-app-client.js.map +1 -1
  36. package/dist/src/agent/codex-app-events.js +72 -1
  37. package/dist/src/agent/codex-app-events.js.map +1 -1
  38. package/dist/src/agent/codex-host-pool.js +617 -0
  39. package/dist/src/agent/codex-host-pool.js.map +1 -0
  40. package/dist/src/agent/jwc-runtime.js +12 -4
  41. package/dist/src/agent/jwc-runtime.js.map +1 -1
  42. package/dist/src/agent/kiro-auth.js +10 -4
  43. package/dist/src/agent/kiro-auth.js.map +1 -1
  44. package/dist/src/agent/kiro-runtime.js +45 -8
  45. package/dist/src/agent/kiro-runtime.js.map +1 -1
  46. package/dist/src/agent/lifecycle-handler.js +75 -52
  47. package/dist/src/agent/lifecycle-handler.js.map +1 -1
  48. package/dist/src/agent/memory-flush-controller.js +241 -33
  49. package/dist/src/agent/memory-flush-controller.js.map +1 -1
  50. package/dist/src/agent/prompt-context.js +4 -0
  51. package/dist/src/agent/prompt-context.js.map +1 -1
  52. package/dist/src/agent/runtime-pool.js +64 -34
  53. package/dist/src/agent/runtime-pool.js.map +1 -1
  54. package/dist/src/agent/session-persistence.js +71 -15
  55. package/dist/src/agent/session-persistence.js.map +1 -1
  56. package/dist/src/agent/spawn/queue.js +319 -162
  57. package/dist/src/agent/spawn/queue.js.map +1 -1
  58. package/dist/src/agent/spawn.js +581 -268
  59. package/dist/src/agent/spawn.js.map +1 -1
  60. package/dist/src/bgtask/types.js +1 -1
  61. package/dist/src/bgtask/types.js.map +1 -1
  62. package/dist/src/browser/adaptive-fetch/safety.js +25 -0
  63. package/dist/src/browser/adaptive-fetch/safety.js.map +1 -1
  64. package/dist/src/browser/connection.js +2 -8
  65. package/dist/src/browser/connection.js.map +1 -1
  66. package/dist/src/cli/capability-probe-worker.js +172 -0
  67. package/dist/src/cli/capability-probe-worker.js.map +1 -0
  68. package/dist/src/cli/capability-probe.js +83 -0
  69. package/dist/src/cli/capability-probe.js.map +1 -0
  70. package/dist/src/cli/claude-models.js +2 -2
  71. package/dist/src/cli/claude-models.js.map +1 -1
  72. package/dist/src/cli/cli-status-worker.js +296 -0
  73. package/dist/src/cli/cli-status-worker.js.map +1 -0
  74. package/dist/src/cli/cli-status.js +81 -0
  75. package/dist/src/cli/cli-status.js.map +1 -0
  76. package/dist/src/cli/command-context.js +17 -5
  77. package/dist/src/cli/command-context.js.map +1 -1
  78. package/dist/src/cli/compact.js +56 -19
  79. package/dist/src/cli/compact.js.map +1 -1
  80. package/dist/src/cli/handlers-runtime.js +6 -4
  81. package/dist/src/cli/handlers-runtime.js.map +1 -1
  82. package/dist/src/cli/handlers.js +7 -2
  83. package/dist/src/cli/handlers.js.map +1 -1
  84. package/dist/src/cli/opencodex-models.js +5 -32
  85. package/dist/src/cli/opencodex-models.js.map +1 -1
  86. package/dist/src/cli/opencodex-runtime.js +82 -0
  87. package/dist/src/cli/opencodex-runtime.js.map +1 -0
  88. package/dist/src/cli/readiness.js +35 -18
  89. package/dist/src/cli/readiness.js.map +1 -1
  90. package/dist/src/cli/registry-live.js +8 -2
  91. package/dist/src/cli/registry-live.js.map +1 -1
  92. package/dist/src/cli/registry.js +1 -1
  93. package/dist/src/cli/registry.js.map +1 -1
  94. package/dist/src/core/browser-open-default.js +6 -4
  95. package/dist/src/core/browser-open-default.js.map +1 -1
  96. package/dist/src/core/browser-open.js +11 -15
  97. package/dist/src/core/browser-open.js.map +1 -1
  98. package/dist/src/core/bus.js +7 -1
  99. package/dist/src/core/bus.js.map +1 -1
  100. package/dist/src/core/chat-sessions.js +121 -12
  101. package/dist/src/core/chat-sessions.js.map +1 -1
  102. package/dist/src/core/cli-detect.js +148 -4
  103. package/dist/src/core/cli-detect.js.map +1 -1
  104. package/dist/src/core/codex-config.js +21 -0
  105. package/dist/src/core/codex-config.js.map +1 -1
  106. package/dist/src/core/compact.js +73 -21
  107. package/dist/src/core/compact.js.map +1 -1
  108. package/dist/src/core/config.js +449 -20
  109. package/dist/src/core/config.js.map +1 -1
  110. package/dist/src/core/db.js +225 -0
  111. package/dist/src/core/db.js.map +1 -1
  112. package/dist/src/core/event-bus.js +49 -4
  113. package/dist/src/core/event-bus.js.map +1 -1
  114. package/dist/src/core/event-scope.js +100 -0
  115. package/dist/src/core/event-scope.js.map +1 -0
  116. package/dist/src/core/main-session.js +64 -21
  117. package/dist/src/core/main-session.js.map +1 -1
  118. package/dist/src/core/message-summary.js +12 -2
  119. package/dist/src/core/message-summary.js.map +1 -1
  120. package/dist/src/core/platform-kind.js +139 -0
  121. package/dist/src/core/platform-kind.js.map +1 -0
  122. package/dist/src/core/rate-limit.js +156 -0
  123. package/dist/src/core/rate-limit.js.map +1 -0
  124. package/dist/src/core/runtime-settings.js +165 -17
  125. package/dist/src/core/runtime-settings.js.map +1 -1
  126. package/dist/src/core/session-context.js +7 -0
  127. package/dist/src/core/session-context.js.map +1 -0
  128. package/dist/src/core/session-ops.js +53 -9
  129. package/dist/src/core/session-ops.js.map +1 -1
  130. package/dist/src/core/settings-merge.js +105 -3
  131. package/dist/src/core/settings-merge.js.map +1 -1
  132. package/dist/src/core/settings-watch.js +29 -5
  133. package/dist/src/core/settings-watch.js.map +1 -1
  134. package/dist/src/core/widget-watcher.js +15 -2
  135. package/dist/src/core/widget-watcher.js.map +1 -1
  136. package/dist/src/discord/bot.js +5 -2
  137. package/dist/src/discord/bot.js.map +1 -1
  138. package/dist/src/discord/commands.js +4 -4
  139. package/dist/src/discord/commands.js.map +1 -1
  140. package/dist/src/goal/heartbeat.js +1 -1
  141. package/dist/src/goal/heartbeat.js.map +1 -1
  142. package/dist/src/goal/runtime.js +1 -1
  143. package/dist/src/goal/runtime.js.map +1 -1
  144. package/dist/src/goal-run/controller.js +2 -2
  145. package/dist/src/goal-run/controller.js.map +1 -1
  146. package/dist/src/manager/internal-fetch.js +15 -0
  147. package/dist/src/manager/internal-fetch.js.map +1 -0
  148. package/dist/src/manager/logs.js +2 -1
  149. package/dist/src/manager/logs.js.map +1 -1
  150. package/dist/src/manager/memory/chat-federation.js +104 -11
  151. package/dist/src/manager/memory/chat-federation.js.map +1 -1
  152. package/dist/src/manager/memory/federation.js +100 -0
  153. package/dist/src/manager/memory/federation.js.map +1 -1
  154. package/dist/src/manager/metadata.js +4 -1
  155. package/dist/src/manager/metadata.js.map +1 -1
  156. package/dist/src/manager/notes/assets.js +1 -1
  157. package/dist/src/manager/notes/assets.js.map +1 -1
  158. package/dist/src/manager/notes/frontmatter.js +5 -49
  159. package/dist/src/manager/notes/frontmatter.js.map +1 -1
  160. package/dist/src/manager/notes/link-resolver.js +2 -2
  161. package/dist/src/manager/notes/link-resolver.js.map +1 -1
  162. package/dist/src/manager/notes/remote-assets.js +1 -1
  163. package/dist/src/manager/notes/remote-assets.js.map +1 -1
  164. package/dist/src/manager/notes/routes.js +2 -2
  165. package/dist/src/manager/notes/routes.js.map +1 -1
  166. package/dist/src/manager/notes/store.js +2 -2
  167. package/dist/src/manager/notes/store.js.map +1 -1
  168. package/dist/src/manager/notes/trash.js +1 -1
  169. package/dist/src/manager/notes/trash.js.map +1 -1
  170. package/dist/src/manager/notes/vault-index.js +2 -2
  171. package/dist/src/manager/notes/vault-index.js.map +1 -1
  172. package/dist/src/manager/notes/wiki-links.js +1 -1
  173. package/dist/src/manager/notes/wiki-links.js.map +1 -1
  174. package/dist/src/manager/notes/wiki-routes.js +84 -0
  175. package/dist/src/manager/notes/wiki-routes.js.map +1 -0
  176. package/dist/src/manager/proxy.js +1 -0
  177. package/dist/src/manager/proxy.js.map +1 -1
  178. package/dist/src/manager/routes/dashboard-memory.js +24 -1
  179. package/dist/src/manager/routes/dashboard-memory.js.map +1 -1
  180. package/dist/src/manager/routes/runtime-monitor.js +1 -1
  181. package/dist/src/manager/routes/runtime-monitor.js.map +1 -1
  182. package/dist/src/manager/scan.js +5 -3
  183. package/dist/src/manager/scan.js.map +1 -1
  184. package/dist/src/manager/server.js +20 -6
  185. package/dist/src/manager/server.js.map +1 -1
  186. package/dist/src/manager/telegram-hub/hub-bot.js +4 -3
  187. package/dist/src/manager/telegram-hub/hub-bot.js.map +1 -1
  188. package/dist/src/manager/worker-events.js +2 -1
  189. package/dist/src/manager/worker-events.js.map +1 -1
  190. package/dist/src/memory/heartbeat.js +6 -5
  191. package/dist/src/memory/heartbeat.js.map +1 -1
  192. package/dist/src/memory/indexing.js +162 -45
  193. package/dist/src/memory/indexing.js.map +1 -1
  194. package/dist/src/messaging/channel-validate.js +14 -1
  195. package/dist/src/messaging/channel-validate.js.map +1 -1
  196. package/dist/src/messaging/redact.js +2 -1
  197. package/dist/src/messaging/redact.js.map +1 -1
  198. package/dist/src/messaging/session-key.js +21 -2
  199. package/dist/src/messaging/session-key.js.map +1 -1
  200. package/dist/src/notes/constants.js.map +1 -0
  201. package/dist/src/notes/frontmatter.js +59 -0
  202. package/dist/src/notes/frontmatter.js.map +1 -0
  203. package/dist/src/notes/path-guards.js.map +1 -0
  204. package/dist/src/notes/search.js.map +1 -0
  205. package/dist/src/orchestrator/attestation.js +1 -1
  206. package/dist/src/orchestrator/attestation.js.map +1 -1
  207. package/dist/src/orchestrator/gateway.js +135 -31
  208. package/dist/src/orchestrator/gateway.js.map +1 -1
  209. package/dist/src/orchestrator/pipeline.js +53 -21
  210. package/dist/src/orchestrator/pipeline.js.map +1 -1
  211. package/dist/src/orchestrator/scope.js +36 -2
  212. package/dist/src/orchestrator/scope.js.map +1 -1
  213. package/dist/src/orchestrator/session-lanes.js +87 -0
  214. package/dist/src/orchestrator/session-lanes.js.map +1 -0
  215. package/dist/src/orchestrator/session-work.js +25 -0
  216. package/dist/src/orchestrator/session-work.js.map +1 -0
  217. package/dist/src/orchestrator/state-machine.js +1 -5
  218. package/dist/src/orchestrator/state-machine.js.map +1 -1
  219. package/dist/src/orchestrator/worker-registry.js +20 -10
  220. package/dist/src/orchestrator/worker-registry.js.map +1 -1
  221. package/dist/src/prompt/builder.js +40 -2
  222. package/dist/src/prompt/builder.js.map +1 -1
  223. package/dist/src/prompt/templates/a1-system.md +8 -1
  224. package/dist/src/routes/agent-control.js +44 -8
  225. package/dist/src/routes/agent-control.js.map +1 -1
  226. package/dist/src/routes/chat-sessions.js +34 -6
  227. package/dist/src/routes/chat-sessions.js.map +1 -1
  228. package/dist/src/routes/command.js +63 -8
  229. package/dist/src/routes/command.js.map +1 -1
  230. package/dist/src/routes/events.js +11 -2
  231. package/dist/src/routes/events.js.map +1 -1
  232. package/dist/src/routes/link-preview.js +1 -27
  233. package/dist/src/routes/link-preview.js.map +1 -1
  234. package/dist/src/routes/messages.js +6 -5
  235. package/dist/src/routes/messages.js.map +1 -1
  236. package/dist/src/routes/messaging.js +45 -1
  237. package/dist/src/routes/messaging.js.map +1 -1
  238. package/dist/src/routes/orchestrate.js +53 -33
  239. package/dist/src/routes/orchestrate.js.map +1 -1
  240. package/dist/src/routes/search.js +84 -0
  241. package/dist/src/routes/search.js.map +1 -0
  242. package/dist/src/routes/session-request.js +29 -0
  243. package/dist/src/routes/session-request.js.map +1 -0
  244. package/dist/src/routes/settings.js +110 -3
  245. package/dist/src/routes/settings.js.map +1 -1
  246. package/dist/src/routes/static.js +30 -8
  247. package/dist/src/routes/static.js.map +1 -1
  248. package/dist/src/routes/system.js +1 -1
  249. package/dist/src/routes/system.js.map +1 -1
  250. package/dist/src/routes/wiki.js +122 -0
  251. package/dist/src/routes/wiki.js.map +1 -0
  252. package/dist/src/search/contract.js +2 -0
  253. package/dist/src/search/contract.js.map +1 -0
  254. package/dist/src/search/coordinator.js +153 -0
  255. package/dist/src/search/coordinator.js.map +1 -0
  256. package/dist/src/search/provider.js +40 -0
  257. package/dist/src/search/provider.js.map +1 -0
  258. package/dist/src/search/providers/chat.js +115 -0
  259. package/dist/src/search/providers/chat.js.map +1 -0
  260. package/dist/src/search/providers/memory.js +53 -0
  261. package/dist/src/search/providers/memory.js.map +1 -0
  262. package/dist/src/search/providers/wiki.js +80 -0
  263. package/dist/src/search/providers/wiki.js.map +1 -0
  264. package/dist/src/slack/api.js +10 -1
  265. package/dist/src/slack/api.js.map +1 -1
  266. package/dist/src/slack/attachment-recovery.js +42 -0
  267. package/dist/src/slack/attachment-recovery.js.map +1 -0
  268. package/dist/src/slack/bot.js +120 -41
  269. package/dist/src/slack/bot.js.map +1 -1
  270. package/dist/src/slack/commands.js +24 -8
  271. package/dist/src/slack/commands.js.map +1 -1
  272. package/dist/src/slack/events.js +12 -1
  273. package/dist/src/slack/events.js.map +1 -1
  274. package/dist/src/slack/history.js +108 -0
  275. package/dist/src/slack/history.js.map +1 -0
  276. package/dist/src/slack/inbound-file.js +242 -0
  277. package/dist/src/slack/inbound-file.js.map +1 -0
  278. package/dist/src/slack/inbound-url.js +39 -0
  279. package/dist/src/slack/inbound-url.js.map +1 -0
  280. package/dist/src/slack/ingress.js +140 -0
  281. package/dist/src/slack/ingress.js.map +1 -0
  282. package/dist/src/slack/manifest.js +3 -0
  283. package/dist/src/slack/manifest.js.map +1 -1
  284. package/dist/src/slack/socket.js +1 -1
  285. package/dist/src/slack/socket.js.map +1 -1
  286. package/dist/src/slack/thread-tracker.js +86 -0
  287. package/dist/src/slack/thread-tracker.js.map +1 -0
  288. package/dist/src/telegram/bot.js +21 -11
  289. package/dist/src/telegram/bot.js.map +1 -1
  290. package/dist/src/wiki/config.js +193 -0
  291. package/dist/src/wiki/config.js.map +1 -0
  292. package/dist/src/wiki/entities.js +61 -0
  293. package/dist/src/wiki/entities.js.map +1 -0
  294. package/dist/src/wiki/ontology.js +72 -0
  295. package/dist/src/wiki/ontology.js.map +1 -0
  296. package/dist/src/wiki/prompt.js +172 -0
  297. package/dist/src/wiki/prompt.js.map +1 -0
  298. package/dist/src/wiki/safe-traversal.js +198 -0
  299. package/dist/src/wiki/safe-traversal.js.map +1 -0
  300. package/dist/src/wiki/scaffold.js +77 -0
  301. package/dist/src/wiki/scaffold.js.map +1 -0
  302. package/package.json +1 -1
  303. package/public/css/session-hub.css +266 -0
  304. package/public/dist/assets/AdvancedExport-DYGcCAUq.js +1 -0
  305. package/public/dist/assets/Agent-CNNQ7KkJ.js +1 -0
  306. package/public/dist/assets/{DesignPanel-BGMPhZSx.js → DesignPanel-DcL369nB.js} +1 -1
  307. package/public/dist/assets/{DocPanel-Dz-J_l5M.js → DocPanel-K8A2pm_1.js} +1 -1
  308. package/public/dist/assets/{FolderPanel-DbPhzzvi.js → FolderPanel-CV52EzwG.js} +1 -1
  309. package/public/dist/assets/{MilkdownWysiwygEditor-Em7iC6y9.js → MilkdownWysiwygEditor-BKOCDI4Z.js} +1 -1
  310. package/public/dist/assets/api-B25zNNFJ.js +1 -0
  311. package/public/dist/assets/{app-DX4e6isX.css → app-DjgGeXhJ.css} +1 -1
  312. package/public/dist/assets/app-Dsgp4xpU.js +55 -0
  313. package/public/dist/assets/bgtask-badge-C6h5C-o4.js +1 -0
  314. package/public/dist/assets/{bgtask-badge-qMkKOZx0.js → bgtask-badge-CpavV0Jc.js} +4 -4
  315. package/public/dist/assets/{employees-BnobWM_2.js → employees-BJyEgim7.js} +4 -4
  316. package/public/dist/assets/idb-cache-B5PX63p7.js +1 -0
  317. package/public/dist/assets/{idb-cache-BzzftUET.js → idb-cache-lOntwU3S.js} +1 -1
  318. package/public/dist/assets/{iframe-renderer-C5vRbbl9.js → iframe-renderer-B8k3AvTv.js} +12 -12
  319. package/public/dist/assets/iframe-renderer-BO_GgCTR.js +1 -0
  320. package/public/dist/assets/manager-LkGs6qEJ.css +1 -0
  321. package/public/dist/assets/manager-kXYQyMhR.js +13 -0
  322. package/public/dist/assets/{memory-DF4kPwwC.js → memory-D2RbBVO-.js} +11 -11
  323. package/public/dist/assets/memory-amsvg4Ey.js +1 -0
  324. package/public/dist/assets/message-history-BGHZxvgV.js +1 -0
  325. package/public/dist/assets/message-history-Bw2E_b8L.js +1 -0
  326. package/public/dist/assets/pi-settings-5HVg1sXJ.js +1 -0
  327. package/public/dist/assets/render-BkGcpH7z.js +157 -0
  328. package/public/dist/assets/settings-B1FPORO2.js +56 -0
  329. package/public/dist/assets/settings-CMN-zN4Q.js +1 -0
  330. package/public/dist/assets/settings-core-B_OjGYVN.js +1 -0
  331. package/public/dist/assets/settings-core-CxRwqx86.js +144 -0
  332. package/public/dist/assets/sidebar-DW78DANx.js +49 -0
  333. package/public/dist/assets/skills-Dz-9Yrxr.js +1 -0
  334. package/public/dist/assets/{skills-IGquen_j.js → skills-lWi0ZFiV.js} +6 -6
  335. package/public/dist/assets/{slash-commands-ZRDOL99E.js → slash-commands-DYOI6nKj.js} +1 -1
  336. package/public/dist/assets/slash-commands-xJFCdTTM.js +1 -0
  337. package/public/dist/assets/{trace-drawer-D8To8fYt.js → trace-drawer-DBX9xzmt.js} +1 -1
  338. package/public/dist/assets/{ui-Hx_mNoDf.js → ui-Bx7eqnXt.js} +1 -1
  339. package/public/dist/assets/ui-JOcrYeG-.js +1 -0
  340. package/public/dist/assets/{wiki-link-suggestions-DZQc78gj.js → wiki-link-suggestions-Dp6kBQVu.js} +1 -1
  341. package/public/dist/index.html +4 -4
  342. package/public/dist/manager/index.html +2 -2
  343. package/public/index.html +3 -2
  344. package/public/js/event-channel.ts +25 -0
  345. package/public/js/features/channel-onboarding-flow.ts +16 -1
  346. package/public/js/features/channel-onboarding.ts +20 -2
  347. package/public/js/features/chat-messages.ts +3 -1
  348. package/public/js/features/chat-search.ts +2 -1
  349. package/public/js/features/chat.ts +56 -18
  350. package/public/js/features/elicitation.ts +15 -0
  351. package/public/js/features/idb-cache.ts +34 -0
  352. package/public/js/features/message-history.ts +2 -1
  353. package/public/js/features/session-hub.ts +334 -0
  354. package/public/js/features/settings-cli-status-render.ts +1 -1
  355. package/public/js/features/settings-cli-status.ts +25 -11
  356. package/public/js/features/settings-core.ts +72 -3
  357. package/public/js/features/settings-types.ts +32 -0
  358. package/public/js/features/ui-status.ts +2 -1
  359. package/public/js/main.ts +20 -0
  360. package/public/js/render/compose-block.ts +7 -0
  361. package/public/js/ws.ts +22 -2
  362. package/public/locales/en.json +46 -4
  363. package/public/locales/ja.json +46 -4
  364. package/public/locales/ko.json +46 -4
  365. package/public/locales/zh.json +46 -4
  366. package/public/manager/src/App.tsx +1 -1
  367. package/public/manager/src/InstancePreview.tsx +5 -1
  368. package/public/manager/src/api.ts +2 -1
  369. package/public/manager/src/components/InstanceGroups.tsx +10 -0
  370. package/public/manager/src/components/InstanceListContent.tsx +24 -1
  371. package/public/manager/src/components/InstanceRow.tsx +33 -3
  372. package/public/manager/src/components/InstanceSessionList.tsx +132 -0
  373. package/public/manager/src/hooks/useActiveSessionDisclosure.ts +45 -0
  374. package/public/manager/src/lib/resilient-fetch.ts +93 -0
  375. package/public/manager/src/lib/session-store.ts +216 -0
  376. package/public/manager/src/manager-components.css +97 -0
  377. package/public/manager/src/settings/pages/AdvancedExport.tsx +12 -2
  378. package/public/manager/src/settings/pages/Agent.tsx +113 -1
  379. package/public/manager/src/settings/pages/components/agent/agent-meta.ts +1 -1
  380. package/scripts/burst-scenario.mjs +72 -0
  381. package/scripts/check-redaction-sinks.mjs +1 -0
  382. package/scripts/check-strict-baseline.mjs +1 -1
  383. package/dist/src/manager/notes/constants.js.map +0 -1
  384. package/dist/src/manager/notes/path-guards.js.map +0 -1
  385. package/dist/src/manager/notes/search.js.map +0 -1
  386. package/public/dist/assets/AdvancedExport-DsDpqXoH.js +0 -1
  387. package/public/dist/assets/Agent-BjbZVZ9p.js +0 -1
  388. package/public/dist/assets/api-C9dOxby7.js +0 -1
  389. package/public/dist/assets/app-BzD_ZLvt.js +0 -55
  390. package/public/dist/assets/bgtask-badge-Dqbp-Y4e.js +0 -1
  391. package/public/dist/assets/idb-cache-ZH6lOebk.js +0 -1
  392. package/public/dist/assets/iframe-renderer-Df4KIVyx.js +0 -1
  393. package/public/dist/assets/manager-C4eNDNFt.css +0 -1
  394. package/public/dist/assets/manager-DaN201mN.js +0 -13
  395. package/public/dist/assets/memory-D-bULTHl.js +0 -1
  396. package/public/dist/assets/message-history-C4WOisGx.js +0 -1
  397. package/public/dist/assets/message-history-ZBoza6ew.js +0 -1
  398. package/public/dist/assets/pi-settings-CBUGDAuT.js +0 -1
  399. package/public/dist/assets/render-DYX6GRQ0.js +0 -122
  400. package/public/dist/assets/settings-DbJpGoaX.js +0 -55
  401. package/public/dist/assets/settings-SreihUje.js +0 -1
  402. package/public/dist/assets/settings-core-B1Y5Nb4Z.js +0 -1
  403. package/public/dist/assets/settings-core-DobZWIZ7.js +0 -140
  404. package/public/dist/assets/sidebar-Cp22g0Gv.js +0 -49
  405. package/public/dist/assets/skills-DwPIP08e.js +0 -1
  406. package/public/dist/assets/slash-commands-BBVfwcnW.js +0 -1
  407. package/public/dist/assets/ui-CAicpUZc.js +0 -1
  408. /package/dist/src/{manager/notes → notes}/constants.js +0 -0
  409. /package/dist/src/{manager/notes → notes}/path-guards.js +0 -0
  410. /package/dist/src/{manager/notes → notes}/search.js +0 -0
  411. /package/public/dist/assets/{pi-settings-Dh5lgb87.js → pi-settings-DEuKgvIj.js} +0 -0
@@ -6,9 +6,9 @@
6
6
  <link rel="icon" type="image/png" href="/dist/assets/icon-192-CwXPIYF5.png">
7
7
  <title>Jaw Manager</title>
8
8
  <script src="/manager/theme-boot.js"></script>
9
- <script type="module" crossorigin src="/dist/assets/manager-DaN201mN.js"></script>
9
+ <script type="module" crossorigin src="/dist/assets/manager-kXYQyMhR.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="/dist/assets/vendor-render-Bjnw0wQ6.css">
11
- <link rel="stylesheet" crossorigin href="/dist/assets/manager-C4eNDNFt.css">
11
+ <link rel="stylesheet" crossorigin href="/dist/assets/manager-LkGs6qEJ.css">
12
12
  </head>
13
13
  <body>
14
14
  <div id="manager-root"></div>
package/public/index.html CHANGED
@@ -42,6 +42,7 @@
42
42
  <link rel="stylesheet" href="/css/tool-ui.css">
43
43
  <link rel="stylesheet" href="/css/trace-drawer.css">
44
44
  <link rel="stylesheet" href="/css/diagram.css">
45
+ <link rel="stylesheet" href="/css/session-hub.css">
45
46
  <!-- Vite handles module bundling in dev (HMR) and production (build) -->
46
47
  </head>
47
48
 
@@ -583,7 +584,7 @@
583
584
  <span class="slack-step-num">1</span>
584
585
  <div class="slack-step-body">
585
586
  <div class="slack-step-title" data-i18n="settings.slack.guide.step1Title">Slack 앱 만들기</div>
586
- <div class="slack-step-desc" data-i18n="settings.slack.guide.step1Desc">Create an App → From an app manifest → 워크스페이스 선택 → 붙여넣기</div>
587
+ <div class="slack-step-desc" data-i18n="settings.slack.guide.step1Desc">Create an App → From an app manifest → 워크스페이스 선택 → 붙여넣기 (기존 앱은 files:read 추가)</div>
587
588
  <div class="slack-step-actions">
588
589
  <button id="slack-copy-manifest" class="perm-btn" type="button" data-i18n="settings.slack.guide.copyManifest">매니페스트 복사</button>
589
590
  <button id="slack-open-apps" class="perm-btn" type="button" data-i18n="settings.slack.guide.openApps">앱 페이지 열기</button>
@@ -594,7 +595,7 @@
594
595
  <span class="slack-step-num">2</span>
595
596
  <div class="slack-step-body">
596
597
  <div class="slack-step-title" data-i18n="settings.slack.guide.step2Title">봇 토큰 붙여넣기</div>
597
- <div class="slack-step-desc" data-i18n="settings.slack.guide.step2Desc">앱 설정 → OAuth &amp; Permissions → Install to Workspace → Bot User OAuth Token 복사</div>
598
+ <div class="slack-step-desc" data-i18n="settings.slack.guide.step2Desc">앱 설정 → OAuth &amp; Permissions → files:read 확인 → Reinstall to Workspace → Bot User OAuth Token 복사</div>
598
599
  <input type="password" id="slBotToken" placeholder="xoxb-..." class="input-sm">
599
600
  <div class="slack-field-error" id="slack-bot-token-error" style="display:none" data-i18n="settings.slack.guide.botTokenError">xoxb-로 시작하는 봇 토큰이어야 합니다</div>
600
601
  </div>
@@ -39,6 +39,30 @@ let lastMessageAt = 0;
39
39
  let pingCapable = false;
40
40
  let stallTimer: ReturnType<typeof setInterval> | null = null;
41
41
 
42
+ // ── Per-tab scope (072 §1.3a) ──
43
+ // A tab viewing one session subscribes to that session's scope so another
44
+ // session's output never lands in it. Injected rather than imported: the scope
45
+ // comes from the session view, which imports the API layer, and reading it
46
+ // through a provider keeps this module free of that cycle and picks up the
47
+ // current value on every reconnect rather than freezing the boot-time one.
48
+ let scopeProvider: (() => string | null) | null = null;
49
+
50
+ export function setEventChannelScopeProvider(provider: (() => string | null) | null): void {
51
+ scopeProvider = provider;
52
+ }
53
+
54
+ function currentScopeParam(): string {
55
+ let scope: string | null = null;
56
+ try {
57
+ scope = scopeProvider?.() ?? null;
58
+ } catch {
59
+ // A broken provider must not take the whole channel down; an unscoped
60
+ // connection still receives everything, which is the old behaviour.
61
+ scope = null;
62
+ }
63
+ return scope ? `&scope=${encodeURIComponent(scope)}` : '';
64
+ }
65
+
42
66
  function armStallWatchdog(): void {
43
67
  if (stallTimer) return;
44
68
  stallTimer = setInterval(() => {
@@ -111,6 +135,7 @@ export function connectEventChannel(lang: string): void {
111
135
  if (source) source.close();
112
136
 
113
137
  const url = `${API_BASE}/api/events?lang=${encodeURIComponent(lang)}` +
138
+ currentScopeParam() +
114
139
  (lastEventId ? `&lastEventId=${lastEventId}` : '');
115
140
  openedThisAttempt = false;
116
141
  unavailableFiredThisAttempt = false;
@@ -60,6 +60,8 @@ export type FlowState = {
60
60
  error: string | null;
61
61
  /** Scopes the provider reported as missing, shown with the error. */
62
62
  missingScopes: string[];
63
+ /** Optional feature scopes; warnings only, never a step-3 blocker. */
64
+ missingCapabilities: string[];
63
65
  saved: boolean;
64
66
  };
65
67
 
@@ -72,6 +74,7 @@ export function createFlow(channel: OnboardChannel, draft: Draft = {}): FlowStat
72
74
  validatedTeamId: '',
73
75
  error: null,
74
76
  missingScopes: [],
77
+ missingCapabilities: [],
75
78
  saved: false,
76
79
  };
77
80
  }
@@ -139,12 +142,20 @@ export function setField(state: FlowState, key: string, value: string): FlowStat
139
142
  validatedTeamId: '',
140
143
  error: null,
141
144
  missingScopes: [],
145
+ missingCapabilities: [],
142
146
  };
143
147
  }
144
148
 
145
149
  export function applyValidation(
146
150
  state: FlowState,
147
- result: { ok?: boolean; identity?: string; teamId?: string; error?: string; missing?: string[] },
151
+ result: {
152
+ ok?: boolean;
153
+ identity?: string;
154
+ teamId?: string;
155
+ error?: string;
156
+ missing?: string[];
157
+ missingCapabilities?: string[];
158
+ },
148
159
  ): FlowState {
149
160
  if (result?.ok) {
150
161
  return {
@@ -153,6 +164,9 @@ export function applyValidation(
153
164
  validatedTeamId: result.teamId || '',
154
165
  error: null,
155
166
  missingScopes: [],
167
+ missingCapabilities: Array.isArray(result.missingCapabilities)
168
+ ? result.missingCapabilities
169
+ : [],
156
170
  };
157
171
  }
158
172
  return {
@@ -161,6 +175,7 @@ export function applyValidation(
161
175
  validatedTeamId: '',
162
176
  error: result?.error || 'network',
163
177
  missingScopes: Array.isArray(result?.missing) ? result.missing : [],
178
+ missingCapabilities: [],
164
179
  };
165
180
  }
166
181
 
@@ -119,6 +119,7 @@ function stepBody(state: FlowState): string {
119
119
  if (state.step === 1) {
120
120
  return `
121
121
  <p class="onboarding-guide">${escapeHtml(t(`onboarding.guide.${state.channel}`))}</p>
122
+ ${state.channel === 'slack' ? `<p class="onboarding-guide">${escapeHtml(t('onboarding.slackExistingApp'))}</p>` : ''}
122
123
  <div class="onboarding-actions">
123
124
  <button type="button" class="perm-btn" data-onboard-issuer="1">
124
125
  ${escapeHtml(t('onboarding.openIssuer'))}
@@ -126,7 +127,7 @@ function stepBody(state: FlowState): string {
126
127
  </div>`;
127
128
  }
128
129
  if (state.step === 2) {
129
- return fieldsFor(state.channel).map((field) => {
130
+ const fields = fieldsFor(state.channel).map((field) => {
130
131
  const label = t(`onboarding.token.${field.key}`) + (field.optional ? ` (${t('onboarding.optional')})` : '');
131
132
  return `
132
133
  <div class="onboarding-field">
@@ -139,6 +140,9 @@ function stepBody(state: FlowState): string {
139
140
  <span class="onboarding-hint">${escapeHtml(t(`onboarding.hint.${state.channel}.${field.key}`))}</span>
140
141
  </div>`;
141
142
  }).join('');
143
+ return `${fields}${state.channel === 'slack'
144
+ ? `<p class="onboarding-guide">${escapeHtml(t('onboarding.slackExistingApp'))}</p>`
145
+ : ''}`;
142
146
  }
143
147
  if (state.step === 3) {
144
148
  const status = validating
@@ -194,6 +198,13 @@ function render(): void {
194
198
  ${state.error ? escapeHtml(t(`onboarding.error.${state.error}`)) : ''}
195
199
  ${state.missingScopes.length ? `<span class="onboarding-scopes">${escapeHtml(state.missingScopes.join(', '))}</span>` : ''}
196
200
  </div>
201
+ <div class="onboarding-capability-warning" role="status" style="display:${state.missingCapabilities.length ? '' : 'none'}">
202
+ ${state.missingCapabilities.length
203
+ ? escapeHtml(t('onboarding.warning.missingCapabilities', {
204
+ capabilities: state.missingCapabilities.join(', '),
205
+ }))
206
+ : ''}
207
+ </div>
197
208
  <div class="onboarding-actions onboarding-footer">${footer(state)}</div>
198
209
  </div>`;
199
210
 
@@ -273,7 +284,14 @@ async function runValidation(): Promise<void> {
273
284
  const generation = flowGeneration;
274
285
  validating = true;
275
286
  render();
276
- const res = await api<{ ok?: boolean; identity?: string; teamId?: string; error?: string; missing?: string[] }>(
287
+ const res = await api<{
288
+ ok?: boolean;
289
+ identity?: string;
290
+ teamId?: string;
291
+ error?: string;
292
+ missing?: string[];
293
+ missingCapabilities?: string[];
294
+ }>(
277
295
  '/api/channels/validate',
278
296
  {
279
297
  method: 'POST',
@@ -74,7 +74,9 @@ export function formatUserPrompt(text: string): string {
74
74
  const userMsg = userMsgMatch ? ' ' + userMsgMatch[1].trim() : '';
75
75
  return `📎 [${count} files]${userMsg}`;
76
76
  }
77
- const fileMatch = text.match(/^\[(?:사용자가 파일을 보냈습니다|사용자가 이미지를 보냈습니다|사용자가 동영상을 보냈습니다|User sent a file|User sent an image|User sent a video): ([^\]]+)\]/);
77
+ // ja/zh 변형이 빠지면 해당 로케일 사용자는 인라인 미리보기를 잃고 원문이
78
+ // 그대로 노출된다. zh 는 전각 콜론(:)을 쓰므로 구분자를 일반화한다.
79
+ const fileMatch = text.match(/^\[(?:사용자가 파일을 보냈습니다|사용자가 이미지를 보냈습니다|사용자가 동영상을 보냈습니다|User sent a file|User sent an image|User sent a video|ユーザーがファイルを送信しました|ユーザーが画像を送信しました|ユーザーが動画を送信しました|用户发送了文件|用户发送了图片|用户发送了视频)[::]\s*([^\]]+)\]/);
78
80
  if (fileMatch) {
79
81
  const fullPath = fileMatch[1];
80
82
  const fileName = fullPath.split('/').pop() || fullPath;
@@ -2,6 +2,7 @@ import { api } from '../api.js';
2
2
  import { getVirtualScroll } from '../virtual-scroll.js';
3
3
  import { escapeHtml } from '../render.js';
4
4
  import { bootMessageQuery } from './message-history.js';
5
+ import { withCurrentSessionQuery } from './session-hub.js';
5
6
 
6
7
  const IS_IFRAME = window.parent !== window;
7
8
 
@@ -111,7 +112,7 @@ function buildMessageIndexMap(): void {
111
112
 
112
113
  async function runSearch(q: string, resultsEl: HTMLElement, countEl: HTMLElement): Promise<void> {
113
114
  lastQuery = q;
114
- const data = await api<SearchResult[]>(`/api/messages/search?q=${encodeURIComponent(q)}&limit=20`);
115
+ const data = await api<SearchResult[]>(withCurrentSessionQuery(`/api/messages/search?q=${encodeURIComponent(q)}&limit=20`));
115
116
  if (!data || lastQuery !== q) return;
116
117
  countEl.textContent = data.length > 0 ? `${data.length} found` : 'no results';
117
118
  if (data.length === 0) {
@@ -16,6 +16,8 @@ import { tryCommandInfo } from './command-info.js';
16
16
  import { isChatNearBottom, markFollowingBottom, reconcileChatBottomAfterLayout } from './chat-scroll.js';
17
17
  import { copyText } from './copy-text.js';
18
18
  import { isLocalPreviewRelayOrigin, previewParentOrigin } from '../preview-parent-origin.js';
19
+ import { canSendFromCurrentView, showReadOnlySwitchAffordance, withCurrentSessionBody } from './session-hub.js';
20
+ import { mediaKindFromPath, type MediaKind } from '../../../lib/media-kind.js';
19
21
 
20
22
  let activeObjectURLs: string[] = [];
21
23
 
@@ -50,13 +52,25 @@ function getCommandTimeoutMs(text: string): number {
50
52
  return /^\/compact(?:\s|$)/i.test(String(text || '').trim()) ? 5 * 60 * 1000 : 10_000;
51
53
  }
52
54
 
55
+ // 첨부 프롬프트는 파일 종류를 유지해야 한다. 고정 "파일" 문구만 쓰면 이미지를
56
+ // 올려도 에이전트가 Read 도구로 열려다 실패한다 (devlog 260806_slack_multifile_ingest/001 §4 D2).
57
+ const ATTACHMENT_KEY: Record<MediaKind, string> = {
58
+ image: 'chat.file.sentImage',
59
+ video: 'chat.file.sentVideo',
60
+ file: 'chat.file.sent',
61
+ };
62
+
63
+ function attachmentLine(path: string): string {
64
+ return t(ATTACHMENT_KEY[mediaKindFromPath(path)], { path });
65
+ }
66
+
53
67
  function buildAttachmentPrompt(paths: string[], text = ''): string {
54
- const prompt = paths.map(p => t('chat.file.sent', { path: p })).join('\n');
68
+ const prompt = paths.map(attachmentLine).join('\n');
55
69
  return text ? `${prompt}${t('chat.file.sentWithMsg', { text })}` : prompt;
56
70
  }
57
71
 
58
72
  function buildSlashCommandAttachmentText(text: string, paths: string[]): string {
59
- const fileContext = paths.map(p => t('chat.file.sent', { path: p })).join('\n');
73
+ const fileContext = paths.map(attachmentLine).join('\n');
60
74
  return fileContext ? `${text}\n\n${fileContext}` : text;
61
75
  }
62
76
 
@@ -91,7 +105,13 @@ function sendPreviewMessageViaParent(prompt: string): Promise<MessagePostResult
91
105
  const timeout = window.setTimeout(() => settle(null), PREVIEW_SEND_RELAY_TIMEOUT_MS);
92
106
  window.addEventListener('message', onMessage);
93
107
  try {
94
- window.parent.postMessage({ type: 'jaw-preview-send-message', requestId, prompt }, targetOrigin);
108
+ // The session travels with the relay too. Without it the manager forwards a
109
+ // bare prompt and the instance writes to whichever session is globally
110
+ // active, which is not the one this preview is showing (072 §1.1).
111
+ window.parent.postMessage(
112
+ withCurrentSessionBody({ type: 'jaw-preview-send-message', requestId, prompt }),
113
+ targetOrigin,
114
+ );
95
115
  } catch {
96
116
  settle(null);
97
117
  }
@@ -105,7 +125,7 @@ async function postChatMessage(prompt: string): Promise<MessagePostResult> {
105
125
  const res = await fetch(`${API_BASE}/api/message`, {
106
126
  method: 'POST',
107
127
  headers: { 'Content-Type': 'application/json' },
108
- body: JSON.stringify({ prompt }),
128
+ body: JSON.stringify(withCurrentSessionBody({ prompt })),
109
129
  });
110
130
  const data: MessageResult = await res.json().catch(() => ({}));
111
131
  return { ok: res.ok, status: res.status, data };
@@ -133,7 +153,7 @@ async function postSlashCommand(text: string): Promise<{ ok: boolean; status: nu
133
153
  'Accept-Language': locale,
134
154
  ...(token ? { 'Authorization': `Bearer ${token}` } : {}),
135
155
  },
136
- body: JSON.stringify({ text, locale }),
156
+ body: JSON.stringify(withCurrentSessionBody({ text, locale })),
137
157
  signal,
138
158
  });
139
159
  if (timer) clearTimeout(timer);
@@ -155,7 +175,7 @@ async function handleSlashCommandResponse(
155
175
  }
156
176
  addMessage('user', originalText);
157
177
  upsertMessage({ role: 'user', content: originalText, timestamp: Date.now() });
158
- await apiJson('/api/message', 'POST', { prompt: originalText });
178
+ await apiJson('/api/message', 'POST', withCurrentSessionBody({ prompt: originalText }));
159
179
  return;
160
180
  }
161
181
  if (!response.ok && !result?.text) throw new Error(`HTTP ${response.status}`);
@@ -167,7 +187,7 @@ async function handleSlashCommandResponse(
167
187
  }
168
188
  if (result?.text || result?.recovery) addSystemMsg(renderCommandRecovery(result), '', result.type);
169
189
  if (result?.steerPrompt) {
170
- await apiJson('/api/message', 'POST', { prompt: result.steerPrompt });
190
+ await apiJson('/api/message', 'POST', withCurrentSessionBody({ prompt: result.steerPrompt }));
171
191
  }
172
192
  }
173
193
 
@@ -216,7 +236,7 @@ document.addEventListener('click', async (event) => {
216
236
 
217
237
  // In-flight guard: prevents double-send from rapid clicks / Enter-bursts while the
218
238
  // POST to /api/message is outstanding. Server-side dedup in gateway.ts is the
219
- // second line of defense. See devlog/_plan/260417_message_duplication/.
239
+ // second line of defense. See devlog/_fin/260417_message_duplication/.
220
240
  let __chatSending = false;
221
241
 
222
242
  export type SendSource = 'button' | 'enter' | 'cmd-execute';
@@ -226,12 +246,24 @@ export async function sendMessage(source: SendSource = 'enter'): Promise<void> {
226
246
  const btn = document.getElementById('btnSend');
227
247
  if (!input || !btn) return;
228
248
 
249
+ const text = input.value.trim();
250
+ // A stop-mode button click is not a message send, so it carries no command
251
+ // text and the read-only guard must judge it on its own. Every other path —
252
+ // including an ordinary button click on `/switch 1` — must pass the text,
253
+ // or the escape commands that let a user leave a read-only session would be
254
+ // rejected through the button while still working via Enter.
255
+ const isStopClick = source === 'button' && btn.classList.contains('stop-mode');
256
+ if (!canSendFromCurrentView(isStopClick ? '' : text)) {
257
+ showReadOnlySwitchAffordance();
258
+ return;
259
+ }
260
+
229
261
  // Stop-mode click policy (devlog 260501_chat_pause_and_unread_badge):
230
262
  // - any stop-mode button click → fire /api/stop and return.
231
- // /api/stop calls killAllAgents() server-side, which is the user's
232
- // intent ("실제로 정지가 안돼"). The early `return` preserves typed
233
- // text and attachments — typing is never auto-steered into the
234
- // just-killed run.
263
+ // /api/stop stops this session's run when the tab names one, and every
264
+ // run otherwise, which is the user's intent ("실제로 정지가 안돼").
265
+ // The early `return` preserves typed text and attachments — typing is
266
+ // never auto-steered into the just-killed run.
235
267
  // - Enter key and slash-command execute do NOT enter this branch
236
268
  // (source !== 'button'), so they keep their normal behavior even
237
269
  // while the agent is busy.
@@ -240,14 +272,16 @@ export async function sendMessage(source: SendSource = 'enter'): Promise<void> {
240
272
  // SendSource keeps detection deterministic across cross-frame clicks.
241
273
  const stopByExplicitButton = source === 'button';
242
274
  if (btn.classList.contains('stop-mode') && stopByExplicitButton) {
243
- apiFire('/api/stop', 'POST');
275
+ // On /:seq this stops only that session. Elsewhere the body carries no
276
+ // session and the server falls back to stopping everything, which is what
277
+ // the single-session view has always done.
278
+ apiFire('/api/stop', 'POST', withCurrentSessionBody({}));
244
279
  return;
245
280
  }
246
281
 
247
282
  // Double-submit guard: if a previous send is still in flight, drop this call.
248
283
  if (__chatSending) return;
249
284
 
250
- const text = input.value.trim();
251
285
  if (!text && !state.attachedFiles.length) return;
252
286
  clearUnreadResponses();
253
287
 
@@ -298,7 +332,7 @@ export async function sendMessage(source: SendSource = 'enter'): Promise<void> {
298
332
  const commandText = buildSlashCommandAttachmentText(text, paths);
299
333
  const commandResponse = await postSlashCommand(commandText);
300
334
  await handleSlashCommandResponse(commandText, commandResponse, async () => {
301
- await apiJson('/api/message', 'POST', { prompt });
335
+ await apiJson('/api/message', 'POST', withCurrentSessionBody({ prompt }));
302
336
  });
303
337
  } catch (err) {
304
338
  addSystemMsg(t('chat.cmd.fail', { msg: (err as Error).message }), '', 'error');
@@ -307,7 +341,7 @@ export async function sendMessage(source: SendSource = 'enter'): Promise<void> {
307
341
  }
308
342
  return;
309
343
  }
310
- await apiJson('/api/message', 'POST', { prompt });
344
+ await apiJson('/api/message', 'POST', withCurrentSessionBody({ prompt }));
311
345
  } catch (err) {
312
346
  addSystemMsg(t('chat.file.uploadFail', { msg: (err as Error).message }));
313
347
  clearAttachedFiles();
@@ -539,6 +573,10 @@ export function initDragDrop(): void {
539
573
 
540
574
  /** Upload recorded voice blob, combine with pending text/files, send unified message */
541
575
  export async function sendVoiceToServer(blob: Blob, ext: string, mime: string): Promise<void> {
576
+ if (!canSendFromCurrentView()) {
577
+ showReadOnlySwitchAffordance();
578
+ return;
579
+ }
542
580
  const input = document.getElementById('chatInput') as HTMLTextAreaElement | null;
543
581
  const pendingText = input?.value.trim() || '';
544
582
  const pendingFiles = [...state.attachedFiles];
@@ -583,12 +621,12 @@ export async function sendVoiceToServer(blob: Blob, ext: string, mime: string):
583
621
  // Step 3: Build combined prompt and send via /api/message
584
622
  const promptParts: string[] = [];
585
623
  for (const p of filePaths) {
586
- promptParts.push(t('chat.file.sent', { path: p }));
624
+ promptParts.push(attachmentLine(p));
587
625
  }
588
626
  promptParts.push(`🎤 ${sttResult.text}`);
589
627
  if (pendingText) promptParts.push(pendingText);
590
628
 
591
- await apiJson('/api/message', 'POST', { prompt: promptParts.join('\n') });
629
+ await apiJson('/api/message', 'POST', withCurrentSessionBody({ prompt: promptParts.join('\n') }));
592
630
  } catch (err) {
593
631
  addSystemMsg(t('voice.sttFail', { msg: (err as Error).message }), '', 'error');
594
632
  }
@@ -8,6 +8,8 @@ import {
8
8
  type NormalizedSpec,
9
9
  } from './elicitation-state.js';
10
10
  import { escapeHtml } from '../render/html.js';
11
+ import { canSendFromCurrentView, showReadOnlySwitchAffordance } from './session-hub.js';
12
+ import { t } from './i18n.js';
11
13
 
12
14
  type ElicitationKind = 'elicitation' | 'choice-buttons';
13
15
 
@@ -262,6 +264,19 @@ function createInputEvent(input: Element, type: string): Event {
262
264
 
263
265
  function submitComposedPrompt(block: HTMLElement, state: ElicitationState): void {
264
266
  if (block.dataset['elicitationState'] === SUBMITTING_STATE) return;
267
+ if (!canSendFromCurrentView()) {
268
+ showReadOnlySwitchAffordance();
269
+ block.classList.add('elicitation-error');
270
+ let notice = block.querySelector<HTMLElement>('.elicitation-readonly-notice');
271
+ if (!notice) {
272
+ notice = document.createElement('div');
273
+ notice.className = 'elicitation-readonly-notice';
274
+ notice.setAttribute('role', 'status');
275
+ block.appendChild(notice);
276
+ }
277
+ notice.textContent = t('sessionReadonly.elicitationRejected');
278
+ return;
279
+ }
265
280
  block.dataset['elicitationState'] = SUBMITTING_STATE;
266
281
  const input = document.getElementById('chatInput') as HTMLTextAreaElement | HTMLInputElement | null;
267
282
  if (!input) {
@@ -223,3 +223,37 @@ export async function clearCache(): Promise<void> {
223
223
  console.warn('[idb-cache] clearCache failed:', e);
224
224
  }
225
225
  }
226
+
227
+ // Clearing one session's screen must not erase another session's offline fallback:
228
+ // with several tabs open, a global wipe makes every other tab re-fetch its whole
229
+ // history for a clear it did not ask for (072 §1.3a).
230
+ //
231
+ // It takes no scope on purpose. This store is keyed by location and working directory
232
+ // (message-history.ts), which is a different namespace from the execution scopes the
233
+ // server puts on events, and a caller passing one of those would name a key that was
234
+ // never written and silently delete nothing.
235
+ export async function clearScopedCache(): Promise<void> {
236
+ const targetScope = currentScope;
237
+ if (!targetScope) {
238
+ await clearCache();
239
+ return;
240
+ }
241
+ try {
242
+ const db = await openDB();
243
+ const tx = db.transaction(STORE, 'readwrite');
244
+ const store = tx.objectStore(STORE);
245
+ const cursorReq = store.index('scope').openCursor(IDBKeyRange.only(targetScope));
246
+ cursorReq.onsuccess = () => {
247
+ const cursor = cursorReq.result;
248
+ if (!cursor) return;
249
+ cursor.delete();
250
+ cursor.continue();
251
+ };
252
+ await new Promise<void>((resolve, reject) => {
253
+ tx.oncomplete = () => resolve();
254
+ tx.onerror = () => reject(tx.error);
255
+ });
256
+ } catch (e) {
257
+ console.warn('[idb-cache] clearScopedCache failed:', e);
258
+ }
259
+ }
@@ -21,6 +21,7 @@ import { normalizeMessageToolLog, parseToolLog, toProcessSteps, type MessageItem
21
21
  import { canFollowAfterRestore, ensureScrollTracking, markFollowingBottom, settleChatBottomAfterInitialLoad } from './chat-scroll.js';
22
22
  import { updateStatMsgs } from './ui-status.js';
23
23
  import { seedCompletedElicitationsFromMessages } from './elicitation-state.js';
24
+ import { withCurrentSessionQuery } from './session-hub.js';
24
25
 
25
26
  export function buildVirtualHistoryItems(msgs: MessageItem[]): VirtualItem[] {
26
27
  return msgs.map((m, index) => buildLazyVirtualMessageItem(normalizeMessageToolLog(m), index));
@@ -37,7 +38,7 @@ export function buildVirtualHistoryItems(msgs: MessageItem[]): VirtualItem[] {
37
38
  const BOOT_MESSAGE_WINDOW = 3000;
38
39
 
39
40
  export function bootMessageQuery(): string {
40
- return `?limit=${BOOT_MESSAGE_WINDOW}`;
41
+ return withCurrentSessionQuery(`?limit=${BOOT_MESSAGE_WINDOW}`);
41
42
  }
42
43
 
43
44
  function normalizeMessageScopePart(value: string | null | undefined): string {