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
@@ -302,6 +302,103 @@ h2 { font-size: 13px; }
302
302
  .quick-btn.action-open { text-decoration: none; color: var(--text-secondary); }
303
303
  .quick-btn.action-open:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, var(--border-subtle)); }
304
304
  .quick-btn:disabled, .quick-btn.is-disabled { opacity: 0.35; pointer-events: none; }
305
+ .quick-btn.action-sessions[aria-expanded="true"] {
306
+ color: var(--accent);
307
+ border-color: color-mix(in srgb, var(--accent) 40%, var(--border-subtle));
308
+ }
309
+
310
+ /* ─── Active-row session disclosure (devlog 260806 D2) ─── */
311
+ .instance-session-list {
312
+ display: grid;
313
+ gap: 2px;
314
+ margin: -2px 0 8px;
315
+ padding: 6px;
316
+ border: 1px solid var(--border-subtle);
317
+ border-radius: 8px;
318
+ background: var(--row-bg);
319
+ animation: session-list-in 0.12s ease;
320
+ }
321
+ @keyframes session-list-in {
322
+ from { opacity: 0; transform: translateY(-2px); }
323
+ to { opacity: 1; transform: none; }
324
+ }
325
+ @media (prefers-reduced-motion: reduce) {
326
+ .instance-session-list { animation: none; }
327
+ }
328
+ .instance-session-row {
329
+ display: flex;
330
+ align-items: center;
331
+ justify-content: space-between;
332
+ gap: 8px;
333
+ width: 100%;
334
+ padding: 5px 8px;
335
+ border: 0;
336
+ border-radius: 6px;
337
+ background: transparent;
338
+ color: var(--text-secondary);
339
+ font-size: 12px;
340
+ text-align: left;
341
+ cursor: pointer;
342
+ }
343
+ .instance-session-row:hover:not(:disabled) { background: var(--row-bg-hover); color: var(--text-primary); }
344
+ .instance-session-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
345
+ .instance-session-row:disabled { cursor: default; opacity: 0.6; }
346
+ .instance-session-row.is-active {
347
+ color: var(--text-primary);
348
+ box-shadow: inset 3px 0 0 var(--accent);
349
+ background: color-mix(in srgb, var(--accent) 5%, transparent);
350
+ }
351
+ .instance-session-title {
352
+ min-width: 0;
353
+ display: flex;
354
+ align-items: baseline;
355
+ gap: 6px;
356
+ overflow: hidden;
357
+ text-overflow: ellipsis;
358
+ white-space: nowrap;
359
+ }
360
+ .session-source-badge {
361
+ flex-shrink: 0;
362
+ padding: 1px 5px;
363
+ border: 1px solid var(--border-subtle);
364
+ border-radius: 999px;
365
+ font-size: 10px;
366
+ color: var(--text-tertiary, var(--text-secondary));
367
+ text-transform: lowercase;
368
+ }
369
+ .instance-session-meta {
370
+ flex-shrink: 0;
371
+ font-size: 11px;
372
+ color: var(--text-tertiary, var(--text-secondary));
373
+ font-variant-numeric: tabular-nums;
374
+ }
375
+ .instance-session-error, .instance-session-empty {
376
+ padding: 5px 8px;
377
+ font-size: 11px;
378
+ color: var(--text-secondary);
379
+ }
380
+ .instance-session-error {
381
+ display: flex;
382
+ align-items: center;
383
+ justify-content: space-between;
384
+ gap: 8px;
385
+ color: var(--status-error);
386
+ }
387
+ .instance-session-retry {
388
+ flex: 0 0 auto;
389
+ padding: 2px 7px;
390
+ border: 1px solid var(--border-subtle);
391
+ border-radius: 5px;
392
+ background: var(--row-bg);
393
+ color: var(--text-secondary);
394
+ font: inherit;
395
+ cursor: pointer;
396
+ }
397
+ .instance-session-retry:hover {
398
+ color: var(--text-primary);
399
+ background: var(--surface-raised);
400
+ }
401
+ .instance-session-retry:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
305
402
  .instance-row-title { min-width: 0; display: grid; gap: 3px; }
