@prestyj/cli 5.11.0 → 5.13.0

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 (466) hide show
  1. package/README.md +46 -36
  2. package/assets/skills/bulletproof/SKILL.md +158 -0
  3. package/assets/skills/bulletproof/references/agent-surface.md +82 -0
  4. package/assets/skills/bulletproof/references/audit-protocol.md +147 -0
  5. package/assets/skills/bulletproof/references/platform-playbooks.md +176 -0
  6. package/assets/skills/bulletproof/references/provenance.md +45 -0
  7. package/assets/skills/bulletproof/references/secure-defaults.md +108 -0
  8. package/assets/skills/bulletproof/references/supply-chain.md +76 -0
  9. package/assets/skills/bulletproof/references/threat-landscape.md +74 -0
  10. package/assets/skills/bulletproof/references/verification.md +65 -0
  11. package/assets/skills/compliance-guard/SKILL.md +251 -0
  12. package/assets/skills/compliance-guard/references/artifacts.md +186 -0
  13. package/assets/skills/compliance-guard/references/eu-uk.md +151 -0
  14. package/assets/skills/compliance-guard/references/exposure-triage.md +161 -0
  15. package/assets/skills/compliance-guard/references/lawsuit-vectors.md +141 -0
  16. package/assets/skills/compliance-guard/references/provenance.md +42 -0
  17. package/assets/skills/compliance-guard/references/sector-gates.md +173 -0
  18. package/assets/skills/compliance-guard/references/security-baseline.md +130 -0
  19. package/assets/skills/compliance-guard/references/trigger-map.md +163 -0
  20. package/assets/skills/compliance-guard/references/us.md +164 -0
  21. package/assets/skills/durable/SKILL.md +111 -0
  22. package/assets/skills/durable/references/backups-and-runtime.md +86 -0
  23. package/assets/skills/durable/references/migrations-and-schema.md +79 -0
  24. package/assets/skills/lean/SKILL.md +110 -0
  25. package/assets/skills/lean/references/memory-and-processes.md +98 -0
  26. package/assets/skills/lean/references/playbooks.md +107 -0
  27. package/dist/app-sidecar-auth-broadcast.test.js +111 -10
  28. package/dist/app-sidecar-auth-broadcast.test.js.map +1 -1
  29. package/dist/app-sidecar-sessions.test.js +1 -1
  30. package/dist/app-sidecar.js +210 -72
  31. package/dist/app-sidecar.js.map +1 -1
  32. package/dist/chat-agents/jiwa.test.js +1 -1
  33. package/dist/cli/auth.d.ts.map +1 -1
  34. package/dist/cli/auth.js +33 -36
  35. package/dist/cli/auth.js.map +1 -1
  36. package/dist/cli/mcp.js +30 -10
  37. package/dist/cli/mcp.js.map +1 -1
  38. package/dist/cli.d.ts.map +1 -1
  39. package/dist/cli.js +18 -5
  40. package/dist/cli.js.map +1 -1
  41. package/dist/config.d.ts +28 -5
  42. package/dist/config.d.ts.map +1 -1
  43. package/dist/config.js +50 -76
  44. package/dist/config.js.map +1 -1
  45. package/dist/config.test.js +56 -13
  46. package/dist/config.test.js.map +1 -1
  47. package/dist/core/agent-session-checkpoint-flush.test.js +2 -2
  48. package/dist/core/agent-session-compaction.test.js +60 -0
  49. package/dist/core/agent-session-compaction.test.js.map +1 -1
  50. package/dist/core/agent-session-crash-durability.test.js +3 -3
  51. package/dist/core/agent-session-model-switch.test.js +1 -1
  52. package/dist/core/agent-session-process-gate.test.js +7 -1
  53. package/dist/core/agent-session-process-gate.test.js.map +1 -1
  54. package/dist/core/agent-session-review-coverage.test.js +52 -1
  55. package/dist/core/agent-session-review-coverage.test.js.map +1 -1
  56. package/dist/core/agent-session-tool-tiers.test.d.ts +2 -0
  57. package/dist/core/agent-session-tool-tiers.test.d.ts.map +1 -0
  58. package/dist/core/agent-session-tool-tiers.test.js +138 -0
  59. package/dist/core/agent-session-tool-tiers.test.js.map +1 -0
  60. package/dist/core/agent-session-verification-gate.test.d.ts +2 -0
  61. package/dist/core/agent-session-verification-gate.test.d.ts.map +1 -0
  62. package/dist/core/agent-session-verification-gate.test.js +135 -0
  63. package/dist/core/agent-session-verification-gate.test.js.map +1 -0
  64. package/dist/core/agent-session.d.ts +77 -3
  65. package/dist/core/agent-session.d.ts.map +1 -1
  66. package/dist/core/agent-session.js +268 -33
  67. package/dist/core/agent-session.js.map +1 -1
  68. package/dist/core/agents.d.ts +29 -1
  69. package/dist/core/agents.d.ts.map +1 -1
  70. package/dist/core/agents.js +47 -97
  71. package/dist/core/agents.js.map +1 -1
  72. package/dist/core/agents.test.js +87 -7
  73. package/dist/core/agents.test.js.map +1 -1
  74. package/dist/core/api-benchmark.d.ts +3 -3
  75. package/dist/core/api-benchmark.js +7 -7
  76. package/dist/core/auth-providers.d.ts +37 -1
  77. package/dist/core/auth-providers.d.ts.map +1 -1
  78. package/dist/core/auth-providers.js +84 -4
  79. package/dist/core/auth-providers.js.map +1 -1
  80. package/dist/core/auto-update.test.js +1 -1
  81. package/dist/core/autopilot-gate.d.ts +2 -2
  82. package/dist/core/autopilot-gate.js +1 -1
  83. package/dist/core/autopilot-gate.test.js +7 -4
  84. package/dist/core/autopilot-gate.test.js.map +1 -1
  85. package/dist/core/bash-spawn-benchmark.js +6 -6
  86. package/dist/core/bundled-agents.d.ts +3 -0
  87. package/dist/core/bundled-agents.d.ts.map +1 -0
  88. package/dist/core/bundled-agents.js +255 -0
  89. package/dist/core/bundled-agents.js.map +1 -0
  90. package/dist/core/cache-warm-benchmark.d.ts +2 -2
  91. package/dist/core/cache-warm-benchmark.js +5 -5
  92. package/dist/core/code-retrieval-chunkers.d.ts +27 -0
  93. package/dist/core/code-retrieval-chunkers.d.ts.map +1 -0
  94. package/dist/core/code-retrieval-chunkers.js +264 -0
  95. package/dist/core/code-retrieval-chunkers.js.map +1 -0
  96. package/dist/core/code-retrieval-chunkers.test.d.ts +2 -0
  97. package/dist/core/code-retrieval-chunkers.test.d.ts.map +1 -0
  98. package/dist/core/code-retrieval-chunkers.test.js +203 -0
  99. package/dist/core/code-retrieval-chunkers.test.js.map +1 -0
  100. package/dist/core/code-retrieval.d.ts +6 -0
  101. package/dist/core/code-retrieval.d.ts.map +1 -1
  102. package/dist/core/code-retrieval.js +38 -1
  103. package/dist/core/code-retrieval.js.map +1 -1
  104. package/dist/core/compaction/token-estimator.test.js +1 -1
  105. package/dist/core/encode-cwd.test.js +1 -1
  106. package/dist/core/event-bus.d.ts +11 -1
  107. package/dist/core/event-bus.d.ts.map +1 -1
  108. package/dist/core/event-bus.js +1 -0
  109. package/dist/core/event-bus.js.map +1 -1
  110. package/dist/core/event-bus.test.d.ts +2 -0
  111. package/dist/core/event-bus.test.d.ts.map +1 -0
  112. package/dist/core/event-bus.test.js +33 -0
  113. package/dist/core/event-bus.test.js.map +1 -0
  114. package/dist/core/extensions/plugin-bundles.js +2 -2
  115. package/dist/core/extensions/plugin-bundles.js.map +1 -1
  116. package/dist/core/extensions/plugin-bundles.test.js +2 -2
  117. package/dist/core/extensions/plugin-bundles.test.js.map +1 -1
  118. package/dist/core/fast-apply-benchmark.d.ts +2 -2
  119. package/dist/core/fast-apply-benchmark.js +6 -6
  120. package/dist/core/foreign-session-import.test.js +1 -1
  121. package/dist/core/goal-lifecycle-smoke.test.js +2 -2
  122. package/dist/core/goal-store.js +1 -1
  123. package/dist/core/goal-store.test.js +2 -2
  124. package/dist/core/goal-verifier.test.js +2 -2
  125. package/dist/core/goal-worker-dev-server-lifecycle.test.js +1 -1
  126. package/dist/core/goal-worker.test.js +2 -2
  127. package/dist/core/hashline-edit-benchmark.d.ts +2 -2
  128. package/dist/core/hashline-edit-benchmark.js +7 -7
  129. package/dist/core/local-endpoint-store.test.js +1 -1
  130. package/dist/core/logger.d.ts +1 -1
  131. package/dist/core/logger.js +2 -2
  132. package/dist/core/logger.js.map +1 -1
  133. package/dist/core/lsp/client-navigation.test.d.ts +2 -0
  134. package/dist/core/lsp/client-navigation.test.d.ts.map +1 -0
  135. package/dist/core/lsp/client-navigation.test.js +135 -0
  136. package/dist/core/lsp/client-navigation.test.js.map +1 -0
  137. package/dist/core/lsp/client.d.ts +69 -0
  138. package/dist/core/lsp/client.d.ts.map +1 -1
  139. package/dist/core/lsp/client.js +151 -2
  140. package/dist/core/lsp/client.js.map +1 -1
  141. package/dist/core/lsp/integration.test.js +2 -2
  142. package/dist/core/lsp/manager-navigation.test.d.ts +2 -0
  143. package/dist/core/lsp/manager-navigation.test.d.ts.map +1 -0
  144. package/dist/core/lsp/manager-navigation.test.js +121 -0
  145. package/dist/core/lsp/manager-navigation.test.js.map +1 -0
  146. package/dist/core/lsp/manager.d.ts +35 -1
  147. package/dist/core/lsp/manager.d.ts.map +1 -1
  148. package/dist/core/lsp/manager.js +92 -0
  149. package/dist/core/lsp/manager.js.map +1 -1
  150. package/dist/core/lsp/servers.test.js +1 -1
  151. package/dist/core/lsp/windows.test.js +4 -4
  152. package/dist/core/mcp/catalog-cache.test.js +1 -1
  153. package/dist/core/mcp/client-session-recovery.test.js +1 -1
  154. package/dist/core/mcp/client.d.ts.map +1 -1
  155. package/dist/core/mcp/client.js +2 -10
  156. package/dist/core/mcp/client.js.map +1 -1
  157. package/dist/core/mcp/content.d.ts +19 -0
  158. package/dist/core/mcp/content.d.ts.map +1 -0
  159. package/dist/core/mcp/content.js +172 -0
  160. package/dist/core/mcp/content.js.map +1 -0
  161. package/dist/core/mcp/content.test.d.ts +2 -0
  162. package/dist/core/mcp/content.test.d.ts.map +1 -0
  163. package/dist/core/mcp/content.test.js +135 -0
  164. package/dist/core/mcp/content.test.js.map +1 -0
  165. package/dist/core/mcp/defaults.d.ts +3 -1
  166. package/dist/core/mcp/defaults.d.ts.map +1 -1
  167. package/dist/core/mcp/defaults.js +9 -2
  168. package/dist/core/mcp/defaults.js.map +1 -1
  169. package/dist/core/mcp/defaults.test.d.ts +2 -0
  170. package/dist/core/mcp/defaults.test.d.ts.map +1 -0
  171. package/dist/core/mcp/defaults.test.js +60 -0
  172. package/dist/core/mcp/defaults.test.js.map +1 -0
  173. package/dist/core/mcp/deferred-catalog.d.ts +1 -1
  174. package/dist/core/mcp/deferred-catalog.js +1 -1
  175. package/dist/core/mcp/elicitation.test.js +1 -1
  176. package/dist/core/mcp/legacy-negotiation.test.js +2 -2
  177. package/dist/core/mcp/oauth-store.test.js +2 -2
  178. package/dist/core/mcp/resolve-stdio.test.js +5 -5
  179. package/dist/core/mcp/shared-pool.test.js +5 -5
  180. package/dist/core/mcp/store.test.js +5 -5
  181. package/dist/core/mcp/when-connected.test.js +2 -2
  182. package/dist/core/nolan-model.test.js +1 -1
  183. package/dist/core/nolan-prompt.js +5 -5
  184. package/dist/core/oauth/xai.d.ts +2 -0
  185. package/dist/core/oauth/xai.d.ts.map +1 -0
  186. package/dist/core/oauth/xai.js +4 -0
  187. package/dist/core/oauth/xai.js.map +1 -0
  188. package/dist/core/persistent-shell.js +1 -1
  189. package/dist/core/persistent-shell.test.js +6 -6
  190. package/dist/core/process-manager-dev-server-repro.test.js +51 -12
  191. package/dist/core/process-manager-dev-server-repro.test.js.map +1 -1
  192. package/dist/core/process-manager-notifications.test.js +24 -14
  193. package/dist/core/process-manager-notifications.test.js.map +1 -1
  194. package/dist/core/process-manager-wake.test.d.ts +2 -0
  195. package/dist/core/process-manager-wake.test.d.ts.map +1 -0
  196. package/dist/core/process-manager-wake.test.js +96 -0
  197. package/dist/core/process-manager-wake.test.js.map +1 -0
  198. package/dist/core/process-manager.d.ts +61 -1
  199. package/dist/core/process-manager.d.ts.map +1 -1
  200. package/dist/core/process-manager.js +204 -4
  201. package/dist/core/process-manager.js.map +1 -1
  202. package/dist/core/progress/git-xp.test.js +2 -2
  203. package/dist/core/progress/ranks.d.ts +14 -3
  204. package/dist/core/progress/ranks.d.ts.map +1 -1
  205. package/dist/core/progress/ranks.js +181 -12
  206. package/dist/core/progress/ranks.js.map +1 -1
  207. package/dist/core/progress/ranks.test.js +48 -7
  208. package/dist/core/progress/ranks.test.js.map +1 -1
  209. package/dist/core/progress/rebuild.test.js +1 -1
  210. package/dist/core/progress/store.test.js +1 -1
  211. package/dist/core/progress/types.d.ts +2 -1
  212. package/dist/core/progress/types.d.ts.map +1 -1
  213. package/dist/core/project-discovery.d.ts.map +1 -1
  214. package/dist/core/project-discovery.js +62 -48
  215. package/dist/core/project-discovery.js.map +1 -1
  216. package/dist/core/project-discovery.test.js +10 -10
  217. package/dist/core/prompt-commands.d.ts.map +1 -1
  218. package/dist/core/prompt-commands.js +3 -3
  219. package/dist/core/prompt-commands.js.map +1 -1
  220. package/dist/core/provider-cache.e2e.test.d.ts +2 -0
  221. package/dist/core/provider-cache.e2e.test.d.ts.map +1 -0
  222. package/dist/core/provider-cache.e2e.test.js +125 -0
  223. package/dist/core/provider-cache.e2e.test.js.map +1 -0
  224. package/dist/core/radio.d.ts +1 -1
  225. package/dist/core/radio.d.ts.map +1 -1
  226. package/dist/core/radio.js +90 -6
  227. package/dist/core/radio.js.map +1 -1
  228. package/dist/core/radio.test.js +15 -2
  229. package/dist/core/radio.test.js.map +1 -1
  230. package/dist/core/run-journal.test.js +2 -2
  231. package/dist/core/sandbox.test.js +6 -6
  232. package/dist/core/semantic-search-benchmark.d.ts +2 -2
  233. package/dist/core/semantic-search-benchmark.js +5 -5
  234. package/dist/core/session-compaction.test.js +1 -1
  235. package/dist/core/session-injections.test.d.ts +2 -0
  236. package/dist/core/session-injections.test.d.ts.map +1 -0
  237. package/dist/core/session-injections.test.js +117 -0
  238. package/dist/core/session-injections.test.js.map +1 -0
  239. package/dist/core/session-manager.d.ts.map +1 -1
  240. package/dist/core/session-manager.js +83 -67
  241. package/dist/core/session-manager.js.map +1 -1
  242. package/dist/core/session-manager.test.js +7 -7
  243. package/dist/core/session-reconstructability.test.d.ts +2 -0
  244. package/dist/core/session-reconstructability.test.d.ts.map +1 -0
  245. package/dist/core/session-reconstructability.test.js +105 -0
  246. package/dist/core/session-reconstructability.test.js.map +1 -0
  247. package/dist/core/session-storage-fd-leak.test.d.ts +2 -0
  248. package/dist/core/session-storage-fd-leak.test.d.ts.map +1 -0
  249. package/dist/core/session-storage-fd-leak.test.js +116 -0
  250. package/dist/core/session-storage-fd-leak.test.js.map +1 -0
  251. package/dist/core/session-storage.d.ts +22 -2
  252. package/dist/core/session-storage.d.ts.map +1 -1
  253. package/dist/core/session-storage.js +33 -6
  254. package/dist/core/session-storage.js.map +1 -1
  255. package/dist/core/session-storage.test.js +2 -2
  256. package/dist/core/settings-manager.d.ts +12 -0
  257. package/dist/core/settings-manager.d.ts.map +1 -1
  258. package/dist/core/settings-manager.js +46 -1
  259. package/dist/core/settings-manager.js.map +1 -1
  260. package/dist/core/shell-path.js +1 -1
  261. package/dist/core/shell.d.ts +1 -1
  262. package/dist/core/shell.js +2 -2
  263. package/dist/core/shell.test.js +2 -2
  264. package/dist/core/shutdown.d.ts +71 -0
  265. package/dist/core/shutdown.d.ts.map +1 -0
  266. package/dist/core/shutdown.js +144 -0
  267. package/dist/core/shutdown.js.map +1 -0
  268. package/dist/core/shutdown.test.d.ts +2 -0
  269. package/dist/core/shutdown.test.d.ts.map +1 -0
  270. package/dist/core/shutdown.test.js +160 -0
  271. package/dist/core/shutdown.test.js.map +1 -0
  272. package/dist/core/sidecar-error-reporter.js +1 -1
  273. package/dist/core/sidecar-error-reporter.test.js +2 -2
  274. package/dist/core/skills-routing.test.js +142 -0
  275. package/dist/core/skills-routing.test.js.map +1 -1
  276. package/dist/core/skills.d.ts.map +1 -1
  277. package/dist/core/skills.js +4 -1
  278. package/dist/core/skills.js.map +1 -1
  279. package/dist/core/subagent-manager.d.ts +9 -0
  280. package/dist/core/subagent-manager.d.ts.map +1 -1
  281. package/dist/core/subagent-manager.js +45 -12
  282. package/dist/core/subagent-manager.js.map +1 -1
  283. package/dist/core/subagent-manager.test.js +14 -8
  284. package/dist/core/subagent-manager.test.js.map +1 -1
  285. package/dist/core/subagent-store.test.js +3 -3
  286. package/dist/core/task-store.js +1 -1
  287. package/dist/core/verification-gate.d.ts +55 -0
  288. package/dist/core/verification-gate.d.ts.map +1 -0
  289. package/dist/core/verification-gate.js +188 -0
  290. package/dist/core/verification-gate.js.map +1 -0
  291. package/dist/core/verification-gate.test.d.ts +2 -0
  292. package/dist/core/verification-gate.test.d.ts.map +1 -0
  293. package/dist/core/verification-gate.test.js +102 -0
  294. package/dist/core/verification-gate.test.js.map +1 -0
  295. package/dist/index.js +1 -1
  296. package/dist/index.js.map +1 -1
  297. package/dist/modes/acp-mode.js +2 -2
  298. package/dist/modes/acp-mode.test.js +10 -8
  299. package/dist/modes/acp-mode.test.js.map +1 -1
  300. package/dist/modes/agent-home-mode.d.ts.map +1 -1
  301. package/dist/modes/agent-home-mode.js +15 -11
  302. package/dist/modes/agent-home-mode.js.map +1 -1
  303. package/dist/modes/json-mode.d.ts +9 -1
  304. package/dist/modes/json-mode.d.ts.map +1 -1
  305. package/dist/modes/json-mode.js +3 -1
  306. package/dist/modes/json-mode.js.map +1 -1
  307. package/dist/modes/serve-mode.d.ts +1 -1
  308. package/dist/modes/serve-mode.d.ts.map +1 -1
  309. package/dist/modes/serve-mode.js +14 -9
  310. package/dist/modes/serve-mode.js.map +1 -1
  311. package/dist/modes/subagent-worker-mode.d.ts +5 -0
  312. package/dist/modes/subagent-worker-mode.d.ts.map +1 -1
  313. package/dist/modes/subagent-worker-mode.js.map +1 -1
  314. package/dist/session.test.js +1 -1
  315. package/dist/system-prompt.d.ts +37 -1
  316. package/dist/system-prompt.d.ts.map +1 -1
  317. package/dist/system-prompt.js +170 -12
  318. package/dist/system-prompt.js.map +1 -1
  319. package/dist/system-prompt.test.js +170 -11
  320. package/dist/system-prompt.test.js.map +1 -1
  321. package/dist/system-prompt.tiering.test.d.ts +2 -0
  322. package/dist/system-prompt.tiering.test.d.ts.map +1 -0
  323. package/dist/system-prompt.tiering.test.js +108 -0
  324. package/dist/system-prompt.tiering.test.js.map +1 -0
  325. package/dist/tools/bash.d.ts +4 -0
  326. package/dist/tools/bash.d.ts.map +1 -1
  327. package/dist/tools/bash.js +57 -3
  328. package/dist/tools/bash.js.map +1 -1
  329. package/dist/tools/bash.test.js +37 -0
  330. package/dist/tools/bash.test.js.map +1 -1
  331. package/dist/tools/code-nav.d.ts +30 -0
  332. package/dist/tools/code-nav.d.ts.map +1 -0
  333. package/dist/tools/code-nav.js +320 -0
  334. package/dist/tools/code-nav.js.map +1 -0
  335. package/dist/tools/code-nav.test.d.ts +2 -0
  336. package/dist/tools/code-nav.test.d.ts.map +1 -0
  337. package/dist/tools/code-nav.test.js +136 -0
  338. package/dist/tools/code-nav.test.js.map +1 -0
  339. package/dist/tools/edit.d.ts.map +1 -1
  340. package/dist/tools/edit.js +37 -4
  341. package/dist/tools/edit.js.map +1 -1
  342. package/dist/tools/edit.test.js +45 -1
  343. package/dist/tools/edit.test.js.map +1 -1
  344. package/dist/tools/generate-image.d.ts +1 -1
  345. package/dist/tools/goals.d.ts +2 -2
  346. package/dist/tools/goals.test.js +2 -2
  347. package/dist/tools/grep-parity.test.d.ts +2 -0
  348. package/dist/tools/grep-parity.test.d.ts.map +1 -0
  349. package/dist/tools/grep-parity.test.js +92 -0
  350. package/dist/tools/grep-parity.test.js.map +1 -0
  351. package/dist/tools/grep-recall.test.d.ts +2 -0
  352. package/dist/tools/grep-recall.test.d.ts.map +1 -0
  353. package/dist/tools/grep-recall.test.js +114 -0
  354. package/dist/tools/grep-recall.test.js.map +1 -0
  355. package/dist/tools/grep-spawn.test.d.ts +2 -0
  356. package/dist/tools/grep-spawn.test.d.ts.map +1 -0
  357. package/dist/tools/grep-spawn.test.js +66 -0
  358. package/dist/tools/grep-spawn.test.js.map +1 -0
  359. package/dist/tools/grep.d.ts +13 -3
  360. package/dist/tools/grep.d.ts.map +1 -1
  361. package/dist/tools/grep.js +340 -40
  362. package/dist/tools/grep.js.map +1 -1
  363. package/dist/tools/grep.test.js +17 -14
  364. package/dist/tools/grep.test.js.map +1 -1
  365. package/dist/tools/index.d.ts +7 -0
  366. package/dist/tools/index.d.ts.map +1 -1
  367. package/dist/tools/index.js +8 -1
  368. package/dist/tools/index.js.map +1 -1
  369. package/dist/tools/plan-mode.test.js +1 -1
  370. package/dist/tools/prompt-hints.d.ts +9 -0
  371. package/dist/tools/prompt-hints.d.ts.map +1 -1
  372. package/dist/tools/prompt-hints.js +62 -8
  373. package/dist/tools/prompt-hints.js.map +1 -1
  374. package/dist/tools/read-only-bash.d.ts +6 -0
  375. package/dist/tools/read-only-bash.d.ts.map +1 -1
  376. package/dist/tools/read-only-bash.js +168 -9
  377. package/dist/tools/read-only-bash.js.map +1 -1
  378. package/dist/tools/read-only-bash.test.js +49 -0
  379. package/dist/tools/read-only-bash.test.js.map +1 -1
  380. package/dist/tools/safe-env.js +1 -1
  381. package/dist/tools/search-code.d.ts.map +1 -1
  382. package/dist/tools/search-code.js +7 -6
  383. package/dist/tools/search-code.js.map +1 -1
  384. package/dist/tools/search-code.test.js +8 -2
  385. package/dist/tools/search-code.test.js.map +1 -1
  386. package/dist/tools/skill.js +3 -1
  387. package/dist/tools/skill.js.map +1 -1
  388. package/dist/tools/subagent-control.d.ts.map +1 -1
  389. package/dist/tools/subagent-control.js +20 -5
  390. package/dist/tools/subagent-control.js.map +1 -1
  391. package/dist/tools/subagent-control.test.js +34 -1
  392. package/dist/tools/subagent-control.test.js.map +1 -1
  393. package/dist/tools/subagent-shared.d.ts +20 -1
  394. package/dist/tools/subagent-shared.d.ts.map +1 -1
  395. package/dist/tools/subagent-shared.js +34 -7
  396. package/dist/tools/subagent-shared.js.map +1 -1
  397. package/dist/tools/subagent-shared.test.js +43 -9
  398. package/dist/tools/subagent-shared.test.js.map +1 -1
  399. package/dist/tools/subagent.d.ts.map +1 -1
  400. package/dist/tools/subagent.js +11 -8
  401. package/dist/tools/subagent.js.map +1 -1
  402. package/dist/tools/subagent.test.js +3 -0
  403. package/dist/tools/subagent.test.js.map +1 -1
  404. package/dist/tools/task-send.test.js +19 -10
  405. package/dist/tools/task-send.test.js.map +1 -1
  406. package/dist/tools/tool-catalog.test.d.ts +2 -0
  407. package/dist/tools/tool-catalog.test.d.ts.map +1 -0
  408. package/dist/tools/tool-catalog.test.js +124 -0
  409. package/dist/tools/tool-catalog.test.js.map +1 -0
  410. package/dist/tools/tool-search.d.ts.map +1 -1
  411. package/dist/tools/tool-search.js +2 -1
  412. package/dist/tools/tool-search.js.map +1 -1
  413. package/dist/tools/tool-tiers.d.ts +42 -0
  414. package/dist/tools/tool-tiers.d.ts.map +1 -0
  415. package/dist/tools/tool-tiers.js +78 -0
  416. package/dist/tools/tool-tiers.js.map +1 -0
  417. package/dist/tools/tool-tiers.test.d.ts +2 -0
  418. package/dist/tools/tool-tiers.test.d.ts.map +1 -0
  419. package/dist/tools/tool-tiers.test.js +48 -0
  420. package/dist/tools/tool-tiers.test.js.map +1 -0
  421. package/dist/tools/write.test.js +1 -1
  422. package/dist/ui/App.d.ts +3 -0
  423. package/dist/ui/App.d.ts.map +1 -1
  424. package/dist/ui/App.js +39 -3
  425. package/dist/ui/App.js.map +1 -1
  426. package/dist/ui/app-items.d.ts +2 -0
  427. package/dist/ui/app-items.d.ts.map +1 -1
  428. package/dist/ui/app-items.js +2 -0
  429. package/dist/ui/app-items.js.map +1 -1
  430. package/dist/ui/components/Banner.d.ts.map +1 -1
  431. package/dist/ui/components/Banner.js +24 -16
  432. package/dist/ui/components/Banner.js.map +1 -1
  433. package/dist/ui/components/ToolExecution.js +42 -34
  434. package/dist/ui/components/ToolExecution.js.map +1 -1
  435. package/dist/ui/components/force-color.d.ts +2 -0
  436. package/dist/ui/components/force-color.d.ts.map +1 -0
  437. package/dist/ui/components/force-color.js +11 -0
  438. package/dist/ui/components/force-color.js.map +1 -0
  439. package/dist/ui/components/themed-output.test.d.ts +2 -0
  440. package/dist/ui/components/themed-output.test.d.ts.map +1 -0
  441. package/dist/ui/components/themed-output.test.js +111 -0
  442. package/dist/ui/components/themed-output.test.js.map +1 -0
  443. package/dist/ui/error-item.d.ts +1 -1
  444. package/dist/ui/error-item.js +3 -3
  445. package/dist/ui/error-item.js.map +1 -1
  446. package/dist/ui/hooks/useAgentLoop.d.ts +4 -1
  447. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  448. package/dist/ui/hooks/useAgentLoop.js +27 -2
  449. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  450. package/dist/ui/login.js +2 -2
  451. package/dist/ui/login.js.map +1 -1
  452. package/dist/ui/render.d.ts +1 -1
  453. package/dist/ui/render.js +6 -6
  454. package/dist/ui/theme/dark-ansi.json +6 -0
  455. package/dist/ui/theme/dark-daltonized.json +6 -0
  456. package/dist/ui/theme/dark.json +6 -0
  457. package/dist/ui/theme/light-ansi.json +6 -0
  458. package/dist/ui/theme/light-daltonized.json +6 -0
  459. package/dist/ui/theme/light.json +6 -0
  460. package/dist/ui/theme/theme.d.ts +6 -0
  461. package/dist/ui/theme/theme.d.ts.map +1 -1
  462. package/dist/ui/tui-terminal-recorder.test.js +1 -1
  463. package/dist/utils/git.test.js +3 -3
  464. package/dist/utils/image.d.ts.map +1 -1
  465. package/dist/utils/image.js.map +1 -1
  466. package/package.json +7 -7
