muonroi-cli 1.8.3 → 1.8.5

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 (477) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +133 -122
  3. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  4. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  5. package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
  6. package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
  7. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
  8. package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
  9. package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
  10. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  11. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  12. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  13. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  14. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  15. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  16. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  17. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  18. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  19. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  20. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  21. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  22. package/dist/src/agent-harness/mock-model.d.ts +28 -0
  23. package/dist/src/agent-harness/mock-model.js +63 -1
  24. package/dist/src/agent-harness/test-spawn.js +31 -0
  25. package/dist/src/cli/config/screen-providers.js +1 -1
  26. package/dist/src/cli/cost-forensics.d.ts +10 -0
  27. package/dist/src/cli/cost-forensics.js +30 -15
  28. package/dist/src/cli/keys-bundle.d.ts +1 -1
  29. package/dist/src/cli/keys-bundle.js +1 -1
  30. package/dist/src/cli/keys.d.ts +2 -2
  31. package/dist/src/cli/keys.js +19 -81
  32. package/dist/src/council/clarifier.d.ts +28 -2
  33. package/dist/src/council/clarifier.js +81 -15
  34. package/dist/src/council/context.js +49 -15
  35. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  36. package/dist/src/council/debate-checkpoint.js +176 -0
  37. package/dist/src/council/debate-planner.js +51 -3
  38. package/dist/src/council/debate-summary.d.ts +25 -0
  39. package/dist/src/council/debate-summary.js +85 -0
  40. package/dist/src/council/debate.d.ts +169 -2
  41. package/dist/src/council/debate.js +1210 -134
  42. package/dist/src/council/index.d.ts +85 -1
  43. package/dist/src/council/index.js +634 -196
  44. package/dist/src/council/leader.d.ts +26 -0
  45. package/dist/src/council/leader.js +150 -9
  46. package/dist/src/council/llm.d.ts +32 -0
  47. package/dist/src/council/llm.js +231 -38
  48. package/dist/src/council/panel-select.d.ts +30 -0
  49. package/dist/src/council/panel-select.js +72 -0
  50. package/dist/src/council/planner.js +23 -0
  51. package/dist/src/council/preflight.d.ts +7 -0
  52. package/dist/src/council/preflight.js +14 -2
  53. package/dist/src/council/prompts.d.ts +30 -3
  54. package/dist/src/council/prompts.js +254 -84
  55. package/dist/src/council/stance-recall.d.ts +42 -0
  56. package/dist/src/council/stance-recall.js +57 -0
  57. package/dist/src/council/strip-think.d.ts +17 -0
  58. package/dist/src/council/strip-think.js +33 -0
  59. package/dist/src/council/types.d.ts +128 -0
  60. package/dist/src/ee/artifact-cache.d.ts +16 -0
  61. package/dist/src/ee/artifact-cache.js +32 -0
  62. package/dist/src/ee/auth.d.ts +1 -0
  63. package/dist/src/ee/auth.js +15 -2
  64. package/dist/src/ee/bridge.d.ts +10 -0
  65. package/dist/src/ee/bridge.js +58 -0
  66. package/dist/src/ee/client.js +81 -18
  67. package/dist/src/ee/export-transcripts.d.ts +1 -0
  68. package/dist/src/ee/export-transcripts.js +8 -10
  69. package/dist/src/ee/extract-session.js +29 -0
  70. package/dist/src/ee/extract-style.d.ts +58 -0
  71. package/dist/src/ee/extract-style.js +270 -0
  72. package/dist/src/ee/recall-ledger.d.ts +9 -0
  73. package/dist/src/ee/recall-ledger.js +3 -0
  74. package/dist/src/ee/scope.d.ts +1 -0
  75. package/dist/src/ee/scope.js +26 -1
  76. package/dist/src/ee/search.d.ts +7 -0
  77. package/dist/src/ee/search.js +24 -0
  78. package/dist/src/ee/transcript-emit.js +2 -0
  79. package/dist/src/ee/types.d.ts +22 -0
  80. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  81. package/dist/src/ee/who-am-i-brain.js +220 -0
  82. package/dist/src/ee/who-am-i.d.ts +10 -3
  83. package/dist/src/ee/who-am-i.js +12 -0
  84. package/dist/src/ee/workflow-event.d.ts +48 -0
  85. package/dist/src/ee/workflow-event.js +81 -0
  86. package/dist/src/flow/compaction/compress.d.ts +3 -3
  87. package/dist/src/flow/compaction/compress.js +45 -8
  88. package/dist/src/flow/compaction/extract.d.ts +4 -7
  89. package/dist/src/flow/compaction/extract.js +50 -10
  90. package/dist/src/flow/compaction/index.d.ts +13 -1
  91. package/dist/src/flow/compaction/index.js +70 -3
  92. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  93. package/dist/src/flow/compaction/input-guard.js +43 -0
  94. package/dist/src/flow/fold-planning.d.ts +36 -0
  95. package/dist/src/flow/fold-planning.js +83 -0
  96. package/dist/src/flow/hierarchy.d.ts +146 -0
  97. package/dist/src/flow/hierarchy.js +427 -0
  98. package/dist/src/flow/index.d.ts +1 -0
  99. package/dist/src/flow/index.js +2 -0
  100. package/dist/src/flow/run-artifacts.d.ts +102 -0
  101. package/dist/src/flow/run-artifacts.js +208 -0
  102. package/dist/src/generated/version.d.ts +1 -1
  103. package/dist/src/generated/version.js +1 -1
  104. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  105. package/dist/src/gsd/assessment-schema.js +134 -0
  106. package/dist/src/gsd/capability-registry.d.ts +45 -0
  107. package/dist/src/gsd/capability-registry.js +337 -0
  108. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  109. package/dist/src/gsd/complexity-assessor.js +152 -0
  110. package/dist/src/gsd/config-bridge.d.ts +7 -0
  111. package/dist/src/gsd/config-bridge.js +114 -0
  112. package/dist/src/gsd/config-loader.d.ts +27 -0
  113. package/dist/src/gsd/config-loader.js +50 -0
  114. package/dist/src/gsd/council-context.d.ts +44 -0
  115. package/dist/src/gsd/council-context.js +114 -0
  116. package/dist/src/gsd/ee-closure.d.ts +28 -0
  117. package/dist/src/gsd/ee-closure.js +49 -0
  118. package/dist/src/gsd/flags.d.ts +55 -0
  119. package/dist/src/gsd/flags.js +83 -0
  120. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  121. package/dist/src/gsd/gsd-dispatch.js +131 -0
  122. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  123. package/dist/src/gsd/gsd-runtime.js +37 -0
  124. package/dist/src/gsd/host-adapter.d.ts +11 -0
  125. package/dist/src/gsd/host-adapter.js +29 -0
  126. package/dist/src/gsd/index.d.ts +24 -1
  127. package/dist/src/gsd/index.js +27 -0
  128. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  129. package/dist/src/gsd/loop-host-contract.js +39 -0
  130. package/dist/src/gsd/loop-host.d.ts +69 -0
  131. package/dist/src/gsd/loop-host.js +245 -0
  132. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  133. package/dist/src/gsd/loop-resolver.js +79 -0
  134. package/dist/src/gsd/model-tier.d.ts +13 -0
  135. package/dist/src/gsd/model-tier.js +45 -0
  136. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  137. package/dist/src/gsd/mutation-gate.js +41 -0
  138. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  139. package/dist/src/gsd/native-roadmap.js +343 -0
  140. package/dist/src/gsd/native-state.d.ts +47 -0
  141. package/dist/src/gsd/native-state.js +220 -0
  142. package/dist/src/gsd/paths.d.ts +23 -0
  143. package/dist/src/gsd/paths.js +66 -0
  144. package/dist/src/gsd/phase-dag.d.ts +12 -0
  145. package/dist/src/gsd/phase-dag.js +94 -0
  146. package/dist/src/gsd/phase-sync.d.ts +42 -0
  147. package/dist/src/gsd/phase-sync.js +321 -0
  148. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  149. package/dist/src/gsd/pil-gate-context.js +64 -0
  150. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  151. package/dist/src/gsd/pil-gate-critic.js +74 -0
  152. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  153. package/dist/src/gsd/plan-council-prompts.js +79 -0
  154. package/dist/src/gsd/plan-council.d.ts +44 -0
  155. package/dist/src/gsd/plan-council.js +251 -0
  156. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  157. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  158. package/dist/src/gsd/product-workspace.d.ts +13 -0
  159. package/dist/src/gsd/product-workspace.js +124 -0
  160. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  161. package/dist/src/gsd/ship-bridge.js +65 -0
  162. package/dist/src/gsd/state-document.d.ts +40 -0
  163. package/dist/src/gsd/state-document.js +163 -0
  164. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  165. package/dist/src/gsd/verdict-schema.js +144 -0
  166. package/dist/src/gsd/verify-context.d.ts +22 -0
  167. package/dist/src/gsd/verify-context.js +27 -0
  168. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  169. package/dist/src/gsd/verify-council-prompts.js +85 -0
  170. package/dist/src/gsd/verify-council.d.ts +25 -0
  171. package/dist/src/gsd/verify-council.js +119 -0
  172. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  173. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  174. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  175. package/dist/src/gsd/workflow-engine.js +207 -0
  176. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  177. package/dist/src/gsd/workflow-tools.js +277 -0
  178. package/dist/src/hooks/index.js +1 -1
  179. package/dist/src/index.js +44 -11
  180. package/dist/src/maintain/pr-builder.js +23 -13
  181. package/dist/src/mcp/auto-setup.js +57 -32
  182. package/dist/src/mcp/client-pool.js +1 -1
  183. package/dist/src/mcp/ee-tools.js +1 -0
  184. package/dist/src/mcp/oauth-callback.js +2 -2
  185. package/dist/src/mcp/research-onboarding.js +8 -7
  186. package/dist/src/mcp/runtime.js +34 -2
  187. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  188. package/dist/src/mcp/setup-guide-text.js +77 -76
  189. package/dist/src/models/catalog-client.d.ts +87 -0
  190. package/dist/src/models/catalog-client.js +105 -38
  191. package/dist/src/models/catalog.json +528 -265
  192. package/dist/src/models/registry.d.ts +22 -7
  193. package/dist/src/models/registry.js +73 -10
  194. package/dist/src/ops/doctor.js +8 -8
  195. package/dist/src/orchestrator/auto-commit.js +1 -1
  196. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  197. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  198. package/dist/src/orchestrator/cache-prefix.js +83 -0
  199. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  200. package/dist/src/orchestrator/compact-request.js +41 -0
  201. package/dist/src/orchestrator/compaction.d.ts +10 -0
  202. package/dist/src/orchestrator/compaction.js +27 -7
  203. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  204. package/dist/src/orchestrator/council-manager.js +65 -24
  205. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  206. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  207. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  208. package/dist/src/orchestrator/error-utils.js +132 -24
  209. package/dist/src/orchestrator/grounding-check.js +39 -1
  210. package/dist/src/orchestrator/message-processor.js +242 -33
  211. package/dist/src/orchestrator/orchestrator.d.ts +39 -3
  212. package/dist/src/orchestrator/orchestrator.js +651 -102
  213. package/dist/src/orchestrator/preprocessor.js +1 -1
  214. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  215. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  216. package/dist/src/orchestrator/prompts.js +159 -159
  217. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  218. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  219. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  220. package/dist/src/orchestrator/retry-classifier.js +46 -2
  221. package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
  222. package/dist/src/orchestrator/safety-intercept.js +55 -0
  223. package/dist/src/orchestrator/scope-reminder.js +1 -1
  224. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  225. package/dist/src/orchestrator/session-experience.js +2 -1
  226. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  227. package/dist/src/orchestrator/should-run-gate.js +18 -0
  228. package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
  229. package/dist/src/orchestrator/stall-watchdog.js +47 -13
  230. package/dist/src/orchestrator/stream-runner.js +62 -29
  231. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  232. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  233. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  234. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  235. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  236. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  237. package/dist/src/orchestrator/tool-engine.d.ts +26 -0
  238. package/dist/src/orchestrator/tool-engine.js +669 -56
  239. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  240. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  241. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  242. package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
  243. package/dist/src/orchestrator/turn-watchdog.js +55 -0
  244. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  245. package/dist/src/pil/agent-operating-contract.js +1 -1
  246. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  247. package/dist/src/pil/cheap-model-playbook.js +5 -1
  248. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  249. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  250. package/dist/src/pil/discovery-types.d.ts +1 -0
  251. package/dist/src/pil/discovery.js +16 -11
  252. package/dist/src/pil/layer1-intent.d.ts +18 -6
  253. package/dist/src/pil/layer1-intent.js +66 -757
  254. package/dist/src/pil/layer15-context-scan.js +15 -1
  255. package/dist/src/pil/layer2_5-ponytail.js +8 -8
  256. package/dist/src/pil/layer3-ee-injection.js +23 -8
  257. package/dist/src/pil/layer4-gsd.js +69 -16
  258. package/dist/src/pil/layer5-context.js +7 -3
  259. package/dist/src/pil/layer6-output.d.ts +23 -0
  260. package/dist/src/pil/layer6-output.js +5 -1
  261. package/dist/src/pil/llm-classify.d.ts +33 -2
  262. package/dist/src/pil/llm-classify.js +123 -131
  263. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  264. package/dist/src/pil/native-capabilities-workbook.js +1 -0
  265. package/dist/src/pil/pipeline.js +34 -2
  266. package/dist/src/pil/response-tools.js +5 -3
  267. package/dist/src/pil/schema.d.ts +1 -0
  268. package/dist/src/pil/schema.js +2 -0
  269. package/dist/src/pil/types.d.ts +18 -0
  270. package/dist/src/playbook/directives.d.ts +4 -0
  271. package/dist/src/playbook/directives.js +17 -5
  272. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  273. package/dist/src/product-loop/backlog-builder.js +30 -6
  274. package/dist/src/product-loop/discovery-context-format.js +3 -1
  275. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  276. package/dist/src/product-loop/discovery-interview.js +32 -3
  277. package/dist/src/product-loop/discovery-schema.js +5 -1
  278. package/dist/src/product-loop/done-gate.js +3 -3
  279. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  280. package/dist/src/product-loop/ideal-trace.js +64 -0
  281. package/dist/src/product-loop/index.d.ts +13 -1
  282. package/dist/src/product-loop/index.js +333 -52
  283. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  284. package/dist/src/product-loop/loop-driver.js +327 -116
  285. package/dist/src/product-loop/phase-plan.d.ts +5 -0
  286. package/dist/src/product-loop/phase-plan.js +39 -2
  287. package/dist/src/product-loop/phase-runner.js +9 -1
  288. package/dist/src/product-loop/progress-snapshot.js +4 -4
  289. package/dist/src/product-loop/sprint-runner.d.ts +111 -0
  290. package/dist/src/product-loop/sprint-runner.js +559 -16
  291. package/dist/src/product-loop/types.d.ts +36 -5
  292. package/dist/src/providers/adapter.d.ts +1 -1
  293. package/dist/src/providers/adapter.js +3 -4
  294. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  295. package/dist/src/providers/auth/browser-flow.js +1 -1
  296. package/dist/src/providers/auth/openai-oauth.js +1 -1
  297. package/dist/src/providers/auth/registry.js +0 -34
  298. package/dist/src/providers/auth/token-store.js +4 -1
  299. package/dist/src/providers/auth/types.d.ts +1 -1
  300. package/dist/src/providers/auth/types.js +1 -1
  301. package/dist/src/providers/capabilities.d.ts +24 -5
  302. package/dist/src/providers/capabilities.js +42 -24
  303. package/dist/src/providers/endpoints.d.ts +2 -2
  304. package/dist/src/providers/endpoints.js +11 -10
  305. package/dist/src/providers/keychain.d.ts +1 -1
  306. package/dist/src/providers/keychain.js +7 -9
  307. package/dist/src/providers/mcp-vision-bridge.js +82 -172
  308. package/dist/src/providers/openai-compatible.js +8 -1
  309. package/dist/src/providers/pricing.d.ts +2 -2
  310. package/dist/src/providers/pricing.js +3 -13
  311. package/dist/src/providers/runtime.d.ts +27 -2
  312. package/dist/src/providers/runtime.js +78 -15
  313. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  314. package/dist/src/providers/strategies/base.strategy.js +24 -1
  315. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  316. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  317. package/dist/src/providers/strategies/registry.js +4 -4
  318. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  319. package/dist/src/providers/strategies/thinking-mode.js +280 -1
  320. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  321. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  322. package/dist/src/providers/types.d.ts +5 -6
  323. package/dist/src/providers/types.js +2 -2
  324. package/dist/src/providers/vision-backend.d.ts +47 -0
  325. package/dist/src/providers/vision-backend.js +258 -0
  326. package/dist/src/providers/vision-proxy.d.ts +22 -9
  327. package/dist/src/providers/vision-proxy.js +63 -132
  328. package/dist/src/providers/wire-debug.js +95 -0
  329. package/dist/src/reporter/index.js +1 -1
  330. package/dist/src/router/decide.d.ts +13 -0
  331. package/dist/src/router/decide.js +138 -36
  332. package/dist/src/router/peak-hour.d.ts +38 -0
  333. package/dist/src/router/peak-hour.js +107 -0
  334. package/dist/src/router/step-router.js +3 -2
  335. package/dist/src/router/warm.js +4 -5
  336. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  337. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  338. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  339. package/dist/src/scaffold/continuation-prompt.js +86 -60
  340. package/dist/src/scaffold/init-new.js +453 -453
  341. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  342. package/dist/src/scaffold/point-to-existing.js +25 -0
  343. package/dist/src/self-qa/agentic-loop.js +22 -22
  344. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  345. package/dist/src/{ui/state → state}/active-run.js +21 -0
  346. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  347. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  348. package/dist/src/state/turn-trace.d.ts +43 -0
  349. package/dist/src/state/turn-trace.js +32 -0
  350. package/dist/src/storage/db.js +2 -1
  351. package/dist/src/storage/index.d.ts +1 -1
  352. package/dist/src/storage/index.js +1 -1
  353. package/dist/src/storage/interaction-log.d.ts +1 -1
  354. package/dist/src/storage/interaction-log.js +5 -5
  355. package/dist/src/storage/migrations.js +196 -126
  356. package/dist/src/storage/session-experience-store.js +4 -4
  357. package/dist/src/storage/sessions.d.ts +28 -10
  358. package/dist/src/storage/sessions.js +112 -55
  359. package/dist/src/storage/transcript-view.js +1 -1
  360. package/dist/src/storage/transcript.d.ts +51 -0
  361. package/dist/src/storage/transcript.js +383 -112
  362. package/dist/src/storage/usage.js +14 -14
  363. package/dist/src/storage/workspaces.js +12 -12
  364. package/dist/src/tools/file.d.ts +15 -0
  365. package/dist/src/tools/file.js +32 -0
  366. package/dist/src/tools/native-tools.js +5 -0
  367. package/dist/src/tools/registry.d.ts +3 -0
  368. package/dist/src/tools/registry.js +460 -22
  369. package/dist/src/tools/research.d.ts +29 -0
  370. package/dist/src/tools/research.js +233 -0
  371. package/dist/src/types/index.d.ts +118 -3
  372. package/dist/src/ui/app.js +0 -0
  373. package/dist/src/ui/cards/product-status-card.js +1 -1
  374. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  375. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  376. package/dist/src/ui/components/context-rail.d.ts +26 -0
  377. package/dist/src/ui/components/context-rail.js +33 -0
  378. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  379. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  380. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  381. package/dist/src/ui/components/council-debate-pill.js +34 -0
  382. package/dist/src/ui/components/council-info-card.js +2 -2
  383. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  384. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  385. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  386. package/dist/src/ui/components/council-message-bubble.js +16 -15
  387. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  388. package/dist/src/ui/components/council-phase-timeline.js +49 -15
  389. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  390. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  391. package/dist/src/ui/components/council-question-card.js +12 -12
  392. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  393. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  394. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  395. package/dist/src/ui/components/council-round-group.js +88 -0
  396. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  397. package/dist/src/ui/components/council-status-list.js +36 -24
  398. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  399. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  400. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  401. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  402. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  403. package/dist/src/ui/components/prompt-box.js +18 -16
  404. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  405. package/dist/src/ui/components/session-tree-card.js +46 -0
  406. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  407. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  408. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  409. package/dist/src/ui/components/task-list-panel.js +22 -2
  410. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  411. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  412. package/dist/src/ui/mcp-modal.js +2 -4
  413. package/dist/src/ui/modals/api-key-modal.js +1 -1
  414. package/dist/src/ui/modals/connect-modal.js +4 -3
  415. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  416. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  417. package/dist/src/ui/picker-providers.d.ts +1 -1
  418. package/dist/src/ui/picker-providers.js +1 -1
  419. package/dist/src/ui/primitives/index.d.ts +1 -0
  420. package/dist/src/ui/primitives/index.js +2 -0
  421. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  422. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  423. package/dist/src/ui/slash/compact.js +5 -7
  424. package/dist/src/ui/slash/cost.js +1 -1
  425. package/dist/src/ui/slash/council-inspect.js +4 -4
  426. package/dist/src/ui/slash/council.js +19 -1
  427. package/dist/src/ui/slash/debug.d.ts +3 -31
  428. package/dist/src/ui/slash/debug.js +9 -20
  429. package/dist/src/ui/slash/ideal.d.ts +6 -2
  430. package/dist/src/ui/slash/ideal.js +97 -7
  431. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  432. package/dist/src/ui/slash/menu-items.js +12 -18
  433. package/dist/src/ui/slash/registry.d.ts +2 -0
  434. package/dist/src/ui/slash/registry.js +4 -0
  435. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  436. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  437. package/dist/src/ui/status-bar/index.d.ts +1 -1
  438. package/dist/src/ui/status-bar/index.js +7 -3
  439. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  440. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  441. package/dist/src/ui/theme.d.ts +1 -0
  442. package/dist/src/ui/theme.js +2 -0
  443. package/dist/src/ui/types.d.ts +7 -0
  444. package/dist/src/ui/use-app-logic.js +0 -0
  445. package/dist/src/ui/utils/format.d.ts +14 -0
  446. package/dist/src/ui/utils/format.js +23 -3
  447. package/dist/src/usage/downgrade.js +2 -2
  448. package/dist/src/usage/product-ledger.js +2 -2
  449. package/dist/src/utils/clipboard-image.js +23 -23
  450. package/dist/src/utils/install-manager.js +14 -11
  451. package/dist/src/utils/logger.js +2 -2
  452. package/dist/src/utils/permission-mode.js +5 -3
  453. package/dist/src/utils/redactor.js +1 -1
  454. package/dist/src/utils/settings.d.ts +153 -5
  455. package/dist/src/utils/settings.js +233 -29
  456. package/dist/src/utils/side-question.js +2 -2
  457. package/dist/src/utils/skills.js +3 -3
  458. package/dist/src/utils/visible-retry.d.ts +11 -0
  459. package/dist/src/utils/visible-retry.js +10 -1
  460. package/dist/src/verify/entrypoint.d.ts +1 -1
  461. package/dist/src/verify/entrypoint.js +1 -1
  462. package/dist/src/verify/recipes.d.ts +13 -0
  463. package/dist/src/verify/recipes.js +15 -0
  464. package/package.json +135 -132
  465. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  466. package/dist/src/providers/auth/gcloud.js +0 -102
  467. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  468. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  469. package/dist/src/providers/gemini.d.ts +0 -11
  470. package/dist/src/providers/gemini.js +0 -45
  471. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  472. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  473. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  474. package/dist/src/providers/strategies/google.strategy.js +0 -174
  475. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  476. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  477. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -24,6 +24,15 @@ export interface RecallLedger {
24
24
  clear(id: string): boolean;
25
25
  /** Check if an id was ever cleared (fed back) in this session. Used by PIL Layer 3 to suppress re-injection. */
26
26
  wasCleared(id: string): boolean;
27
+ /**
28
+ * Check if an id is currently PENDING (recorded as unrated debt, not yet fed
29
+ * back). PIL Layer 3 uses this to suppress re-injecting the FULL content of a
30
+ * hint it already surfaced earlier this session — the hit is already in the
31
+ * conversation history and still listed in the feedback nudge, so repeating
32
+ * its body every turn is pure token waste (the "hint lặp" repetition). First
33
+ * sighting is injected + recorded; subsequent turns skip the body.
34
+ */
35
+ isPending(id: string): boolean;
27
36
  /** Oldest-first list of still-unrated recalls. */
28
37
  pending(): PendingRecall[];
29
38
  pendingCount(): number;
@@ -45,6 +45,9 @@ export function createRecallLedger() {
45
45
  wasCleared(id) {
46
46
  return cleared.has(String(id ?? "").trim());
47
47
  },
48
+ isPending(id) {
49
+ return map.has(String(id ?? "").trim());
50
+ },
48
51
  pending() {
49
52
  return [...map.values()].sort((a, b) => a.ts - b.ts);
50
53
  },
@@ -1,4 +1,5 @@
1
1
  import type { Scope } from "./types.js";
2
+ export declare function sanitizeRemoteUrl(remoteUrl: string): string;
2
3
  export declare function buildScope(opts: {
3
4
  cwd: string;
4
5
  }): Promise<Scope>;
@@ -42,6 +42,30 @@ async function findGitRoot(start) {
42
42
  }
43
43
  return null;
44
44
  }
45
+ export function sanitizeRemoteUrl(remoteUrl) {
46
+ const trimmed = remoteUrl.trim();
47
+ // If it starts with a scheme like http://, https://, ssh://, git:// etc.
48
+ if (/^[a-zA-Z0-9+-.]+:\/\//.test(trimmed)) {
49
+ try {
50
+ const parsed = new URL(trimmed);
51
+ if (parsed.protocol === "http:" ||
52
+ parsed.protocol === "https:" ||
53
+ parsed.protocol === "git+http:" ||
54
+ parsed.protocol === "git+https:") {
55
+ parsed.username = "";
56
+ parsed.password = "";
57
+ }
58
+ return parsed.toString();
59
+ }
60
+ catch {
61
+ // Fallback for valid scheme prefix but invalid URL syntax
62
+ return trimmed.replace(/^([^:]+:\/\/)[^@]+@/, "$1");
63
+ }
64
+ }
65
+ // Prevent parsing exceptions on SSH remotes (e.g. git@github.com:...)
66
+ // SCP-like SSH remotes do not start with a scheme + "://"
67
+ return trimmed;
68
+ }
45
69
  export async function buildScope(opts) {
46
70
  if (cached && cachedFor === opts.cwd)
47
71
  return cached;
@@ -53,7 +77,8 @@ export async function buildScope(opts) {
53
77
  }
54
78
  const head = await fs.readFile(path.join(root, ".git", "HEAD"), "utf8").catch(() => "");
55
79
  const cfg = await fs.readFile(path.join(root, ".git", "config"), "utf8").catch(() => "");
56
- const { remote } = parseGitConfig(cfg);
80
+ const { remote: rawRemote } = parseGitConfig(cfg);
81
+ const remote = rawRemote ? sanitizeRemoteUrl(rawRemote) : undefined;
57
82
  const { branch } = parseHEAD(head);
58
83
  // Ecosystem detection — check if remote matches any configured pattern
59
84
  if (remote) {
@@ -76,6 +76,13 @@ export interface FeedbackResult {
76
76
  verdict?: string;
77
77
  reason?: string;
78
78
  error?: string;
79
+ /**
80
+ * True when the brain was unreachable/transient and the verdict was durably
81
+ * enqueued for later delivery instead of lost. Callers treat this as success
82
+ * (the agent discharged its rating) and clear their recall ledger so the
83
+ * mandatory-rating gate cannot loop while the brain is down.
84
+ */
85
+ queued?: boolean;
79
86
  }
80
87
  /**
81
88
  * Record an agent verdict on a recalled `[id col]` entry, byte-for-byte matching
@@ -173,6 +173,13 @@ export async function feedbackEE(pointId, collection, verdict, reason) {
173
173
  /* non-JSON error body — fall through to status-based handling */
174
174
  }
175
175
  if (!res.ok) {
176
+ // Transient server-side failures (5xx / rate limit / timeout) must not lose
177
+ // the verdict OR block the agent: queue it for later delivery. Hard client
178
+ // errors (bad collection, unknown id) are the caller's fault — surface them.
179
+ const transient = res.status >= 500 || res.status === 429 || res.status === 408;
180
+ if (transient && (await tryEnqueueFeedback(body))) {
181
+ return { ok: true, resolvedId: pointId, verdict: wire, reason, queued: true };
182
+ }
176
183
  return { ok: false, error: json?.error || text || `HTTP ${res.status}` };
177
184
  }
178
185
  const resolvedId = json?.resolvedId || pointId;
@@ -182,9 +189,26 @@ export async function feedbackEE(pointId, collection, verdict, reason) {
182
189
  catch (err) {
183
190
  const { logEeFailure, classifyEeError } = await import("../utils/ee-logger.js");
184
191
  logEeFailure("search.feedbackEE", classifyEeError(err), err);
192
+ // Network-level failure (brain down, socket drop) — durably queue so the
193
+ // verdict survives and the agent's rating gate does not loop. Only report a
194
+ // hard failure if even the enqueue fails.
195
+ if (await tryEnqueueFeedback(body)) {
196
+ return { ok: true, resolvedId: pointId, verdict: wire, reason, queued: true };
197
+ }
185
198
  return { ok: false, error: err instanceof Error ? err.message : String(err) };
186
199
  }
187
200
  }
201
+ /** Enqueue a /api/feedback body to the offline queue; false if enqueue itself fails. */
202
+ async function tryEnqueueFeedback(body) {
203
+ try {
204
+ const { enqueue } = await import("./offline-queue.js");
205
+ await enqueue({ endpoint: "/api/feedback", body, enqueuedAt: Date.now() });
206
+ return true;
207
+ }
208
+ catch {
209
+ return false;
210
+ }
211
+ }
188
212
  /**
189
213
  * Mirror a feedback verdict as a LOCAL `op:'feedback'` activity row (parity with
190
214
  * mirrorRecallLocally's `op:'recall'`). Lets the session-end nudge + forensics
@@ -23,6 +23,8 @@ import fs from "node:fs";
23
23
  import os from "node:os";
24
24
  import path from "node:path";
25
25
  function experienceRoot() {
26
+ if (process.env.MUONROI_EXPERIENCE_ROOT_OVERRIDE)
27
+ return process.env.MUONROI_EXPERIENCE_ROOT_OVERRIDE;
26
28
  return path.join(os.homedir(), ".experience");
27
29
  }
28
30
  function emitRoot() {
@@ -125,6 +125,11 @@ export interface RouteModelRequest {
125
125
  projectSize?: "small" | "medium" | "large";
126
126
  filesTouched?: number;
127
127
  mode?: string;
128
+ turnIndex?: number;
129
+ messageCount?: number;
130
+ compactionCount?: number;
131
+ totalSavedTokens?: number;
132
+ compactionSummary?: string;
128
133
  };
129
134
  runtime?: "claude" | "gemini" | "codex" | "opencode";
130
135
  }
@@ -357,6 +362,15 @@ export interface EERecallOptions {
357
362
  /** Override per-call timeout. Defaults to 15000ms — server bounds recall at ~8s internally; allow margin for embed + 3-leg search + network. */
358
363
  timeoutMs?: number;
359
364
  signal?: AbortSignal;
365
+ /**
366
+ * Council stance/role for per-stance recall weighting (Sprint-2 item 3). The
367
+ * server weights the recall collections toward the knowledge this stance cares
368
+ * about (researcher → principles, implementer → behavioral, verifier →
369
+ * self-QA). Unknown/absent stance is a no-op server-side.
370
+ */
371
+ stance?: string;
372
+ /** Alias for stance; forwarded as body.role. Either resolves the same weights. */
373
+ role?: string;
360
374
  }
361
375
  export interface EEUserResponse {
362
376
  user: string;
@@ -374,6 +388,14 @@ export interface BrainProxyOptions {
374
388
  maxTokens?: number;
375
389
  /** Override the brain provider (siliconflow | ollama | openai | ...). */
376
390
  provider?: string;
391
+ /**
392
+ * Ask the server to use its stronger `brainExtractModel` (structured-extraction
393
+ * model) instead of the hot-path `brainModel`. Zero-hardcode by design: the client
394
+ * sends only this flag, the server resolves the model id from its own config + key.
395
+ * Used by the thin-client WhoAmI brain fallback, where the weak default model
396
+ * mis-spells the dim vocabulary. Ignored by servers that don't recognise it.
397
+ */
398
+ useExtractModel?: boolean;
377
399
  }
378
400
  export interface EEClient {
379
401
  health(): Promise<{
@@ -0,0 +1,35 @@
1
+ import { type PrivacyLevel, type WhoAmIProfile } from "./who-am-i.js";
2
+ /** Feature flag — default ON, opt out with MUONROI_WHOAMI_BRAIN=0. */
3
+ export declare function isBrainWhoAmIEnabled(): boolean;
4
+ /**
5
+ * PURE: build the classify prompt. Asks the brain to distil the retrieved rules into
6
+ * a strict dims JSON using only the allowed names + vocabulary. Emitting fewer dims
7
+ * (only the confident ones) is explicitly encouraged — downstream drops the rest.
8
+ */
9
+ export declare function buildStyleClassifyPrompt(rules: string[]): string;
10
+ /**
11
+ * PURE: coerce a brain reply (JSON string, fenced block, or already-parsed object)
12
+ * into a privacy-gated WhoAmIProfile. Reuses selectWhoAmIDims for the allowlist +
13
+ * confidence floor. Returns null when nothing usable survives.
14
+ */
15
+ export declare function parseBrainProfile(raw: unknown, level?: PrivacyLevel): WhoAmIProfile | null;
16
+ export interface BrainWhoAmIDeps {
17
+ searchByText: (text: string, collections: string[], topK: number) => Promise<Array<{
18
+ payload?: {
19
+ text?: string;
20
+ };
21
+ }>>;
22
+ classifyViaBrain: (prompt: string, timeoutMs?: number, options?: {
23
+ systemPrompt?: string;
24
+ responseFormat?: {
25
+ type: string;
26
+ };
27
+ maxTokens?: number;
28
+ useExtractModel?: boolean;
29
+ }) => Promise<string | null>;
30
+ }
31
+ /**
32
+ * Derive a WhoAmIProfile from the behavioral brain. Search the user's style rules →
33
+ * classify them into dims via the brain LLM → privacy-gate locally. Fail-open null.
34
+ */
35
+ export declare function deriveWhoAmIFromBrain(deps: BrainWhoAmIDeps, level?: PrivacyLevel): Promise<WhoAmIProfile | null>;
@@ -0,0 +1,220 @@
1
+ // src/ee/who-am-i-brain.ts
2
+ //
3
+ // Brain-derived "Who Am I" fallback for THIN-CLIENTS.
4
+ //
5
+ // The device-local profile.yaml pipeline (src/ee/who-am-i.ts) is full-brain-only:
6
+ // the EE prompt interceptor bails early on remote mode (`if (isRemoteMode()) return`),
7
+ // no /api/profile endpoint exists, and signal-extraction from transcripts never runs
8
+ // on a thin-client. So on a thin-client getWhoAmIProfile() is structurally always
9
+ // null and the PIL loses every style signal.
10
+ //
11
+ // The user's working style IS reachable though — it lives in the `experience-behavioral`
12
+ // brain (their own authored rules: "recommend, don't ask", "keep it concise", etc.),
13
+ // which the runtime already queries via searchByText() over /api/search. This module
14
+ // derives the SAME WhoAmIProfile shape from those rules so all existing PIL wiring
15
+ // (outputStyleFromProfile, the layer4/5/6 dim reads) works unchanged.
16
+ //
17
+ // Design guarantees:
18
+ // • Agent-first (no keyword regex): the rule→dim mapping is decided by the brain LLM
19
+ // itself (classifyViaBrain), honouring the repo rule that classification is never
20
+ // regex. The only regex here is tolerant JSON extraction from the LLM's reply.
21
+ // • Privacy: dims are re-gated locally through selectWhoAmIDims — the positive
22
+ // name-allowlist + per-tier confidence floor. Brain rules are user-authored style
23
+ // guidance (Tang-2 equivalent), so we gate at the "standard" tier; emotional.* can
24
+ // never surface (not on the allowlist). This is decoupled from the EE file
25
+ // privacyLevel (which governs the *auto-derived* on-device profile — a different,
26
+ // more sensitive data source that is off by default on thin-clients).
27
+ // • Fail-open: any gap (no rules, classifier null, unparseable output, dep throws)
28
+ // returns null → the PIL keeps its own per-turn default. Never throws.
29
+ import { selectWhoAmIDims } from "./who-am-i.js";
30
+ const BEHAVIORAL_COLLECTION = "experience-behavioral";
31
+ const BRAIN_TOPK = 12;
32
+ /** Brain rules are style-level guidance → the standard tier (never emotional). */
33
+ const BRAIN_TIER = "standard";
34
+ /**
35
+ * System prompt override — REQUIRED. The EE brain proxy's default system prompt is a
36
+ * tier-classifier ("output ONE word: fast|balanced|premium … ignore the task content"),
37
+ * which actively hijacks any structured-output request → malformed JSON. Verified on the
38
+ * live VPS (2026-07-05): omitting this made even DeepSeek-V3 emit garbage; supplying it
39
+ * makes the DEFAULT server model (Qwen2.5-7B) emit clean, accurate dims JSON. We do NOT
40
+ * override the model/provider — that would hardcode a model id (Zero-Hardcode Rule) and
41
+ * cost the user's own provider; the server's default brain model + key does the work.
42
+ */
43
+ const STYLE_SYSTEM_PROMPT = "You extract a developer's working-style profile from their accumulated behavioral " +
44
+ "rules. Follow the user message instructions EXACTLY and output ONLY the requested " +
45
+ "JSON object. Do not classify task complexity, do not output single words, do not " +
46
+ "add prose before or after the JSON.";
47
+ /**
48
+ * Probe the behavioral brain for working-style rules. Free-text; the server embeds
49
+ * + searches Qdrant. Intentionally broad — it should surface brevity / decision /
50
+ * feedback / delegation / risk / conflict guidance the user has accumulated.
51
+ */
52
+ const STYLE_PROBE = "the user's preferred working style: communication brevity, decision speed, " +
53
+ "feedback and correction style, delegation style, risk tolerance, conflict style";
54
+ // The dims the brain may emit, with the EXACT value vocabulary. Source of truth is
55
+ // the EE profile renderer (~/.experience/src/profile-render.js DIRECTIVES) — the same
56
+ // values the device-local profile uses and that the PIL layers compare against
57
+ // literally (e.g. layer4 tests delegation === "autonomous", layer6 tests
58
+ // feedback_style === "precise-correction"). A value outside this set would populate a
59
+ // dim that no lever reads → silently inert. Keep in lockstep with the EE enum.
60
+ const DIM_VOCAB = {
61
+ "communication.brevity": ["concise", "moderate", "verbose"],
62
+ "communication.feedback_style": ["implicit", "precise-correction"],
63
+ "communication.question_style": ["comparison", "debugging", "exploratory", "directive"],
64
+ "personality.decision_speed": ["fast-intuitive", "measured", "deliberate"],
65
+ "personality.risk_tolerance": ["experimental"],
66
+ "personality.conflict_style": ["direct-constructive", "authoritative", "cautious"],
67
+ "work_patterns.energy": ["night-owl", "daytime", "mixed"],
68
+ "work_patterns.multitasking": ["task-switcher", "sequential-deep"],
69
+ "work_patterns.session_length": ["short", "medium", "long"],
70
+ "work_patterns.delegation_style": ["autonomous", "collaborative"],
71
+ };
72
+ /** Feature flag — default ON, opt out with MUONROI_WHOAMI_BRAIN=0. */
73
+ export function isBrainWhoAmIEnabled() {
74
+ return process.env.MUONROI_WHOAMI_BRAIN !== "0";
75
+ }
76
+ /**
77
+ * PURE: build the classify prompt. Asks the brain to distil the retrieved rules into
78
+ * a strict dims JSON using only the allowed names + vocabulary. Emitting fewer dims
79
+ * (only the confident ones) is explicitly encouraged — downstream drops the rest.
80
+ */
81
+ export function buildStyleClassifyPrompt(rules) {
82
+ const vocabLines = Object.entries(DIM_VOCAB)
83
+ .map(([name, values]) => ` "${name}": one of ${values.map((v) => `"${v}"`).join(" | ")}`)
84
+ .join("\n");
85
+ const ruleBlock = rules.length
86
+ ? rules.map((r, i) => `${i + 1}. ${r}`).join("\n")
87
+ : "(no explicit rules retrieved — infer nothing; emit an empty dimensions object)";
88
+ return [
89
+ "You classify a developer's working style from their accumulated behavioral rules.",
90
+ "Output ONLY a JSON object of the form:",
91
+ '{"dimensions": {"<dim.name>": {"value": "<allowed value>", "confidence": 0.0-1.0, "sampleCount": <int>}}}',
92
+ "",
93
+ "Allowed dimensions and their vocabulary (use these names/values EXACTLY):",
94
+ vocabLines,
95
+ "",
96
+ "Rules:",
97
+ "- Include a dimension ONLY when the rules give clear evidence for it.",
98
+ "- Omit anything uncertain — a small, confident object beats a complete guess.",
99
+ "- confidence reflects how strongly the rules support the value; sampleCount = how many rules back it.",
100
+ "- Never invent dimension names or values outside the vocabulary above.",
101
+ "",
102
+ "Behavioral rules:",
103
+ ruleBlock,
104
+ ].join("\n");
105
+ }
106
+ /** Tolerantly extract the first balanced JSON object from an LLM reply. */
107
+ function extractJsonObject(text) {
108
+ const start = text.indexOf("{");
109
+ if (start === -1)
110
+ return null;
111
+ let depth = 0;
112
+ let inStr = false;
113
+ let esc = false;
114
+ for (let i = start; i < text.length; i++) {
115
+ const ch = text[i];
116
+ if (inStr) {
117
+ if (esc)
118
+ esc = false;
119
+ else if (ch === "\\")
120
+ esc = true;
121
+ else if (ch === '"')
122
+ inStr = false;
123
+ continue;
124
+ }
125
+ if (ch === '"')
126
+ inStr = true;
127
+ else if (ch === "{")
128
+ depth++;
129
+ else if (ch === "}") {
130
+ depth--;
131
+ if (depth === 0)
132
+ return text.slice(start, i + 1);
133
+ }
134
+ }
135
+ return null;
136
+ }
137
+ /**
138
+ * PURE: coerce a brain reply (JSON string, fenced block, or already-parsed object)
139
+ * into a privacy-gated WhoAmIProfile. Reuses selectWhoAmIDims for the allowlist +
140
+ * confidence floor. Returns null when nothing usable survives.
141
+ */
142
+ export function parseBrainProfile(raw, level = BRAIN_TIER) {
143
+ let obj = raw;
144
+ if (typeof raw === "string") {
145
+ const json = extractJsonObject(raw);
146
+ if (!json)
147
+ return null;
148
+ try {
149
+ obj = JSON.parse(json);
150
+ }
151
+ catch {
152
+ return null;
153
+ }
154
+ }
155
+ if (!obj || typeof obj !== "object")
156
+ return null;
157
+ const gated = selectWhoAmIDims(obj, level);
158
+ // Value validation — the name allowlist in selectWhoAmIDims does NOT check the VALUE,
159
+ // and weaker brain models mis-spell the vocabulary (verified on the live VPS:
160
+ // Qwen2.5-7B emitted "precice-correction"). An off-vocab value populates a dim that
161
+ // the PIL layers — which compare literally — can never fire on, so it's worse than
162
+ // absent (looks set, does nothing). Drop any value not in DIM_VOCAB[name].
163
+ const dims = {};
164
+ for (const [name, dim] of Object.entries(gated)) {
165
+ const vocab = DIM_VOCAB[name];
166
+ if (vocab && !vocab.includes(dim.value))
167
+ continue;
168
+ dims[name] = dim;
169
+ }
170
+ if (Object.keys(dims).length === 0)
171
+ return null;
172
+ return { level, dims };
173
+ }
174
+ /**
175
+ * Off the hot path (fire-and-forget boot warm), so a wide budget. The server's
176
+ * brainExtractModel is a REASONING model (deepseek-v4-flash) and the who-am-i prompt is large
177
+ * (12 retrieved rules + the full dim-vocabulary schema), so the think+emit cycle runs ~10-15s
178
+ * — verified live that a 15s cap intermittently truncated to a timeout/null. 25s clears the
179
+ * tail; the cost is nil because this never blocks anything (background warm).
180
+ */
181
+ const BRAIN_CLASSIFY_TIMEOUT_MS = 25000;
182
+ /**
183
+ * The dims JSON is tiny (~200 tokens), but the server's extract model (deepseek-v4-flash) is a
184
+ * REASONING model — its thinking tokens count against this same budget (landing in a separate
185
+ * reasoning_content field, not the JSON). A tight cap let the reasoning consume the budget and
186
+ * truncate the actual JSON to empty → intermittent null profiles. Give thinking ample headroom
187
+ * so the dims JSON always survives; extra tokens are only spent when the model reasons, and
188
+ * this is an off-hot-path boot warm.
189
+ */
190
+ const BRAIN_CLASSIFY_MAX_TOKENS = 3000;
191
+ /**
192
+ * Derive a WhoAmIProfile from the behavioral brain. Search the user's style rules →
193
+ * classify them into dims via the brain LLM → privacy-gate locally. Fail-open null.
194
+ */
195
+ export async function deriveWhoAmIFromBrain(deps, level = BRAIN_TIER) {
196
+ try {
197
+ const points = await deps.searchByText(STYLE_PROBE, [BEHAVIORAL_COLLECTION], BRAIN_TOPK);
198
+ const rules = points
199
+ .map((p) => (typeof p.payload?.text === "string" ? p.payload.text.trim() : ""))
200
+ .filter((t) => t.length > 0);
201
+ if (rules.length === 0)
202
+ return null;
203
+ const reply = await deps.classifyViaBrain(buildStyleClassifyPrompt(rules), BRAIN_CLASSIFY_TIMEOUT_MS, {
204
+ systemPrompt: STYLE_SYSTEM_PROMPT,
205
+ responseFormat: { type: "json_object" },
206
+ maxTokens: BRAIN_CLASSIFY_MAX_TOKENS,
207
+ // Structured multi-dim extraction needs the server's stronger brainExtractModel;
208
+ // the hot-path model mis-spells the vocabulary (verified on the live VPS).
209
+ useExtractModel: true,
210
+ });
211
+ if (!reply)
212
+ return null;
213
+ return parseBrainProfile(reply, level);
214
+ }
215
+ catch {
216
+ // Fail-open — a degraded brain must never break the PIL hot path.
217
+ return null;
218
+ }
219
+ }
220
+ //# sourceMappingURL=who-am-i-brain.js.map
@@ -10,13 +10,13 @@ export interface WhoAmIProfile {
10
10
  level: PrivacyLevel;
11
11
  dims: Partial<Record<WhoAmIDimName, WhoAmIDim>>;
12
12
  }
13
- interface RawDim {
13
+ export interface RawDim {
14
14
  value: string | null;
15
15
  confidence?: number;
16
16
  sampleCount?: number;
17
17
  samples?: number;
18
18
  }
19
- interface RawProfile {
19
+ export interface RawProfile {
20
20
  dimensions?: Record<string, RawDim>;
21
21
  }
22
22
  /**
@@ -38,4 +38,11 @@ export declare function outputStyleFromProfile(profile: WhoAmIProfile | null): O
38
38
  export declare function getWhoAmIProfile(): WhoAmIProfile | null;
39
39
  /** Clear the cache — test-only / after a known profile change. */
40
40
  export declare function resetWhoAmICache(): void;
41
- export {};
41
+ /**
42
+ * Prime the per-process cache with an externally-derived profile (e.g. the
43
+ * thin-client brain fallback in who-am-i-brain.ts, wired via the bridge). Only
44
+ * overwrites when the device-local read produced nothing (`_cache` is null/unset)
45
+ * so a real on-device profile always wins over a brain-derived one. No-op when a
46
+ * device-local profile is already loaded.
47
+ */
48
+ export declare function primeWhoAmICache(profile: WhoAmIProfile | null): void;
@@ -110,6 +110,18 @@ export function getWhoAmIProfile() {
110
110
  export function resetWhoAmICache() {
111
111
  _cache = undefined;
112
112
  }
113
+ /**
114
+ * Prime the per-process cache with an externally-derived profile (e.g. the
115
+ * thin-client brain fallback in who-am-i-brain.ts, wired via the bridge). Only
116
+ * overwrites when the device-local read produced nothing (`_cache` is null/unset)
117
+ * so a real on-device profile always wins over a brain-derived one. No-op when a
118
+ * device-local profile is already loaded.
119
+ */
120
+ export function primeWhoAmICache(profile) {
121
+ if (_cache)
122
+ return;
123
+ _cache = profile;
124
+ }
113
125
  function loadWhoAmIProfile() {
114
126
  try {
115
127
  const req = createRequire(import.meta.url);
@@ -0,0 +1,48 @@
1
+ /**
2
+ * src/ee/workflow-event.ts
3
+ *
4
+ * Part C/D client for the EE `/api/workflow-event` write-during-execution
5
+ * channel (added in experience-engine Part D). Lets the council workflow persist
6
+ * experience MID-RUN — per-round debate outcomes, sprint results, decisions,
7
+ * mistakes — into the `workflow_*` collections, so a later run (or a later
8
+ * sprint in the SAME run) can recall it.
9
+ *
10
+ * Contract with the two Kill findings from the plan review:
11
+ * - Kill #7: on a 404 (endpoint disabled / older server) DROP the event — do
12
+ * NOT enqueue. Queuing 404s head-of-line-poisons the offline queue against a
13
+ * server that will never accept them.
14
+ * - Kill #4/#5: writes are fire-and-forget and off the critical path. Recall of
15
+ * these hot entries stays gated on the server side (tier:"intra-session").
16
+ *
17
+ * On a transient network failure the event IS enqueued to the offline queue so
18
+ * a later drain retries it. Never throws; never blocks the workflow.
19
+ */
20
+ import { enqueue } from "./offline-queue.js";
21
+ export type WorkflowEventKind = "council-debate" | "sprint-execution" | "decision" | "mistake";
22
+ export interface WorkflowEventPayload {
23
+ kind: WorkflowEventKind;
24
+ /** Run/phase reference, e.g. `runs/<runId>` or `runs/<runId>#sprint-3`. */
25
+ phaseRef: string;
26
+ sessionId?: string;
27
+ /** Short embeddable summary; falls back to a derived string server-side. */
28
+ text?: string;
29
+ /** Arbitrary structured detail persisted alongside the entry. */
30
+ payload?: Record<string, unknown>;
31
+ }
32
+ export interface FireWorkflowEventOpts {
33
+ baseUrl?: string;
34
+ authToken?: string;
35
+ timeoutMs?: number;
36
+ fetchImpl?: typeof fetch;
37
+ /** Test seam: override the offline-queue enqueue. */
38
+ enqueueImpl?: typeof enqueue;
39
+ }
40
+ /**
41
+ * POST a workflow event. Returns `true` when the server accepted it, `false`
42
+ * otherwise (dropped on 404, enqueued on network error). Never throws.
43
+ */
44
+ export declare function fireWorkflowEvent(payload: WorkflowEventPayload, opts?: FireWorkflowEventOpts): Promise<boolean>;
45
+ /** Fire-and-forget wrapper — never throws, never blocks the workflow. */
46
+ export declare function fireAndForgetWorkflowEvent(payload: WorkflowEventPayload, opts?: FireWorkflowEventOpts): void;
47
+ /** Test-only: reset the once-per-process warning latch. */
48
+ export declare function _resetWorkflowEventState(): void;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * src/ee/workflow-event.ts
3
+ *
4
+ * Part C/D client for the EE `/api/workflow-event` write-during-execution
5
+ * channel (added in experience-engine Part D). Lets the council workflow persist
6
+ * experience MID-RUN — per-round debate outcomes, sprint results, decisions,
7
+ * mistakes — into the `workflow_*` collections, so a later run (or a later
8
+ * sprint in the SAME run) can recall it.
9
+ *
10
+ * Contract with the two Kill findings from the plan review:
11
+ * - Kill #7: on a 404 (endpoint disabled / older server) DROP the event — do
12
+ * NOT enqueue. Queuing 404s head-of-line-poisons the offline queue against a
13
+ * server that will never accept them.
14
+ * - Kill #4/#5: writes are fire-and-forget and off the critical path. Recall of
15
+ * these hot entries stays gated on the server side (tier:"intra-session").
16
+ *
17
+ * On a transient network failure the event IS enqueued to the offline queue so
18
+ * a later drain retries it. Never throws; never blocks the workflow.
19
+ */
20
+ import { classifyEeError, logEeFailure } from "../utils/ee-logger.js";
21
+ import { getCachedAuthToken, getCachedServerBaseUrl } from "./auth.js";
22
+ import { enqueue } from "./offline-queue.js";
23
+ const DEFAULT_BASE = "http://localhost:8082";
24
+ const DEFAULT_TIMEOUT_MS = 3000;
25
+ const ENDPOINT = "/api/workflow-event";
26
+ let _warnedOnce = false;
27
+ /**
28
+ * POST a workflow event. Returns `true` when the server accepted it, `false`
29
+ * otherwise (dropped on 404, enqueued on network error). Never throws.
30
+ */
31
+ export async function fireWorkflowEvent(payload, opts = {}) {
32
+ const baseUrl = opts.baseUrl ?? getCachedServerBaseUrl() ?? DEFAULT_BASE;
33
+ const authToken = opts.authToken ?? getCachedAuthToken() ?? undefined;
34
+ const f = opts.fetchImpl ?? fetch;
35
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
36
+ const doEnqueue = opts.enqueueImpl ?? enqueue;
37
+ const headers = { "Content-Type": "application/json" };
38
+ if (authToken)
39
+ headers.Authorization = `Bearer ${authToken}`;
40
+ try {
41
+ const resp = await f(`${baseUrl}${ENDPOINT}`, {
42
+ method: "POST",
43
+ headers,
44
+ body: JSON.stringify(payload),
45
+ signal: AbortSignal.timeout(timeoutMs),
46
+ });
47
+ if (resp.ok)
48
+ return true;
49
+ // 404 = endpoint disabled or older server → DROP (Kill #7: never queue 404s).
50
+ if (resp.status === 404)
51
+ return false;
52
+ // Other non-OK (5xx, etc.) → treat like a transient failure: enqueue.
53
+ await doEnqueue({ endpoint: ENDPOINT, body: payload, enqueuedAt: Date.now() }).catch(() => { });
54
+ if (!_warnedOnce) {
55
+ _warnedOnce = true;
56
+ console.warn(`[ee] workflow-event non-OK ${resp.status}; queued (silenced after first warning)`);
57
+ }
58
+ return false;
59
+ }
60
+ catch (err) {
61
+ // Network error / timeout → enqueue for later drain.
62
+ await doEnqueue({ endpoint: ENDPOINT, body: payload, enqueuedAt: Date.now() }).catch(() => { });
63
+ if (!_warnedOnce) {
64
+ _warnedOnce = true;
65
+ console.warn(`[ee] workflow-event failed: ${err.message}; queued (silenced after first warning)`);
66
+ }
67
+ return false;
68
+ }
69
+ }
70
+ /** Fire-and-forget wrapper — never throws, never blocks the workflow. */
71
+ export function fireAndForgetWorkflowEvent(payload, opts = {}) {
72
+ void fireWorkflowEvent(payload, opts).catch((err) => {
73
+ logEeFailure("workflow-event.fireAndForgetWorkflowEvent", classifyEeError(err), err);
74
+ /* swallow */
75
+ });
76
+ }
77
+ /** Test-only: reset the once-per-process warning latch. */
78
+ export function _resetWorkflowEventState() {
79
+ _warnedOnce = false;
80
+ }
81
+ //# sourceMappingURL=workflow-event.js.map
@@ -4,7 +4,7 @@
4
4
  * Serializes messages, extracts preserved blocks, compresses if over budget,
5
5
  * then restores preserved blocks into the output.
6
6
  */
7
- import type { ModelMessage } from "ai";
7
+ import { type ModelMessage } from "ai";
8
8
  import { type PreservedBlock } from "./preserve.js";
9
9
  export interface CompressResult {
10
10
  summary: string;
@@ -17,6 +17,6 @@ export interface CompressResult {
17
17
  * 1. Serialize messages via serializeConversation().
18
18
  * 2. Extract preserved blocks.
19
19
  * 3. If under budget, return as-is (with preserved blocks restored).
20
- * 4. If over budget, truncate/compress and restore preserved blocks.
20
+ * 4. If over budget, compress via LLM (if provider given) or truncate, and restore blocks.
21
21
  */
22
- export declare function compressChat(messages: ModelMessage[], _systemPrompt: string, tokenBudget: number, _provider?: unknown, _modelId?: string): Promise<CompressResult>;
22
+ export declare function compressChat(messages: ModelMessage[], _systemPrompt: string, tokenBudget: number, provider?: unknown, modelId?: string, customInstructions?: string): Promise<CompressResult>;