306
403
  .instance-row-title-line {
307
404
  min-width: 0;
@@ -34,7 +34,17 @@ export function validateImportPayload(text: string): { ok: true; value: Record<s
34
34
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
35
35
  return { ok: false, error: 'Import payload must be a JSON object.' };
36
36
  }
37
- return { ok: true, value: parsed as Record<string, unknown> };
37
+ return { ok: true, value: stripServerOwnedSettingsFields(parsed as Record<string, unknown>) };
38
+ }
39
+
40
+ export function stripServerOwnedSettingsFields(settings: Record<string, unknown>): Record<string, unknown> {
41
+ const {
42
+ settingsSchemaVersion: _schema,
43
+ runtimeDefaultMigration: _migration,
44
+ multiSessionDefaultMigration: _sessionMigration,
45
+ ...userOwned
46
+ } = settings;
47
+ return userOwned;
38
48
  }
39
49
 
40
50
  export default function AdvancedExport({ port, client, triggerDownload }: ExportImportProps) {
@@ -55,7 +65,7 @@ export default function AdvancedExport({ port, client, triggerDownload }: Export
55
65
  const onExport = useCallback(() => {
56
66
  if (state.kind !== 'ready') return;
57
67
  const filename = `cli-jaw-settings-${port}.json`;
58
- download(filename, JSON.stringify(state.data, null, 2));
68
+ download(filename, JSON.stringify(stripServerOwnedSettingsFields(state.data), null, 2));
59
69
  }, [state, port, download]);
60
70
 
61
71
  const onImport = useCallback(async () => {
@@ -35,6 +35,8 @@ import {
35
35
  splitAgentSaveBundle,
36
36
  type AgentSettingsSnapshot,
37
37
  } from './components/agent/agent-save';
38
+ import { SettingsRequestError } from '../settings-client';
39
+ import { describeError } from '../components/error-normalize';
38
40
 
39
41
  export { splitAgentSaveBundle };
40
42
 
@@ -44,8 +46,30 @@ type AgentSnapshot = AgentSettingsSnapshot & {
44
46
  permissions?: 'auto' | string[] | unknown;
45
47
  perCli?: Record<string, PerCliEntry>;
46
48
  activeOverrides?: Record<string, ActiveOverride>;
49
+ runtimeDefaultMigration?: {
50
+ id: string;
51
+ state: 'pending' | 'accepted' | 'kept' | 'already-codex-app';
52
+ fromCli: string;
53
+ toCli: 'codex-app';
54
+ } | null;
47
55
  };
48
56
 
57
+ type CliStatusInfo = {
58
+ available: boolean | null;
59
+ capabilityReady: boolean | null;
60
+ probeState: 'checking' | 'fresh' | 'stale';
61
+ };
62
+
63
+ export function conflictSettingsFromError(error: unknown): AgentSnapshot | null {
64
+ if (!(error instanceof SettingsRequestError) || error.status !== 409) return null;
65
+ try {
66
+ const payload = JSON.parse(error.detail) as { settings?: AgentSnapshot };
67
+ return payload.settings ?? null;
68
+ } catch {
69
+ return null;
70
+ }
71
+ }
72
+
49
73
  type FlushSnapshot = {
50
74
  cli?: string;
51
75
  model?: string;
@@ -80,6 +104,11 @@ export default function Agent({ port, client, dirty, registerSave }: SettingsPag
80
104
  const [employeeLoading, setEmployeeLoading] = useState(true);
81
105
  const [employeeError, setEmployeeError] = useState<string | null>(null);
82
106
  const [cliMeta, setCliMeta] = useState<Record<string, CliMeta> | null>(null);
107
+ const [cliStatus, setCliStatus] = useState<Record<string, CliStatusInfo>>({});
108
+ const [migrationBusy, setMigrationBusy] = useState(false);
109
+ const [migrationError, setMigrationError] = useState<string | null>(null);
110
+ const [sessionMigrationBusy, setSessionMigrationBusy] = useState(false);
111
+ const [sessionMigrationError, setSessionMigrationError] = useState<string | null>(null);
83
112
 
84
113
  const loadCliMeta = useCallback(async () => {
85
114
  try {
@@ -93,6 +122,11 @@ export default function Agent({ port, client, dirty, registerSave }: SettingsPag
93
122
  }
94
123
  }, [client]);
95
124
 
125
+ const loadCliStatus = useCallback(async () => {
126
+ try { setCliStatus(await client.get<Record<string, CliStatusInfo>>('/api/cli-status')); }
127
+ catch { setCliStatus({}); }
128
+ }, [client]);
129
+
96
130
  const loadFlush = useCallback(async () => {
97
131
  setFlushLoading(true);
98
132
  setFlushError(null);
@@ -125,9 +159,10 @@ export default function Agent({ port, client, dirty, registerSave }: SettingsPag
125
159
 
126
160
  useEffect(() => {
127
161
  void loadCliMeta();
162
+ void loadCliStatus();
128
163
  void loadFlush();
129
164
  void loadEmployees();
130
- }, [loadCliMeta, loadEmployees, loadFlush]);
165
+ }, [loadCliMeta, loadCliStatus, loadEmployees, loadFlush]);
131
166
 
132
167
  useEffect(() => {
133
168
  if (state.kind !== 'ready') return;
@@ -199,6 +234,58 @@ export default function Agent({ port, client, dirty, registerSave }: SettingsPag
199
234
  : effortChoicesForModel(activeMeta, draft.model);
200
235
  const workingDirError = draft.workingDir.trim() ? null : 'Required';
201
236
 
237
+ const resolveMigration = useCallback(async (action: 'accept' | 'keep') => {
238
+ setMigrationBusy(true);
239
+ setMigrationError(null);
240
+ try {
241
+ const response = await client.post<AgentSnapshot | { data?: AgentSnapshot; settings?: AgentSnapshot }>(
242
+ '/api/settings/runtime-default-migration',
243
+ { action },
244
+ );
245
+ let next: AgentSnapshot;
246
+ if ('settings' in response && response.settings) next = response.settings as AgentSnapshot;
247
+ else if ('data' in response && response.data) next = response.data as AgentSnapshot;
248
+ else next = response as AgentSnapshot;
249
+ setData(next);
250
+ } catch (error) {
251
+ const conflictSettings = conflictSettingsFromError(error);
252
+ if (conflictSettings) {
253
+ setData(conflictSettings);
254
+ return;
255
+ }
256
+ setMigrationError(describeError(error));
257
+ } finally {
258
+ setMigrationBusy(false);
259
+ }
260
+ }, [client, setData]);
261
+
262
+ // Same shape, its own endpoint and its own busy state, because a v1 install has both
263
+ // migrations pending and answering one must not look like answering the other.
264
+ const resolveSessionMigration = useCallback(async (action: 'accept' | 'keep') => {
265
+ setSessionMigrationBusy(true);
266
+ setSessionMigrationError(null);
267
+ try {
268
+ const response = await client.post<AgentSnapshot | { data?: AgentSnapshot; settings?: AgentSnapshot }>(
269
+ '/api/settings/multi-session-default-migration',
270
+ { action },
271
+ );
272
+ let next: AgentSnapshot;
273
+ if ('settings' in response && response.settings) next = response.settings as AgentSnapshot;
274
+ else if ('data' in response && response.data) next = response.data as AgentSnapshot;
275
+ else next = response as AgentSnapshot;
276
+ setData(next);
277
+ } catch (error) {
278
+ const conflictSettings = conflictSettingsFromError(error);
279
+ if (conflictSettings) {
280
+ setData(conflictSettings);
281
+ return;
282
+ }
283
+ setSessionMigrationError(describeError(error));
284
+ } finally {
285
+ setSessionMigrationBusy(false);
286
+ }
287
+ }, [client, setData]);
288
+
202
289
  if (state.kind === 'loading') return <PageLoading />;
203
290
  if (state.kind === 'offline') return <PageOffline port={port} />;
204
291
  if (state.kind === 'error') return <PageError message={state.message} />;
@@ -221,6 +308,31 @@ export default function Agent({ port, client, dirty, registerSave }: SettingsPag
221
308
  void onSave();
222
309
  }}
223
310
  >
311
+ {settingsData.runtimeDefaultMigration?.state === 'pending' ? (
312
+ <div className="settings-inline-notice" role="status">
313
+ <strong>기본 런타임 변경 안내</strong>
314
+ <p>기존 선택은 유지됩니다. Codex App으로 전환하거나 현재 런타임을 계속 사용할 수 있습니다.</p>
315
+ <div className="settings-inline-actions">
316
+ <button type="button" className="settings-action settings-action-save" disabled={migrationBusy} onClick={() => void resolveMigration('accept')}>Codex App 사용</button>
317
+ <button type="button" className="settings-action settings-action-secondary" disabled={migrationBusy} onClick={() => void resolveMigration('keep')}>현재 런타임 유지</button>
318
+ </div>
319
+ {migrationError ? <span className="settings-field-error" role="alert">{migrationError}</span> : null}
320
+ </div>
321
+ ) : null}
322
+ {(settingsData['multiSessionDefaultMigration'] as Record<string, unknown> | undefined)?.['state'] === 'pending' ? (
323
+ <div className="settings-inline-notice" role="status">
324
+ <strong>다중 세션 안내</strong>
325
+ <p>대화 세션을 여러 개 열 수 있습니다. 켜면 동시 실행도 2로 올라가서, 두 번째 세션이 첫 번째가 끝나기를 기다리지 않습니다. 지금 설정은 그대로 유지됩니다.</p>
326
+ <div className="settings-inline-actions">
327
+ <button type="button" className="settings-action settings-action-save" disabled={sessionMigrationBusy} onClick={() => void resolveSessionMigration('accept')}>다중 세션 켜기</button>
328
+ <button type="button" className="settings-action settings-action-secondary" disabled={sessionMigrationBusy} onClick={() => void resolveSessionMigration('keep')}>지금 설정 유지</button>
329
+ </div>
330
+ {sessionMigrationError ? <span className="settings-field-error" role="alert">{sessionMigrationError}</span> : null}
331
+ </div>
332
+ ) : null}
333
+ {cliStatus[draft.cli]?.probeState === 'checking' ? (
334
+ <div className="settings-inline-notice" role="status" aria-live="polite">상태 확인 중</div>
335
+ ) : null}
224
336
  <RuntimeHeader
225
337
  cli={draft.cli}
226
338
  cliOptions={cliOptions.length > 0 ? cliOptions : [draft.cli || 'claude']}
@@ -102,7 +102,7 @@ export const CLI_META: Record<string, CliMeta> = {
102
102
  // parsed by Claude Code (stripped before send, enables 1M context
103
103
  // on Fable 5 + Sonnet 5 + Opus 5 + Opus 4.8/4.7/4.6 + Sonnet 4.6). Mirrors getDefaultClaudeChoices()
104
104
  // in src/cli/claude-models.ts. Verified via Grok web research
105
- // 2026-05-01 (devlog/_plan/260501_claude_model_passthrough/).
105
+ // 2026-05-01 (devlog/_fin/260501_claude_model_passthrough/).
106
106
  models: [
107
107
  'opus',
108
108
  'sonnet',
@@ -111,6 +111,16 @@ fi
111
111
  echo "Rebuilding better-sqlite3 for bundled Node $NODE_VERSION..."
112
112
  while IFS= read -r pkg_json; do
113
113
  pkg_dir="$(dirname "$pkg_json")"
114
+ # better-sqlite3 >= 13 is Node-API: it ships prebuilds/ inside the package,
115
+ # has NO scripts.install (`npm run install` dies with "Missing script"), and
116
+ # the prebuild is ABI-independent, so no per-Node rebuild is needed at all.
117
+ # v12 keeps the old install script ("prebuild-install || node-gyp rebuild").
118
+ # The verification step below opens the DB with the bundled Node either way.
119
+ has_install_script="$("$NODE_BIN" -e 'const p=require(process.argv[1]);process.stdout.write(p.scripts&&p.scripts.install?"yes":"no")' "$pkg_json")"
120
+ if [ "$has_install_script" = "no" ]; then
121
+ echo " skip rebuild (v13+ bundled prebuilds): ${pkg_dir#$SIDECAR_DIR/}"
122
+ continue
123
+ fi
114
124
  echo " rebuild: ${pkg_dir#$SIDECAR_DIR/}"
115
125
  (
116
126
  cd "$pkg_dir"
@@ -126,8 +136,23 @@ done < <(find "$SIDECAR_DIR/node_modules" -path '*/better-sqlite3/package.json'
126
136
 
127
137
  echo "Verifying better-sqlite3 opens with bundled Node..."
128
138
  "$NODE_BIN" -e "const Database = require('better-sqlite3'); new Database(':memory:').close()" && echo " better-sqlite3 OK" || {
129
- echo "ERROR: better-sqlite3 failed to open with bundled Node"
130
- exit 1
139
+ echo " bundled prebuild failed to load building from source (v13 build-release)..."
140
+ sidecar_bsql_dir="$SIDECAR_DIR/node_modules/better-sqlite3"
141
+ if [ -d "$sidecar_bsql_dir" ]; then
142
+ (
143
+ cd "$sidecar_bsql_dir"
144
+ PYTHON="$PYTHON_BIN" \
145
+ npm_config_python="$PYTHON_BIN" \
146
+ npm_config_runtime=node \
147
+ npm_config_target="$NODE_VERSION" \
148
+ npm_config_disturl="https://nodejs.org/dist" \
149
+ npm run build-release --foreground-scripts
150
+ )
151
+ fi
152
+ "$NODE_BIN" -e "const Database = require('better-sqlite3'); new Database(':memory:').close()" && echo " better-sqlite3 OK (source build)" || {
153
+ echo "ERROR: better-sqlite3 failed to open with bundled Node"
154
+ exit 1
155
+ }
131
156
  }
132
157
 
133
158
  echo "Cleaning up Node extract..."
@@ -173,5 +198,28 @@ node "$PROJECT_ROOT/scripts/check-electron-sidecar-no-jwc.cjs" --server-root "$S
173
198
  # Telegram bot could load.
174
199
  node "$PROJECT_ROOT/scripts/check-sidecar-smoke.mjs" --server-root "$SIDECAR_DIR"
175
200
 
201
+ # Sidecar install-state receipt. The sidecar is deliberately built with
202
+ # --ignore-scripts, so postinstall-guard never runs here and its receipt would
203
+ # be absent — which the runtime integrity check would misread as a blocked
204
+ # install and nag every desktop user. This is a controlled build: writing the
205
+ # receipt ourselves, with the sidecar's own package version, is the honest
206
+ # record of what happened.
207
+ echo "Writing sidecar install-state receipt..."
208
+ "$NODE_BIN" -e '
209
+ const fs = require("fs"), path = require("path");
210
+ const root = process.argv[1];
211
+ const packageVersion = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8")).version;
212
+ fs.writeFileSync(path.join(root, ".jaw-install-state.json"), JSON.stringify({
213
+ schema: 1,
214
+ state: "completed",
215
+ sidecar: true,
216
+ packageVersion,
217
+ ranAt: new Date().toISOString(),
218
+ node: process.version,
219
+ platform: process.platform,
220
+ arch: process.arch,
221
+ }, null, 2));
222
+ ' "$SIDECAR_DIR"
223
+
176
224
  echo "=== Sidecar ready ==="
177
225
  du -sh "$SIDECAR_DIR"
@@ -0,0 +1,72 @@
1
+ // Burst measurement for the 260806 rate-limit hardening unit (devlog 050).
2
+ // Three sequential phases so before/after runs are deterministic:
3
+ // 0. fetch + validate the Bearer token BEFORE any load (429-impossible here)
4
+ // 1. "sacrificial" lane: 350 unheadered (browser-class) GETs, concurrency 20
5
+ // — exceeds the browser poll budget (300/min) to force the 429 branch
6
+ // 2. "protected" lane: Bearer (cli-class) chat-sessions/switch/goal traffic
7
+ // — must see zero 429s after hardening; deterministically 429s before it
8
+ // (the old single 120/min bucket is already exhausted by phase 1).
9
+ // Usage: node scripts/burst-scenario.mjs --port <P> --switch-seq <N> [--sacrificial-count 350]
10
+
11
+ const args = process.argv.slice(2);
12
+ function argOf(name, fallback) {
13
+ const i = args.indexOf(name);
14
+ return i >= 0 ? args[i + 1] : fallback;
15
+ }
16
+ const PORT = Number(argOf('--port'));
17
+ const SWITCH_SEQ = argOf('--switch-seq', '');
18
+ const SACRIFICIAL = Number(argOf('--sacrificial-count', '350'));
19
+ if (!PORT) { console.error('usage: burst-scenario.mjs --port <P> --switch-seq <N>'); process.exit(1); }
20
+ const BASE = `http://127.0.0.1:${PORT}`;
21
+
22
+ // Phase 0 — token first: after phase 1 exhausts the shared bucket, a
23
+ // pre-hardening server would 429 the token endpoint itself.
24
+ const tokenRes = await fetch(`${BASE}/api/auth/token`);
25
+ const token = tokenRes.ok ? (await tokenRes.json()).token || '' : '';
26
+ if (!token) { console.error('token fetch failed — aborting'); process.exit(1); }
27
+ const AUTH = { Authorization: `Bearer ${token}` };
28
+
29
+ async function fire(url, init = {}) {
30
+ try {
31
+ const res = await fetch(url, init);
32
+ return { status: res.status, retryAfter: res.headers.get('retry-after') };
33
+ } catch {
34
+ return { status: 0, retryAfter: null };
35
+ }
36
+ }
37
+
38
+ // Phase 1 — sacrificial burst, bounded concurrency 20, single-window guard.
39
+ const phase1Start = Date.now();
40
+ const sacrificial = { total: 0, s429: 0, retryAfterSeen: false };
41
+ let inFlight = [];
42
+ for (let i = 0; i < SACRIFICIAL; i++) {
43
+ inFlight.push(fire(`${BASE}/api/messages/count`).then(r => {
44
+ sacrificial.total++;
45
+ if (r.status === 429) {
46
+ sacrificial.s429++;
47
+ if (r.retryAfter) sacrificial.retryAfterSeen = true;
48
+ }
49
+ }));
50
+ if (inFlight.length >= 20) { await Promise.all(inFlight); inFlight = []; }
51
+ }
52
+ await Promise.all(inFlight);
53
+ const phase1ElapsedMs = Date.now() - phase1Start;
54
+ if (phase1ElapsedMs > 45_000) {
55
+ console.error(`phase 1 took ${phase1ElapsedMs}ms — crossed the window boundary, scenario invalid`);
56
+ process.exit(1);
57
+ }
58
+
59
+ // Phase 2 — protected lane (cli class via Bearer).
60
+ const protectedLane = { total: 0, s429: 0 };
61
+ function track(promise) {
62
+ return promise.then(r => { protectedLane.total++; if (r.status === 429) protectedLane.s429++; });
63
+ }
64
+ const protectedCalls = [];
65
+ for (let i = 0; i < 20; i++) protectedCalls.push(track(fire(`${BASE}/api/chat-sessions`, { headers: AUTH })));
66
+ for (let i = 0; i < 5; i++) {
67
+ protectedCalls.push(track(fire(`${BASE}/api/chat-sessions/${SWITCH_SEQ}/switch`, { method: 'POST', headers: AUTH })));
68
+ }
69
+ for (let i = 0; i < 10; i++) protectedCalls.push(track(fire(`${BASE}/api/goal`, { headers: AUTH })));
70
+ await Promise.all(protectedCalls);
71
+
72
+ console.log(JSON.stringify({ tokenOk: true, phase1ElapsedMs, sacrificial, protected: protectedLane }, null, 2));
@@ -83,6 +83,7 @@ const ALLOWLIST = [
83
83
  { contains: '이 토픽은 인스턴스에 연결되지', why: 'fixed string' },
84
84
  { contains: 'ctx.answerCallbackQuery(ack', why: 'ack is masked where it is built' },
85
85
  { contains: 'ctx.reply(syncText)', why: 'syncText is masked where it is built' },
86
+ { contains: "persist failed:", why: "a Node.js Error.message from fs write, not a credential" },
86
87
  { contains: 'await ctx.reply(syncText)', why: 'syncText is masked where it is built' },
87
88
  { contains: 'Chat ID: <code>', why: 'a chat id the user just asked for' },
88
89
  ];
@@ -2,7 +2,7 @@
2
2
  // @file scripts/check-strict-baseline.mjs
3
3
  // Strict-migration regression gate (AST-aware).
4
4
  //
5
- // Per R2.2 (devlog/_plan/strict-migration/07-pro-review-r2.md), this scanner
5
+ // Per R2.2 (devlog/_fin/strict-migration/07-pro-review-r2.md), this scanner
6
6
  // uses the TypeScript Compiler API to walk source files and count AST nodes
7
7
  // of kind `AnyKeyword` plus any-typed `TypeReference` (Array<any>, Promise<any>,
8
8
  // Record<string, any>, etc.). Comments and string literals are automatically
@@ -7,7 +7,7 @@
7
7
  * rebuild it in-place before build/test steps continue.
8
8
  */
9
9
  const { execFileSync } = require('child_process');
10
- const { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync } = require('fs');
10
+ const { accessSync, constants, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync } = require('fs');
11
11
  const { delimiter, dirname, join } = require('path');
12
12
  const { createRequire } = require('module');
13
13
  const { createHash } = require('crypto');
@@ -58,7 +58,13 @@ function rebuildBetterSqlite3() {
58
58
  const betterSqliteDir = join(root, 'node_modules', 'better-sqlite3');
59
59
  if (existsSync(join(betterSqliteDir, 'package.json'))) {
60
60
  rmSync(join(betterSqliteDir, 'build'), { recursive: true, force: true });
61
- runNpm(['run', 'install', '--foreground-scripts'], {
61
+ // v12 has scripts.install ("prebuild-install || node-gyp rebuild").
62
+ // v13+ removed it (prebuilds ship in the package), and `npm rebuild`
63
+ // returns a no-op success there, so the real source-build script is
64
+ // build-release. Pick by reading the installed package's manifest.
65
+ const manifest = JSON.parse(readFileSync(join(betterSqliteDir, 'package.json'), 'utf8'));
66
+ const script = manifest.scripts && manifest.scripts.install ? 'install' : 'build-release';
67
+ runNpm(['run', script, '--foreground-scripts'], {
62
68
  stdio: 'inherit',
63
69
  cwd: betterSqliteDir,
64
70
  env: nativeBuildEnv(),
@@ -239,10 +245,51 @@ function releaseRepairLock() {
239
245
  try { rmSync(LOCK_DIR, { recursive: true, force: true }); } catch { /* best effort */ }
240
246
  }
241
247
 
248
+ /**
249
+ * Can this process write into the install tree at all? A global npm prefix is
250
+ * often root-owned; telling that user to "rebuild" is advice that cannot work.
251
+ */
252
+ function canWriteInstallTree() {
253
+ try {
254
+ accessSync(join(root, 'node_modules'), constants.W_OK);
255
+ return true;
256
+ } catch {
257
+ return false;
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Package-manager-aware recovery commands. npm >= 12 blocks unreviewed
263
+ * dependency install scripts, which is the likeliest reason a fresh global
264
+ * install is missing its native addon. npm's own printed remediation omits
265
+ * the package argument (npm/cli#9835), so we print the working form.
266
+ */
267
+ function recoveryHints() {
268
+ const lines = [];
269
+ if (/[\\/]\.bun[\\/]/.test(root)) {
270
+ lines.push('[jaw:native] reinstall with lifecycle scripts trusted (bun):');
271
+ lines.push('[jaw:native] bun add -g --trust cli-jaw');
272
+ } else if (/[\\/]pnpm[\\/]|[\\/]\.pnpm[\\/]/.test(root)) {
273
+ lines.push('[jaw:native] reinstall with build scripts allowed (pnpm 11+):');
274
+ lines.push('[jaw:native] pnpm add -g --allow-build=cli-jaw cli-jaw');
275
+ lines.push('[jaw:native] (pnpm <= 10: pnpm approve-builds -g)');
276
+ } else {
277
+ lines.push('[jaw:native] reinstall with install scripts allowed (npm >= 11.16):');
278
+ lines.push('[jaw:native] npm install -g cli-jaw --allow-scripts=cli-jaw');
279
+ lines.push('[jaw:native] npm config set allow-scripts=cli-jaw --location=user');
280
+ }
281
+ return lines;
282
+ }
283
+
242
284
  function reportMissing() {
243
285
  console.error('[jaw:native] ❌ dependencies are not installed (cannot resolve better-sqlite3).');
244
286
  console.error(`[jaw:native] install root: ${root}`);
245
- console.error('[jaw:native] run: npm install');
287
+ if (!canWriteInstallTree()) {
288
+ console.error('[jaw:native] install tree is not writable — a rebuild cannot help here.');
289
+ for (const line of recoveryHints()) console.error(line);
290
+ } else {
291
+ console.error('[jaw:native] run: npm install');
292
+ }
246
293
  }
247
294
 
248
295
  module.exports = {
@@ -67,6 +67,7 @@ function runPackageContentsCheck() {
67
67
  const required = [
68
68
  'scripts/install.sh',
69
69
  'scripts/install-wsl.sh',
70
+ 'scripts/install.ps1',
70
71
  'scripts/verify-fresh-install.sh',
71
72
  'scripts/collect-fresh-install-evidence.sh',
72
73
  'scripts/audit-fresh-install-evidence.mjs',
@@ -94,6 +95,13 @@ function runPackageContentsCheck() {
94
95
  return false;
95
96
  }
96
97
 
98
+ // The install-state receipt is written at install time; if it ever ships in
99
+ // the tarball it always exists, which makes blocked-install detection blind.
100
+ if ([...files].some((file) => file.endsWith('.jaw-install-state.json'))) {
101
+ console.error(`[install-risk] FAIL ${label}: .jaw-install-state.json must never ship in the package`);
102
+ return false;
103
+ }
104
+
97
105
  console.log(`[install-risk] PASS ${label}`);
98
106
  return true;
99
107
  }
@@ -37,6 +37,29 @@ HAS_SUDO=false
37
37
  NPM_PREFIX="$HOME/.local"
38
38
  NPM_PATH_LINE='export PATH="$HOME/.local/bin:$PATH"'
39
39
 
40
+ # npm >= 11.16 understands --allow-scripts; npm 12 blocks unreviewed dependency
41
+ # lifecycle scripts by default, so without this flag a global install can
42
+ # succeed while cli-jaw's postinstall silently never runs. Older npm rejects
43
+ # unknown config, so the flag is attached conditionally. (Duplicated from
44
+ # install.sh — both files are standalone curl-run scripts.)
45
+ JAW_ALLOW_SCRIPTS="cli-jaw"
46
+
47
+ jaw_npm_supports_allow_scripts() {
48
+ local npm_version major minor
49
+ npm_version="$(npm --version 2>/dev/null || true)"
50
+ major="$(printf '%s' "$npm_version" | cut -d. -f1)"
51
+ minor="$(printf '%s' "$npm_version" | cut -d. -f2)"
52
+ case "$major" in (''|*[!0-9]*) return 1 ;; esac
53
+ case "$minor" in (''|*[!0-9]*) minor=0 ;; esac
54
+ [ "$major" -gt 11 ] || { [ "$major" -eq 11 ] && [ "$minor" -ge 16 ]; }
55
+ }
56
+
57
+ jaw_allow_scripts_flag() {
58
+ if jaw_npm_supports_allow_scripts; then
59
+ printf '%s' "--allow-scripts=${JAW_ALLOW_SCRIPTS}"
60
+ fi
61
+ }
62
+
40
63
  # Guard: reject Windows HOME (e.g. /mnt/c/Users/...)
41
64
  case "$HOME" in
42
65
  /mnt/*) fail "HOME points to Windows path: $HOME — launch a proper WSL shell (wsl.exe -d Ubuntu)" ;;
@@ -315,15 +338,17 @@ verify_jaw_command() {
315
338
  # Step 3: Install cli-jaw
316
339
  # ═══════════════════════════════════════
317
340
  install_jaw() {
341
+ local allow_flag
342
+ allow_flag="$(jaw_allow_scripts_flag)"
318
343
  if command -v jaw &>/dev/null; then
319
344
  ok "cli-jaw already installed ($(jaw --version 2>/dev/null || echo 'unknown version'))"
320
345
  info "Updating to latest..."
321
346
  CLI_JAW_INSTALL_CLI_TOOLS=1 \
322
- npm install -g cli-jaw@latest
347
+ npm install -g cli-jaw@latest ${allow_flag:+$allow_flag}
323
348
  else
324
349
  info "Installing cli-jaw globally..."
325
350
  CLI_JAW_INSTALL_CLI_TOOLS=1 \
326
- npm install -g cli-jaw
351
+ npm install -g cli-jaw ${allow_flag:+$allow_flag}
327
352
  fi
328
353
 
329
354
  verify_jaw_command