@@ -8,7 +8,7 @@
8
8
  * POST /prompt → { text } ; runs AgentSession.prompt(text)
9
9
  * POST /cancel → aborts the in-flight run
10
10
  *
11
- * The agent spine (gg-ai → gg-agent → gg-core) and every tool are reused
11
+ * The agent spine (@prestyj/ai → @prestyj/agent → @prestyj/core) and every tool are reused
12
12
  * unchanged via AgentSession — this file is only a network seam.
13
13
  */
14
14
  import http from "node:http";
@@ -39,14 +39,15 @@ import { sessionToMarkdown, defaultExportFilename, } from "./core/session-export
39
39
  import { AuthStorage } from "./core/auth-storage.js";
40
40
  import { cleanupToolOutputs } from "./tools/overflow.js";
41
41
  import { readCappedBody } from "./utils/http-body.js";
42
- import { fetchSubscriptionUsage, MOONSHOT_OAUTH_KEY, SubscriptionUsageError, XIAOMI_CREDITS_KEY, discoverLocalModels, findProbedModel, formatLocalModelId, parseLocalModelId, probeEndpoint, toModelInfo as localModelInfo, } from "@prestyj/core";
42
+ import { fetchSubscriptionUsage, SubscriptionUsageError, XIAOMI_CREDITS_KEY, dualAuthProvider, oauthStorageKey, discoverLocalModels, findProbedModel, formatLocalModelId, parseLocalModelId, probeEndpoint, toModelInfo as localModelInfo, } from "@prestyj/core";
43
43
  import { LocalEndpointError, addCustomEndpoint, listAllEndpoints, removeCustomEndpoint, syncEndpointCredentials, } from "./core/local-endpoint-store.js";
44
44
  import { loginAnthropic } from "./core/oauth/anthropic.js";
45
45
  import { loginOpenAI } from "./core/oauth/openai.js";
46
46
  import { loginGemini } from "./core/oauth/gemini.js";
47
47
  import { loginKimi } from "./core/oauth/kimi.js";
48
- import { AUTH_PROVIDERS } from "./core/auth-providers.js";
49
- import { ensureAppDirs, loadSavedSettings } from "./config.js";
48
+ import { loginXai } from "./core/oauth/xai.js";
49
+ import { AUTH_PROVIDERS, authPriorityNote, describeAuthMethods, } from "./core/auth-providers.js";
50
+ import { ensureAppDirs, loadSavedSettings, projectScopeAllowed } from "./config.js";
50
51
  import { SettingsManager } from "./core/settings-manager.js";
51
52
  import { installPlugin, listInstalledPlugins, removePlugin, } from "./core/extensions/plugin-bundles.js";
52
53
  import { getModel, getDefaultThinkingLevel, getContextWindow, getAllModels, clearRuntimeModels, registerRuntimeModels, } from "./core/model-registry.js";
@@ -61,6 +62,7 @@ import { discoverProjects } from "./core/project-discovery.js";
61
62
  import { listSidecarSessions } from "./app-sidecar-sessions.js";
62
63
  import { loadTasksSync, saveTasksSync, pruneDoneTasksSync, getNextPendingTask, markTaskInProgress, } from "./core/task-store.js";
63
64
  import { initLogger, log } from "./core/logger.js";
65
+ import { installTerminationHandlers } from "./core/shutdown.js";
64
66
  import { RADIO_STATIONS, getCurrentStation, getRadioVolume, playRadio, setRadioVolume, stopRadio, } from "./core/radio.js";
65
67
  import { enrichProcessPath } from "./core/shell-path.js";
66
68
  import { downscaleForPreview, shrinkToFit, validateVisionImage } from "./utils/image.js";
@@ -386,27 +388,49 @@ function mcpRowSummary(config) {
386
388
  }
387
389
  /** Load + connect every server, returning one wire row per server. Mirrors the
388
390
  * CLI dashboard's buildRows (connectAllDetailed, then dispose). Empty list
389
- * short-circuits before spawning any stdio process / opening any HTTP conn. */
390
- async function buildMcpRows(cwd) {
391
+ * short-circuits before spawning any stdio process / opening any HTTP conn.
392
+ * Project-scope servers run repo-controlled commands, so unless the user
393
+ * trusts them (trustProjectMcpServers) they are reported blocked WITHOUT
394
+ * being connected — even a status probe would spawn the process. */
395
+ async function buildMcpRows(cwd, settingsFile) {
391
396
  const scoped = await loadServers(cwd);
392
397
  if (scoped.length === 0)
393
398
  return [];
399
+ const settings = loadSavedSettings(settingsFile);
400
+ const allowProject = projectScopeAllowed(settings.trustProjectMcpServers, settings.trustedProjects, cwd);
401
+ const connectable = scoped.filter((s) => allowProject || s.scope !== "project");
402
+ const blocked = scoped.filter((s) => !allowProject && s.scope === "project");
394
403
  const manager = new MCPClientManager();
395
404
  try {
396
- const results = await manager.connectAllDetailed(scoped.map((s) => s.config));
397
- return scoped.map((s) => {
398
- const result = results.find((r) => r.name === s.config.name);
399
- return {
405
+ const results = connectable.length > 0
406
+ ? await manager.connectAllDetailed(connectable.map((s) => s.config))
407
+ : [];
408
+ return [
409
+ ...connectable.map((s) => {
410
+ const result = results.find((r) => r.name === s.config.name);
411
+ return {
412
+ name: s.config.name,
413
+ scope: s.scope,
414
+ ok: result?.ok ?? false,
415
+ toolCount: result?.toolCount ?? 0,
416
+ error: result?.error,
417
+ kind: s.config.url ? "http" : "stdio",
418
+ summary: mcpRowSummary(s.config),
419
+ requiresAuth: result?.requiresAuth,
420
+ };
421
+ }),
422
+ ...blocked.map((s) => ({
400
423
  name: s.config.name,
401
424
  scope: s.scope,
402
- ok: result?.ok ?? false,
403
- toolCount: result?.toolCount ?? 0,
404
- error: result?.error,
405
- kind: s.config.url ? "http" : "stdio",
425
+ ok: false,
426
+ toolCount: 0,
427
+ error: "Project-scope server not connected — this repo's .ezcoder/mcp.json runs " +
428
+ "repo-controlled commands. Add or re-add a server in this project via " +
429
+ "the MCP modal to trust it.",
430
+ kind: (s.config.url ? "http" : "stdio"),
406
431
  summary: mcpRowSummary(s.config),
407
- requiresAuth: result?.requiresAuth,
408
- };
409
- });
432
+ })),
433
+ ];
410
434
  }
411
435
  finally {
412
436
  await manager.dispose();
@@ -450,6 +474,8 @@ async function runJsonModeIfRequested() {
450
474
  model: { type: "string" },
451
475
  "max-turns": { type: "string" },
452
476
  "system-prompt": { type: "string" },
477
+ "agent-prompt": { type: "string" },
478
+ "agent-context": { type: "string" },
453
479
  tools: { type: "string" },
454
480
  "mcp-servers": { type: "string" },
455
481
  "prompt-cache-key": { type: "string" },
@@ -483,6 +509,8 @@ async function runJsonModeIfRequested() {
483
509
  model: values.model ?? "claude-opus-5",
484
510
  cwd: process.cwd(),
485
511
  systemPrompt: values["system-prompt"],
512
+ agentPrompt: values["agent-prompt"],
513
+ agentContext: values["agent-context"] === "none" ? "none" : undefined,
486
514
  maxTurns: maxTurnsRaw ? parseInt(maxTurnsRaw, 10) : undefined,
487
515
  allowedTools,
488
516
  allowedMcpServers,
@@ -504,9 +532,10 @@ function daemonReadBody(req, res) {
504
532
  return readCappedBody(req, res);
505
533
  }
506
534
  function daemonJson(res, status, body) {
535
+ // No CORS headers: only the Rust proxy (no Origin) should call this daemon.
536
+ // Granting origins would let any web page read responses from loopback.
507
537
  res.writeHead(status, {
508
538
  "content-type": "application/json",
509
- "access-control-allow-origin": "*",
510
539
  });
511
540
  res.end(JSON.stringify(body));
512
541
  }
@@ -524,6 +553,13 @@ async function main() {
524
553
  // GG_APP_LISTENING handshake and consumed by the shell.
525
554
  const port = Number(process.env.GG_APP_PORT ?? 0);
526
555
  const host = "127.0.0.1";
556
+ // Per-launch bearer token. The Rust shell generates one and passes it via
557
+ // GG_APP_TOKEN; spawned any other way (dev, tests, smoke) we mint our own
558
+ // and report it on the GG_APP_LISTENING line. Every request must carry it
559
+ // as x-ez-token: this daemon creates sessions for arbitrary cwds, runs
560
+ // prompts, and installs plugins, so an unauthenticated loopback port lets
561
+ // any local process drive the agent as the user.
562
+ const authToken = process.env.GG_APP_TOKEN ?? randomUUID();
527
563
  const paths = await ensureAppDirs();
528
564
  // Own log file so the app sidecar never clobbers the interactive CLI's
529
565
  // ~/.ezcoder/debug.log (initLogger truncates on each start).
@@ -586,7 +622,7 @@ async function main() {
586
622
  await auth.load();
587
623
  // Every window's session lives here as an in-process object, keyed by the id
588
624
  // the daemon hands back from POST /session. The Rust shell routes each proxy
589
- // request to its window's session via the `x-gg-session` header (and the
625
+ // request to its window's session via the `x-ez-session` header (and the
590
626
  // `?session=` query for the SSE /events stream).
591
627
  const sessions = new Map();
592
628
  /**
@@ -645,7 +681,7 @@ async function main() {
645
681
  const displayName = provider === "anthropic" ? "Anthropic" : provider === "openai" ? "Codex" : "Kimi";
646
682
  // Kimi plan usage is tracked on the OAuth credential specifically — the
647
683
  // Moonshot platform API key is metered per-token, not per plan window.
648
- const authKey = provider === "moonshot" ? MOONSHOT_OAUTH_KEY : provider;
684
+ const authKey = oauthStorageKey(provider) ?? provider;
649
685
  if (!(await auth.hasProviderAuth(authKey))) {
650
686
  // Logged out: drop the replay cache so a later login on a DIFFERENT
651
687
  // account can never inherit the previous one's numbers.
@@ -666,7 +702,14 @@ async function main() {
666
702
  // A provider can revoke an access token before its stored expiry. Refresh
667
703
  // once on 401, matching inference auth recovery, then retry the usage call.
668
704
  if (error instanceof SubscriptionUsageError && error.status === 401) {
669
- credentials = await auth.resolveCredentials(authKey, { forceRefresh: true });
705
+ // Name the rejected token. This poller runs on a timer alongside live
706
+ // agent runs, so an unconditional refresh here would invalidate the
707
+ // token those runs are holding and fail them mid-turn — a background
708
+ // status check must never be able to log out the foreground.
709
+ credentials = await auth.resolveCredentials(authKey, {
710
+ forceRefresh: true,
711
+ rejectedToken: credentials.accessToken,
712
+ });
670
713
  const snapshot = {
671
714
  ...(await fetchSubscriptionUsage(provider, credentials)),
672
715
  connected: true,
@@ -751,10 +794,10 @@ async function main() {
751
794
  usageRequests.delete(provider);
752
795
  }
753
796
  }
754
- /** Resolve the target session id: the `x-gg-session` header, else a
797
+ /** Resolve the target session id: the `x-ez-session` header, else a
755
798
  * `?session=` query param (used by the SSE /events connection). */
756
799
  function sessionIdFromReq(req, url) {
757
- const header = req.headers["x-gg-session"];
800
+ const header = req.headers["x-ez-session"];
758
801
  if (typeof header === "string" && header.length > 0)
759
802
  return header;
760
803
  try {
@@ -767,16 +810,26 @@ async function main() {
767
810
  const server = http.createServer(wrapSidecarHandler((req, res) => {
768
811
  const url = req.url ?? "/";
769
812
  const method = req.method ?? "GET";
770
- // CORS preflight — the webview origin differs from 127.0.0.1.
813
+ // Answer preflights with a bare 204 but grant NO origins — the webview
814
+ // reaches the daemon through the Rust proxy, never cross-origin, so any
815
+ // browser page's preflight must fail here.
771
816
  if (method === "OPTIONS") {
772
- res.writeHead(204, {
773
- "access-control-allow-origin": "*",
774
- "access-control-allow-methods": "GET, POST, DELETE, OPTIONS",
775
- "access-control-allow-headers": "content-type, x-gg-session",
776
- });
817
+ res.writeHead(204);
777
818
  res.end();
778
819
  return;
779
820
  }
821
+ // Host allowlist. The daemon binds 127.0.0.1 only; rejecting any other
822
+ // Host blocks DNS rebinding, where a web page's request arrives with
823
+ // the attacker's hostname (browsers cannot spoof Host).
824
+ const reqHost = req.headers.host ?? "";
825
+ if (!/^(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i.test(reqHost)) {
826
+ daemonJson(res, 403, { error: "forbidden host" });
827
+ return;
828
+ }
829
+ if (req.headers["x-ez-token"] !== authToken) {
830
+ daemonJson(res, 401, { error: "unauthorized" });
831
+ return;
832
+ }
780
833
  // ── Daemon-level routes (session lifecycle) ──────────────────────────
781
834
  // Create a session for a window: { mode?, cwd, sessionPath? } → { sessionId }.
782
835
  if (method === "POST" && url === "/session") {
@@ -874,28 +927,34 @@ async function main() {
874
927
  }, "app-sidecar.http"));
875
928
  server.listen(port, host, () => {
876
929
  const addr = server.address();
877
- // The Rust shell reads this line to learn the daemon port.
878
- process.stdout.write(`GG_APP_LISTENING ${addr.port}\n`);
930
+ // The Rust shell reads this line to learn the daemon port (it already
931
+ // knows the token — it set GG_APP_TOKEN; the field serves other spawners).
932
+ process.stdout.write(`GG_APP_LISTENING ${addr.port} ${authToken}\n`);
879
933
  log("INFO", "app-sidecar", "daemon listening", { port: String(addr.port), host });
880
934
  });
881
935
  const shellPid = process.ppid;
882
- let shuttingDown = false;
883
- async function shutdown() {
884
- if (shuttingDown)
885
- return;
886
- shuttingDown = true;
887
- clearInterval(parentWatch);
888
- // Radio playback is app-wide (one stream across all windows), so it stops
889
- // at the daemon level, not per session.
890
- stopRadio();
891
- // Close the ~/.ezcoder progress fs.watch handle (baseline #8 leak fix).
892
- progress.dispose();
893
- await Promise.all([...sessions.values()].map((c) => c.dispose().catch(() => { })));
894
- server.close();
895
- process.exit(0);
896
- }
897
- process.on("SIGINT", () => void shutdown());
898
- process.on("SIGTERM", () => void shutdown());
936
+ // Session teardown awaits MCP servers, LSP servers and third-party extension
937
+ // `deactivate()` hooks. Any can hang, so a deadline guarantees process exit.
938
+ const shutdown = installTerminationHandlers({
939
+ scope: "app-sidecar",
940
+ teardown: async () => {
941
+ clearInterval(parentWatch);
942
+ // Radio playback is app-wide (one stream across all windows).
943
+ stopRadio();
944
+ // Close the ~/.ezcoder progress fs.watch handle.
945
+ progress.dispose();
946
+ await Promise.all([...sessions.values()].map((c) => c.dispose().catch(() => { })));
947
+ server.close();
948
+ },
949
+ onTimeout: (timeoutMs) => {
950
+ // Radio is audible, so it must stop even when the rest is wedged.
951
+ stopRadio();
952
+ log("WARN", "app-sidecar", "daemon teardown hung; exiting on deadline", {
953
+ timeoutMs: String(timeoutMs),
954
+ sessions: String(sessions.size),
955
+ });
956
+ },
957
+ });
899
958
  process.once("exit", stopRadio);
900
959
  // Tauri can disappear without delivering a signal (force-quit, dev runner
901
960
  // teardown, crash). Detect reparenting or a dead shell so the daemon and its
@@ -911,7 +970,7 @@ async function main() {
911
970
  }
912
971
  }
913
972
  if (!parentAlive)
914
- void shutdown();
973
+ shutdown();
915
974
  }, 1_000);
916
975
  parentWatch.unref?.();
917
976
  }
@@ -1177,7 +1236,7 @@ async function createSession(deps, opts) {
1177
1236
  "Start it again and retry, or pick another model.");
1178
1237
  }
1179
1238
  // Turn any thrown value into the same clear headline/message/guidance shape
1180
- // the TUI shows (see gg-ai's formatError) instead of a bare `err.message`, log
1239
+ // the TUI shows (see @prestyj/ai's formatError) instead of a bare `err.message`, log
1181
1240
  // the full detail, and broadcast it under `type` ("error" or "nolan_error").
1182
1241
  // Without this the webview only ever saw a raw provider string like
1183
1242
  // `400 {"code":"400",...}` with no "is this me or them / when does it reset"
@@ -1587,6 +1646,12 @@ async function createSession(deps, opts) {
1587
1646
  durationMs: String(d.durationMs),
1588
1647
  isError: String(d.isError),
1589
1648
  ...(d.isError ? { result: d.result.slice(0, 500) } : {}),
1649
+ // Consecutive-failure count for schema rejections. Attempt 1 followed by
1650
+ // a success means the model self-corrected; reaching 3 means it looped
1651
+ // and the turn was ended. Grep this to tell the two apart.
1652
+ ...(d.invalidArgAttempt === undefined
1653
+ ? {}
1654
+ : { invalidArgAttempt: String(d.invalidArgAttempt) }),
1590
1655
  });
1591
1656
  broadcast("tool_call_end", d);
1592
1657
  // The agent can add/done/remove project tasks via the `tasks` tool during a
@@ -1610,12 +1675,25 @@ async function createSession(deps, opts) {
1610
1675
  session.eventBus.on("agent_done", (d) => broadcast("agent_done", d));
1611
1676
  // Non-clean stop (max_tokens/refusal/provider error) — info-style frame so
1612
1677
  // the webview can warn instead of presenting truncated output as complete.
1613
- session.eventBus.on("truncated", (d) => broadcast("truncated", d));
1678
+ // empty_response is a hard failure (no output at all): route it through
1679
+ // broadcastError so the app renders an error row — the webview does not
1680
+ // render bare "truncated" frames.
1681
+ session.eventBus.on("truncated", (d) => {
1682
+ if (d.reason === "empty_response") {
1683
+ broadcastError("error", "empty response", new Error("The model returned an empty response after retries — try sending again."));
1684
+ return;
1685
+ }
1686
+ broadcast("truncated", d);
1687
+ });
1614
1688
  session.eventBus.on("error", (d) => {
1615
1689
  broadcastError("error", "agent error", d.error);
1616
1690
  });
1617
1691
  session.eventBus.on("model_change", (d) => broadcast("model_change", d));
1618
1692
  session.eventBus.on("hook", (d) => broadcast("hook", d));
1693
+ // Fires BEFORE the candidate final answer streams. The webview holds assistant
1694
+ // text back while armed, so an Ideal review supersedes a draft that was never
1695
+ // painted instead of deleting one the user already started reading.
1696
+ session.eventBus.on("hook_armed", (d) => broadcast("hook_armed", d));
1619
1697
  session.eventBus.on("subagent_state", (d) => broadcast("subagent_state", d));
1620
1698
  session.eventBus.on("compaction_start", (d) => broadcast("compaction_start", d));
1621
1699
  session.eventBus.on("compaction_end", (d) => broadcast("compaction_end", d));
@@ -2372,10 +2450,52 @@ async function createSession(deps, opts) {
2372
2450
  // Native (Rust) writes to auth.json bypass this daemon's cache; re-read so
2373
2451
  // the reported connection state matches what's actually on disk.
2374
2452
  await auth.reload();
2375
- const providers = await Promise.all(AUTH_PROVIDERS.map(async (p) => ({
2376
- ...p,
2377
- connected: await auth.hasProviderAuth(p.value),
2378
- })));
2453
+ const providers = await Promise.all(AUTH_PROVIDERS.map(async (p) => {
2454
+ const dual = dualAuthProvider(p.value);
2455
+ // Which methods hold a credential right now. Dual-auth providers can hold
2456
+ // both at once, and the app needs them separately: one "connected" bit
2457
+ // cannot express "OAuth signed in, key also on file as backup", nor offer
2458
+ // a per-method disconnect.
2459
+ const oauthKey = dual?.oauthKey;
2460
+ const hasOAuth = oauthKey ? await auth.hasCredentials(oauthKey) : false;
2461
+ const apiKeyKeys = p.apiKeyVariants?.map((v) => v.key) ?? [p.value];
2462
+ const hasApiKey = p.methods.includes("apikey")
2463
+ ? (await Promise.all(apiKeyKeys.map((k) => auth.hasCredentials(k)))).some(Boolean)
2464
+ : false;
2465
+ const connectedMethods = [];
2466
+ // OAuth-only providers (Anthropic/OpenAI/Gemini) store under the provider
2467
+ // id itself, so `hasProviderAuth` is what proves their OAuth connection.
2468
+ if (p.methods.includes("oauth") &&
2469
+ (hasOAuth || (!dual && (await auth.hasCredentials(p.value)))))
2470
+ connectedMethods.push("oauth");
2471
+ if (hasApiKey)
2472
+ connectedMethods.push("apikey");
2473
+ // Which one a request would actually use, mirroring AuthStorage's
2474
+ // resolution: OAuth wins unless its usage window is exhausted AND a key
2475
+ // is configured to cover it.
2476
+ const exhaustedUntil = oauthKey
2477
+ ? ((await auth.getCredentials(oauthKey))?.usageExhaustedUntil ?? 0)
2478
+ : 0;
2479
+ const oauthSidelined = hasOAuth && Date.now() < exhaustedUntil && hasApiKey;
2480
+ const activeMethod = connectedMethods.includes("oauth")
2481
+ ? oauthSidelined
2482
+ ? "apikey"
2483
+ : "oauth"
2484
+ : connectedMethods[0];
2485
+ // `methodDetails` is the server-side lookup table behind
2486
+ // `methodGuidance` — shipping both would duplicate every string on the
2487
+ // wire for no consumer.
2488
+ const { methodDetails: _table, ...wireMeta } = p;
2489
+ return {
2490
+ ...wireMeta,
2491
+ connected: await auth.hasProviderAuth(p.value),
2492
+ connectedMethods,
2493
+ ...(activeMethod ? { activeMethod } : {}),
2494
+ ...(oauthSidelined ? { oauthExhaustedUntil: exhaustedUntil } : {}),
2495
+ ...(authPriorityNote(p.value) ? { priorityNote: authPriorityNote(p.value) } : {}),
2496
+ methodGuidance: describeAuthMethods(p.value),
2497
+ };
2498
+ }));
2379
2499
  return { providers };
2380
2500
  }
2381
2501
  // Background tasks have no event source (the bash tool just spawns them), so
@@ -2448,7 +2568,6 @@ async function createSession(deps, opts) {
2448
2568
  const payload = JSON.stringify(redactValue(body, { secrets: environmentSecrets(process.env) }));
2449
2569
  res.writeHead(status, {
2450
2570
  "content-type": "application/json",
2451
- "access-control-allow-origin": "*",
2452
2571
  });
2453
2572
  res.end(payload);
2454
2573
  }
@@ -2533,7 +2652,6 @@ async function createSession(deps, opts) {
2533
2652
  "content-type": "text/event-stream",
2534
2653
  "cache-control": "no-cache",
2535
2654
  connection: "keep-alive",
2536
- "access-control-allow-origin": "*",
2537
2655
  });
2538
2656
  res.write(`retry: 1000\n\n`);
2539
2657
  const client = { id: ++clientSeq, res };
@@ -3299,7 +3417,7 @@ async function createSession(deps, opts) {
3299
3417
  });
3300
3418
  // After the user's run settles, kick off Nolan's auto-review loop — but
3301
3419
  // only when the turn is actually reviewable (shouldStartAutopilotCycle):
3302
- // workflow commands (/compare, /bullet-proof, …) end with reports or
3420
+ // workflow commands (/compare, /expand, …) end with reports or
3303
3421
  // A/B/C choices reserved for the USER; registry commands (/help) and
3304
3422
  // failed runs add no assistant work to judge; a turn that ended in plan
3305
3423
  // mode has a pending Accept/Reject modal Nolan must not preempt. This is
@@ -4163,9 +4281,11 @@ async function createSession(deps, opts) {
4163
4281
  creds = await loginOpenAI(cb);
4164
4282
  else if (provider === "gemini")
4165
4283
  creds = await loginGemini(cb);
4166
- else if (provider === "moonshot") {
4167
- creds = await loginKimi(cb);
4168
- storageKey = MOONSHOT_OAUTH_KEY;
4284
+ else if (provider === "moonshot" || provider === "xai") {
4285
+ // Subscription OAuth (Kimi plan / SuperGrok-X Premium) stores under
4286
+ // a distinct key so it can coexist with the provider's API key.
4287
+ creds = provider === "moonshot" ? await loginKimi(cb) : await loginXai(cb);
4288
+ storageKey = dualAuthProvider(provider).oauthKey;
4169
4289
  }
4170
4290
  else {
4171
4291
  throw new Error(`OAuth not implemented for ${provider}`);
@@ -4258,22 +4378,34 @@ async function createSession(deps, opts) {
4258
4378
  if (raw === null)
4259
4379
  return;
4260
4380
  let provider;
4381
+ let logoutMethod;
4261
4382
  try {
4262
- provider = JSON.parse(raw).provider ?? "";
4383
+ const body = JSON.parse(raw);
4384
+ provider = body.provider ?? "";
4385
+ logoutMethod =
4386
+ body.method === "oauth" || body.method === "apikey" ? body.method : undefined;
4263
4387
  }
4264
4388
  catch {
4265
4389
  json(res, 400, { error: "invalid JSON body" });
4266
4390
  return;
4267
4391
  }
4268
- await auth.clearCredentials(provider);
4269
- // Moonshot's OAuth credential lives under a distinct key — clear both so
4270
- // "disconnect" fully removes Kimi OAuth and the API key.
4271
- if (provider === "moonshot")
4272
- await auth.clearCredentials(MOONSHOT_OAUTH_KEY);
4273
- // Xiaomi's API Credits credential lives under a distinct key — clear it
4274
- // too so "disconnect" fully removes both the Token Plan and Credits keys.
4275
- if (provider === "xiaomi")
4276
- await auth.clearCredentials(XIAOMI_CREDITS_KEY);
4392
+ const dual = dualAuthProvider(provider);
4393
+ // A dual-auth provider can be disconnected one method at a time: dropping
4394
+ // a spent API key should not sign the user out of their subscription, and
4395
+ // vice versa. Omitting `method` still clears everything (the plain
4396
+ // "Disconnect" action).
4397
+ if (logoutMethod !== "apikey") {
4398
+ await auth.clearCredentials(dual ? dual.oauthKey : provider);
4399
+ }
4400
+ if (logoutMethod !== "oauth") {
4401
+ // Non-dual providers store their only credential under the provider id,
4402
+ // so this covers both them and a dual provider's API key.
4403
+ await auth.clearCredentials(provider);
4404
+ // Xiaomi's API Credits credential lives under a distinct key — clear it
4405
+ // too so "disconnect" fully removes both the Token Plan and Credits keys.
4406
+ if (provider === "xiaomi")
4407
+ await auth.clearCredentials(XIAOMI_CREDITS_KEY);
4408
+ }
4277
4409
  broadcast("auth_done", { provider });
4278
4410
  json(res, 200, { ok: true });
4279
4411
  });
@@ -4467,7 +4599,7 @@ async function createSession(deps, opts) {
4467
4599
  // scope ignores it (always ~/.ezcoder/mcp.json).
4468
4600
  if (method === "GET" && (url === "/mcp" || url.startsWith("/mcp?"))) {
4469
4601
  const targetCwd = new URL(url, `http://${host}`).searchParams.get("cwd") ?? cwd;
4470
- void buildMcpRows(targetCwd)
4602
+ void buildMcpRows(targetCwd, paths.settingsFile)
4471
4603
  .then((servers) => json(res, 200, { servers }))
4472
4604
  .catch((err) => {
4473
4605
  captureSidecarError(err, "app-sidecar.mcp.list");
@@ -4519,6 +4651,12 @@ async function createSession(deps, opts) {
4519
4651
  json(res, 400, { error: saved.error });
4520
4652
  return;
4521
4653
  }
4654
+ // Adding a project-scope server is an explicit trust signal — the
4655
+ // user chose to put a server in this repo's .ezcoder/mcp.json. Auto-trust
4656
+ // the project so all project-scope servers connect on next load.
4657
+ if (scope === "project") {
4658
+ await session.trustProject(targetCwd);
4659
+ }
4522
4660
  json(res, 200, {
4523
4661
  ok: true,
4524
4662
  name: config.name,