muonroi-cli 1.7.2 → 1.8.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 (322) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +122 -122
  3. package/dist/packages/agent-harness-core/src/event-filter.js +1 -0
  4. package/dist/packages/agent-harness-core/src/event-redact.js +8 -0
  5. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +16 -0
  6. package/dist/packages/agent-harness-core/src/mcp-server.js +88 -10
  7. package/dist/packages/agent-harness-core/src/protocol.d.ts +15 -0
  8. package/dist/packages/agent-harness-core/src/selector.js +2 -2
  9. package/dist/packages/agent-harness-core/src/transports/sidechannel.js +1 -1
  10. package/dist/packages/agent-harness-opentui/src/agent-mode.js +1 -1
  11. package/dist/packages/agent-harness-opentui/src/install.js +1 -1
  12. package/dist/src/__test-stubs__/vitest-setup.js +4 -0
  13. package/dist/src/agent-harness/__tests__/mock-model.spec.js +54 -0
  14. package/dist/src/agent-harness/mock-model.d.ts +1 -1
  15. package/dist/src/agent-harness/mock-model.js +32 -3
  16. package/dist/src/agent-harness/test-spawn.js +3 -1
  17. package/dist/src/chat/chat-keychain.js +9 -1
  18. package/dist/src/cli/cost-forensics.js +12 -12
  19. package/dist/src/cli/keys.d.ts +2 -2
  20. package/dist/src/cli/keys.js +107 -28
  21. package/dist/src/cli/usage-report.js +2 -2
  22. package/dist/src/council/__tests__/abort-threading.test.d.ts +1 -0
  23. package/dist/src/council/__tests__/abort-threading.test.js +193 -0
  24. package/dist/src/council/__tests__/clarification-prompt.test.js +21 -1
  25. package/dist/src/council/__tests__/council-turn-length-emit.test.d.ts +1 -0
  26. package/dist/src/council/__tests__/council-turn-length-emit.test.js +130 -0
  27. package/dist/src/council/__tests__/debate-round-budget.test.d.ts +1 -0
  28. package/dist/src/council/__tests__/debate-round-budget.test.js +45 -0
  29. package/dist/src/council/__tests__/evaluator-metrics.test.js +3 -0
  30. package/dist/src/council/__tests__/post-debate-recommendation.test.d.ts +1 -0
  31. package/dist/src/council/__tests__/post-debate-recommendation.test.js +58 -0
  32. package/dist/src/council/__tests__/research-tools.test.js +4 -0
  33. package/dist/src/council/__tests__/round-tools.test.js +6 -0
  34. package/dist/src/council/clarifier-question-cap.test.d.ts +12 -0
  35. package/dist/src/council/clarifier-question-cap.test.js +73 -0
  36. package/dist/src/council/clarifier.d.ts +29 -1
  37. package/dist/src/council/clarifier.js +64 -1
  38. package/dist/src/council/debate-planner.d.ts +2 -1
  39. package/dist/src/council/debate-planner.js +5 -3
  40. package/dist/src/council/debate.d.ts +9 -0
  41. package/dist/src/council/debate.js +88 -8
  42. package/dist/src/council/index.d.ts +46 -1
  43. package/dist/src/council/index.js +125 -19
  44. package/dist/src/council/llm.js +41 -13
  45. package/dist/src/council/prompts.js +56 -21
  46. package/dist/src/council/types.d.ts +14 -1
  47. package/dist/src/ee/__tests__/export-transcripts.test.js +1 -1
  48. package/dist/src/ee/bridge.d.ts +3 -1
  49. package/dist/src/ee/bridge.js +9 -3
  50. package/dist/src/ee/bridge.test.js +1 -2
  51. package/dist/src/ee/client.js +15 -2
  52. package/dist/src/ee/recall-ledger.d.ts +20 -0
  53. package/dist/src/ee/recall-ledger.js +40 -1
  54. package/dist/src/ee/search.d.ts +25 -0
  55. package/dist/src/ee/search.js +73 -0
  56. package/dist/src/ee/types.d.ts +15 -1
  57. package/dist/src/ee/who-am-i.d.ts +41 -0
  58. package/dist/src/ee/who-am-i.js +143 -0
  59. package/dist/src/ee/who-am-i.test.d.ts +1 -0
  60. package/dist/src/ee/who-am-i.test.js +101 -0
  61. package/dist/src/generated/version.d.ts +1 -1
  62. package/dist/src/generated/version.js +1 -1
  63. package/dist/src/headless/council-answers.js +1 -0
  64. package/dist/src/headless/output.d.ts +30 -1
  65. package/dist/src/headless/output.js +180 -0
  66. package/dist/src/headless/output.test.js +61 -1
  67. package/dist/src/hooks/index.js +48 -2
  68. package/dist/src/index.d.ts +2 -0
  69. package/dist/src/index.js +55 -60
  70. package/dist/src/lsp/client-normalize.test.d.ts +12 -0
  71. package/dist/src/lsp/client-normalize.test.js +34 -0
  72. package/dist/src/lsp/client.d.ts +1 -0
  73. package/dist/src/lsp/client.js +10 -2
  74. package/dist/src/lsp/manager.d.ts +1 -1
  75. package/dist/src/lsp/manager.js +2 -2
  76. package/dist/src/lsp/manager.test.js +2 -2
  77. package/dist/src/lsp/runtime.d.ts +1 -1
  78. package/dist/src/lsp/runtime.js +2 -2
  79. package/dist/src/maintain/__tests__/gh-create-pr.test.js +7 -3
  80. package/dist/src/maintain/__tests__/pr-builder.test.js +7 -3
  81. package/dist/src/mcp/__tests__/client-pool.spec.js +3 -3
  82. package/dist/src/mcp/__tests__/ee-tools.test.js +42 -14
  83. package/dist/src/mcp/__tests__/forensics-tools.test.js +3 -3
  84. package/dist/src/mcp/__tests__/lsp-tools.test.js +4 -4
  85. package/dist/src/mcp/client-pool.d.ts +1 -1
  86. package/dist/src/mcp/client-pool.js +25 -9
  87. package/dist/src/mcp/ee-tools.d.ts +10 -0
  88. package/dist/src/mcp/ee-tools.js +34 -0
  89. package/dist/src/mcp/oauth-callback.js +2 -2
  90. package/dist/src/mcp/parse-headers.test.js +14 -14
  91. package/dist/src/mcp/self-verify-jobs.js +1 -3
  92. package/dist/src/mcp/setup-guide-text.js +74 -74
  93. package/dist/src/mcp/smoke.test.js +43 -43
  94. package/dist/src/models/catalog-gemini.test.js +12 -10
  95. package/dist/src/models/catalog.json +106 -38
  96. package/dist/src/ops/doctor.js +7 -7
  97. package/dist/src/orchestrator/__tests__/auto-commit.test.d.ts +1 -0
  98. package/dist/src/orchestrator/__tests__/auto-commit.test.js +142 -0
  99. package/dist/src/orchestrator/__tests__/batch-turn-runner.test.js +2 -0
  100. package/dist/src/orchestrator/__tests__/delegations.test.d.ts +1 -0
  101. package/dist/src/orchestrator/__tests__/delegations.test.js +96 -0
  102. package/dist/src/orchestrator/__tests__/message-processor.test.js +1 -0
  103. package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -0
  104. package/dist/src/orchestrator/agent.test.js +5 -2
  105. package/dist/src/orchestrator/auto-commit.d.ts +110 -0
  106. package/dist/src/orchestrator/auto-commit.js +364 -0
  107. package/dist/src/orchestrator/batch-turn-runner.d.ts +3 -2
  108. package/dist/src/orchestrator/batch-turn-runner.js +2 -1
  109. package/dist/src/orchestrator/compaction-proposer-prompt.d.ts +8 -0
  110. package/dist/src/orchestrator/compaction-proposer-prompt.js +43 -0
  111. package/dist/src/orchestrator/compaction.d.ts +22 -0
  112. package/dist/src/orchestrator/compaction.js +42 -0
  113. package/dist/src/orchestrator/delegations.d.ts +1 -0
  114. package/dist/src/orchestrator/delegations.js +41 -0
  115. package/dist/src/orchestrator/error-utils.js +27 -2
  116. package/dist/src/orchestrator/message-processor.d.ts +21 -5
  117. package/dist/src/orchestrator/message-processor.js +219 -2573
  118. package/dist/src/orchestrator/orchestrator.d.ts +8 -2
  119. package/dist/src/orchestrator/orchestrator.js +187 -34
  120. package/dist/src/orchestrator/preprocessor.d.ts +13 -0
  121. package/dist/src/orchestrator/preprocessor.js +145 -0
  122. package/dist/src/orchestrator/prompts.d.ts +8 -0
  123. package/dist/src/orchestrator/prompts.js +76 -60
  124. package/dist/src/orchestrator/safety-askcard.d.ts +66 -0
  125. package/dist/src/orchestrator/safety-askcard.js +74 -0
  126. package/dist/src/orchestrator/sandbox.test.js +8 -5
  127. package/dist/src/orchestrator/scope-ceiling.js +1 -1
  128. package/dist/src/orchestrator/stall-watchdog.d.ts +47 -0
  129. package/dist/src/orchestrator/stall-watchdog.js +23 -0
  130. package/dist/src/orchestrator/stall-watchdog.test.js +36 -1
  131. package/dist/src/orchestrator/stream-runner.js +52 -30
  132. package/dist/src/orchestrator/sub-agent-model-tier.js +1 -1
  133. package/dist/src/orchestrator/subagent-compactor.d.ts +14 -0
  134. package/dist/src/orchestrator/subagent-compactor.js +83 -10
  135. package/dist/src/orchestrator/subagent-compactor.spec.js +132 -0
  136. package/dist/src/orchestrator/text-tool-call-detector.d.ts +9 -5
  137. package/dist/src/orchestrator/text-tool-call-detector.js +16 -12
  138. package/dist/src/orchestrator/text-tool-call-detector.test.js +66 -8
  139. package/dist/src/orchestrator/tool-args-repair.js +1 -1
  140. package/dist/src/orchestrator/tool-engine.d.ts +151 -0
  141. package/dist/src/orchestrator/tool-engine.js +3079 -0
  142. package/dist/src/orchestrator/turn-runner-deps.d.ts +1 -0
  143. package/dist/src/pil/__tests__/discovery-types.test.js +15 -4
  144. package/dist/src/pil/__tests__/discovery.test.js +89 -145
  145. package/dist/src/pil/__tests__/dual-run.test.js +2 -0
  146. package/dist/src/pil/__tests__/layer1-intent-trace.test.js +3 -0
  147. package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
  148. package/dist/src/pil/__tests__/layer16-clarity.test.js +35 -32
  149. package/dist/src/pil/__tests__/layer18-acceptance.test.js +1 -78
  150. package/dist/src/pil/__tests__/layer2_5-ponytail.test.d.ts +1 -0
  151. package/dist/src/pil/__tests__/layer2_5-ponytail.test.js +53 -0
  152. package/dist/src/pil/__tests__/layer3-ee-injection.test.js +67 -4
  153. package/dist/src/pil/__tests__/layer3-injected-chunk.test.js +14 -2
  154. package/dist/src/pil/__tests__/layer4-gsd.test.js +30 -0
  155. package/dist/src/pil/__tests__/layer5-context.test.js +39 -1
  156. package/dist/src/pil/__tests__/layer6-output.test.js +30 -2
  157. package/dist/src/pil/__tests__/orchestrator-integration.test.js +3 -1
  158. package/dist/src/pil/__tests__/pipeline.test.js +11 -8
  159. package/dist/src/pil/__tests__/response-tools.test.js +52 -0
  160. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  161. package/dist/src/pil/agent-operating-contract.js +5 -3
  162. package/dist/src/pil/agent-operating-contract.test.js +2 -2
  163. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  164. package/dist/src/pil/cheap-model-playbook.js +1 -1
  165. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  166. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  167. package/dist/src/pil/config.d.ts +20 -0
  168. package/dist/src/pil/config.js +35 -0
  169. package/dist/src/pil/discovery-types.d.ts +32 -9
  170. package/dist/src/pil/discovery.d.ts +3 -3
  171. package/dist/src/pil/discovery.js +138 -244
  172. package/dist/src/pil/index.d.ts +1 -1
  173. package/dist/src/pil/index.js +1 -1
  174. package/dist/src/pil/layer1-intent.d.ts +9 -1
  175. package/dist/src/pil/layer1-intent.js +99 -44
  176. package/dist/src/pil/layer1-intent.test.js +112 -2
  177. package/dist/src/pil/layer15-context-scan.js +1 -1
  178. package/dist/src/pil/layer16-clarity.d.ts +21 -25
  179. package/dist/src/pil/layer16-clarity.js +52 -88
  180. package/dist/src/pil/layer18-acceptance.d.ts +13 -3
  181. package/dist/src/pil/layer18-acceptance.js +1 -75
  182. package/dist/src/pil/layer2-personality.js +7 -8
  183. package/dist/src/pil/layer2_5-ponytail.d.ts +2 -0
  184. package/dist/src/pil/layer2_5-ponytail.js +41 -0
  185. package/dist/src/pil/layer3-ee-injection.js +180 -14
  186. package/dist/src/pil/layer4-gsd.js +22 -8
  187. package/dist/src/pil/layer5-context.d.ts +1 -0
  188. package/dist/src/pil/layer5-context.js +26 -11
  189. package/dist/src/pil/layer6-output.js +23 -5
  190. package/dist/src/pil/llm-classify.js +3 -3
  191. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  192. package/dist/src/pil/native-capabilities-workbook.js +4 -3
  193. package/dist/src/pil/pipeline.js +15 -1
  194. package/dist/src/pil/response-tools.d.ts +16 -0
  195. package/dist/src/pil/response-tools.js +41 -0
  196. package/dist/src/pil/schema.d.ts +8 -0
  197. package/dist/src/pil/schema.js +24 -3
  198. package/dist/src/pil/task-tier-map.js +7 -7
  199. package/dist/src/pil/types.d.ts +9 -3
  200. package/dist/src/product-loop/__tests__/discovery-interview.test.js +103 -1
  201. package/dist/src/product-loop/__tests__/gather-selectable-alts.test.js +34 -0
  202. package/dist/src/product-loop/__tests__/sprint-self-verify.test.js +12 -12
  203. package/dist/src/product-loop/discovery-interview.d.ts +20 -0
  204. package/dist/src/product-loop/discovery-interview.js +121 -7
  205. package/dist/src/product-loop/done-gate.js +3 -3
  206. package/dist/src/product-loop/gather.js +19 -5
  207. package/dist/src/product-loop/loop-driver.js +20 -20
  208. package/dist/src/product-loop/progress-snapshot.js +4 -4
  209. package/dist/src/product-loop/sprint-self-verify.js +2 -2
  210. package/dist/src/providers/__tests__/reasoning-roundtrip.test.js +12 -0
  211. package/dist/src/providers/auth/__tests__/gemini-oauth.test.d.ts +1 -5
  212. package/dist/src/providers/auth/__tests__/gemini-oauth.test.js +1 -5
  213. package/dist/src/providers/auth/gcloud.d.ts +28 -0
  214. package/dist/src/providers/auth/gcloud.js +102 -0
  215. package/dist/src/providers/auth/gemini-oauth.d.ts +46 -19
  216. package/dist/src/providers/auth/gemini-oauth.js +292 -105
  217. package/dist/src/providers/auth/grok-oauth.d.ts +1 -1
  218. package/dist/src/providers/auth/grok-oauth.js +1 -1
  219. package/dist/src/providers/auth/registry.js +28 -6
  220. package/dist/src/providers/gemini.d.ts +1 -1
  221. package/dist/src/providers/gemini.js +10 -3
  222. package/dist/src/providers/keychain.d.ts +2 -2
  223. package/dist/src/providers/keychain.js +20 -2
  224. package/dist/src/providers/mcp-vision-bridge.js +48 -48
  225. package/dist/src/providers/openai.js +5 -1
  226. package/dist/src/providers/runtime.d.ts +1 -1
  227. package/dist/src/providers/runtime.js +11 -2
  228. package/dist/src/providers/siliconflow-sse-repair.js +1 -1
  229. package/dist/src/providers/strategies/google.strategy.d.ts +9 -2
  230. package/dist/src/providers/strategies/google.strategy.js +159 -9
  231. package/dist/src/providers/strategies/thinking-mode.js +19 -6
  232. package/dist/src/providers/types.d.ts +1 -1
  233. package/dist/src/reporter/index.js +1 -1
  234. package/dist/src/router/decide.test.js +40 -8
  235. package/dist/src/router/step-router.d.ts +20 -0
  236. package/dist/src/router/step-router.js +73 -0
  237. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  238. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  239. package/dist/src/scaffold/continuation-prompt.js +60 -60
  240. package/dist/src/scaffold/init-new.js +453 -453
  241. package/dist/src/self-qa/__tests__/scenario-planner.test.js +3 -3
  242. package/dist/src/self-qa/agentic-loop.js +19 -19
  243. package/dist/src/self-qa/orchestrator.js +5 -5
  244. package/dist/src/self-qa/scenario-planner.js +1 -1
  245. package/dist/src/self-qa/spec-emitter.js +4 -4
  246. package/dist/src/storage/__tests__/migrations.test.js +2 -2
  247. package/dist/src/storage/index.d.ts +1 -1
  248. package/dist/src/storage/index.js +1 -1
  249. package/dist/src/storage/interaction-log.js +5 -5
  250. package/dist/src/storage/migrations.js +122 -122
  251. package/dist/src/storage/session-experience-store.js +4 -4
  252. package/dist/src/storage/sessions.js +43 -43
  253. package/dist/src/storage/transcript-view.js +7 -1
  254. package/dist/src/storage/transcript.d.ts +1 -0
  255. package/dist/src/storage/transcript.js +55 -0
  256. package/dist/src/storage/ui-interaction-log.d.ts +17 -0
  257. package/dist/src/storage/usage.js +14 -14
  258. package/dist/src/storage/workspaces.js +12 -12
  259. package/dist/src/tools/__tests__/native-tools.test.js +6 -0
  260. package/dist/src/tools/bash.d.ts +36 -23
  261. package/dist/src/tools/bash.js +59 -301
  262. package/dist/src/tools/bash.test.js +2 -257
  263. package/dist/src/tools/file-tracker.d.ts +5 -0
  264. package/dist/src/tools/file-tracker.js +14 -0
  265. package/dist/src/tools/file.js +2 -0
  266. package/dist/src/tools/git-safety.d.ts +38 -1
  267. package/dist/src/tools/git-safety.js +94 -4
  268. package/dist/src/tools/git-safety.test.js +45 -1
  269. package/dist/src/tools/native-tools.d.ts +1 -1
  270. package/dist/src/tools/native-tools.js +56 -0
  271. package/dist/src/tools/registry-bash-empty-command.test.js +4 -3
  272. package/dist/src/tools/registry-git-safety.test.js +17 -6
  273. package/dist/src/tools/registry.d.ts +5 -0
  274. package/dist/src/tools/registry.js +185 -11
  275. package/dist/src/types/index.d.ts +7 -1
  276. package/dist/src/ui/app.d.ts +1 -0
  277. package/dist/src/ui/app.js +0 -0
  278. package/dist/src/ui/components/council-question-card.js +1 -0
  279. package/dist/src/ui/components/message-view.js +37 -3
  280. package/dist/src/ui/components/structured-response-view.js +7 -0
  281. package/dist/src/ui/components/task-list-panel.js +3 -12
  282. package/dist/src/ui/containers/chat-feed.d.ts +40 -0
  283. package/dist/src/ui/containers/chat-feed.js +66 -0
  284. package/dist/src/ui/containers/modals-layer.d.ts +87 -0
  285. package/dist/src/ui/containers/modals-layer.js +18 -0
  286. package/dist/src/ui/slash/__tests__/menu-parity.test.d.ts +1 -0
  287. package/dist/src/ui/slash/__tests__/menu-parity.test.js +1 -0
  288. package/dist/src/ui/slash/__tests__/ponytail.test.d.ts +1 -0
  289. package/dist/src/ui/slash/__tests__/ponytail.test.js +37 -0
  290. package/dist/src/ui/slash/council-inspect.js +4 -4
  291. package/dist/src/ui/slash/export.js +16 -1
  292. package/dist/src/ui/slash/menu-items.js +1 -0
  293. package/dist/src/ui/slash/ponytail.d.ts +2 -0
  294. package/dist/src/ui/slash/ponytail.js +19 -0
  295. package/dist/src/ui/status-bar/store.js +0 -1
  296. package/dist/src/ui/status-bar/store.test.js +2 -2
  297. package/dist/src/ui/use-app-logic.d.ts +26 -0
  298. package/dist/src/ui/use-app-logic.js +0 -0
  299. package/dist/src/ui/utils/relaunch.js +1 -1
  300. package/dist/src/ui/utils/text.d.ts +11 -0
  301. package/dist/src/ui/utils/text.js +21 -1
  302. package/dist/src/ui/utils/text.test.js +25 -1
  303. package/dist/src/ui/utils/tools.js +3 -1
  304. package/dist/src/usage/cost-log.js +2 -2
  305. package/dist/src/usage/estimator.d.ts +14 -0
  306. package/dist/src/usage/estimator.js +22 -0
  307. package/dist/src/utils/__tests__/compaction-caps.test.d.ts +21 -0
  308. package/dist/src/utils/__tests__/compaction-caps.test.js +187 -0
  309. package/dist/src/utils/__tests__/llm-deadline-abort.test.d.ts +1 -0
  310. package/dist/src/utils/__tests__/llm-deadline-abort.test.js +33 -0
  311. package/dist/src/utils/clipboard-image.js +23 -23
  312. package/dist/src/utils/llm-deadline.d.ts +9 -1
  313. package/dist/src/utils/llm-deadline.js +31 -2
  314. package/dist/src/utils/permission-mode.d.ts +33 -0
  315. package/dist/src/utils/permission-mode.js +164 -3
  316. package/dist/src/utils/permission-mode.test.js +92 -1
  317. package/dist/src/utils/settings.d.ts +27 -31
  318. package/dist/src/utils/settings.js +100 -109
  319. package/dist/src/utils/side-question.js +2 -2
  320. package/dist/src/utils/skills.js +3 -3
  321. package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
  322. package/package.json +1 -1
@@ -11,7 +11,7 @@
11
11
  */
12
12
  import { classifyViaBrain, pilContext } from "../ee/bridge.js";
13
13
  import { classify } from "../router/classifier/index.js";
14
- import { isLlmFirstClassifyEnabled, isUnifiedPilEnabled } from "./config.js";
14
+ import { getUnifiedPilBudgetMs, isLlmFirstBrainEnabled, isLlmFirstClassifyEnabled, isUnifiedPilEnabled, } from "./config.js";
15
15
  /** File/path reference regex — matches common source-file extensions. */
16
16
  const FILE_REF_RE = /[\w./-]+\.(ts|tsx|js|jsx|json|md|py|rs|go|cs)\b/gi;
17
17
  /** Keywords that force a "low" complexity signal (additive score -3). */
@@ -645,6 +645,49 @@ export async function layer1Intent(ctx, opts = {}) {
645
645
  complexity,
646
646
  complexityScore,
647
647
  };
648
+ // G3 (b1): reach the unified injection on the default (model-first)
649
+ // path. Without this, _brainData stays null and layer3 falls back to a
650
+ // legacy dense-only /api/search round-trip with divergent source
651
+ // attribution. Populate _brainData from the SAME server-side unified
652
+ // retrieval the offline cascade uses (one round-trip, bounded by budget)
653
+ // so layer3 renders the richer source="unified" block AND records the
654
+ // rateable ledger consistently. On chitchat / null / failure, _brainData
655
+ // stays null → layer3 legacy path (behaviour unchanged). Gate:
656
+ // MUONROI_LLM_FIRST_BRAIN=0 reverts.
657
+ let llmFirstBrainData = null;
658
+ if (isLlmFirstBrainEnabled() && intentKind !== "chitchat") {
659
+ let brainRaw = ctx.raw;
660
+ if (ctx.sessionId) {
661
+ brainRaw =
662
+ ctx.raw +
663
+ ' [EE task checkpoints ("Context checkpoint summary" with ✔ DONE) from prior compactions are available via the ee.query tool. ' +
664
+ "To keep full tool history this turn when you see pre-warn or compaction note, emit exact literal PRESERVE_FULL_CONTEXT in reasoning or assistant note. " +
665
+ 'Self-check "task finished?" or "compacted yet this turn?" using the checkpoints.]';
666
+ }
667
+ intentTrace.pass3UnifiedAttempted = true;
668
+ // try/catch (not .catch) so a SYNC throw from pilContext (e.g. no EE
669
+ // client configured, as in unit tests) is also swallowed — it must
670
+ // never break the model-first return; _brainData just stays null.
671
+ let resp = null;
672
+ try {
673
+ resp = await pilContext(brainRaw, {
674
+ projectCtx: domain ? { domain } : undefined,
675
+ budgetMs: getUnifiedPilBudgetMs(),
676
+ });
677
+ }
678
+ catch (err) {
679
+ console.error(`[pil/layer1] llm-first unified brain fetch failed: ${err?.message}`);
680
+ }
681
+ if (resp) {
682
+ llmFirstBrainData = {
683
+ t0_principles: resp.t0_principles,
684
+ t1_rules: resp.t1_rules,
685
+ t2_patterns: resp.t2_patterns,
686
+ retrieval_skipped_reason: resp.retrieval_skipped_reason,
687
+ };
688
+ intentTrace.pass3UnifiedSucceeded = true;
689
+ }
690
+ }
648
691
  return {
649
692
  ...ctx,
650
693
  taskType: llmRes.taskType,
@@ -665,9 +708,10 @@ export async function layer1Intent(ctx, opts = {}) {
665
708
  // the raw prompt.
666
709
  ecosystemScope: llmRes.ecosystemScope,
667
710
  replyLanguage: llmRes.replyLanguage,
668
- // null lets L6 run its cheap style-rescue if outputStyle is still null;
669
- // EE retrieval enrichment happens downstream in layer3 as usual.
670
- _brainData: null,
711
+ // G3 (b1): populated from the unified pil-context fetch above so
712
+ // layer3 renders source="unified" instead of its legacy dense-only
713
+ // round-trip. null (chitchat / fetch failed) → layer3 legacy path.
714
+ _brainData: llmFirstBrainData,
671
715
  _intentTrace: intentTrace,
672
716
  layers: [
673
717
  ...ctx.layers,
@@ -1040,8 +1084,11 @@ export async function layer1Intent(ctx, opts = {}) {
1040
1084
  // accidentally swallow phrases like "refactor this" or "fix the bug" that
1041
1085
  // happen to be short.
1042
1086
  //
1043
- // TODO(WhoAmI-Pass0): when EE v4.0 Who Am I profile is available, read
1044
- // communication.brevity + decision_speed here as outputStyle baseline.
1087
+ // WhoAmI v4.0: the outputStyle baseline from communication.brevity +
1088
+ // decision_speed is now wired below (getWhoAmIProfile/outputStyleFromProfile
1089
+ // at the classifier-default branch — it replaces "balanced" when no per-turn
1090
+ // signal resolves the style). Not applied at this chitchat hot-path: greetings
1091
+ // already force "concise" regardless of profile.
1045
1092
  const trimmed = ctx.raw.trim();
1046
1093
  const wordCount = trimmed.split(/\s+/).filter(Boolean).length;
1047
1094
  const noTaskSignal = taskType === null || (taskType === "general" && result.reason === "regex:short-message");
@@ -1096,7 +1143,7 @@ export async function layer1Intent(ctx, opts = {}) {
1096
1143
  }
1097
1144
  const resp = await pilContext(brainRaw, {
1098
1145
  projectCtx: domain ? { domain } : undefined,
1099
- budgetMs: 1500,
1146
+ budgetMs: getUnifiedPilBudgetMs(),
1100
1147
  });
1101
1148
  if (resp) {
1102
1149
  if (resp.taskType)
@@ -1196,40 +1243,40 @@ export async function layer1Intent(ctx, opts = {}) {
1196
1243
  // touch existing files.
1197
1244
  // 0.7 confidence threshold for Pass 2 keyword override remains
1198
1245
  // unchanged (HIGH_CONF_THRESHOLD_PASS2 above).
1199
- const brainRaw = await classifyViaBrain(`You are a multilingual prompt classifier. The user's prompt may be in English, Vietnamese, or a mix of both.
1200
- Classify the prompt's INTENT (not its language). Reply with TWO lowercase words separated by a comma: <category>,<style>
1201
-
1202
- Category — pick ONE (listed in neutral order, no precedence):
1203
- analyze — explain / inspect / review existing code (giải thích, phân tích, review)
1204
- debug — fix a bug or investigate failure (sửa lỗi, fix bug, lỗi, traceback)
1205
- generate — create new code/file or add new behavior (tạo, sinh code, viết function mới, thêm)
1206
- refactor — restructure existing code (tái cấu trúc, refactor)
1207
- plan — design / roadmap / architecture (kế hoạch, thiết kế, kiến trúc)
1208
- documentation — write docs/comments (viết docs, comment, jsdoc)
1209
- general — chitchat OR unclear / ambiguous coding intent
1210
-
1211
- Rules (Phase 4 4P-2 disambiguation):
1212
- - Only return refactor when the user EXPLICITLY uses one of: rename, restructure, reorganize, extract, inline, move, migrate, reshape — applied to EXISTING code WITHOUT adding new behavior.
1213
- - Feature additions ('add flag', 'thêm', 'create endpoint', 'thêm option'), changing a DEFAULT value, adding tests, or improving coverage are 'generate' — NOT refactor.
1214
- - 'improve', 'change', 'update', 'modify', 'đổi', 'cải thiện' alone do NOT imply refactor — pick the specific category by what the change actually does.
1215
- - When the request is ambiguous, prefer 'general' over guessing refactor.
1216
-
1217
- Negative examples (NOT refactor):
1218
- - "đổi default --max-tool-rounds 8 sang 12" → generate
1219
- - "improve test coverage" → generate
1220
- - "tại sao X trả empty" → analyze
1221
- - "fix CI failing" → debug
1222
-
1223
- Style — pick ONE:
1224
- concise (ngắn gọn) | balanced (cân bằng) | detailed (chi tiết)
1225
-
1226
- Examples:
1227
- "Refactor this function" → refactor,balanced
1228
- "tại sao test fail" → debug,balanced
1229
- "thiết kế hệ thống auth" → plan,detailed
1230
- "thêm flag --foo" → generate,concise
1231
- "hi" → general,concise
1232
-
1246
+ const brainRaw = await classifyViaBrain(`You are a multilingual prompt classifier. The user's prompt may be in English, Vietnamese, or a mix of both.
1247
+ Classify the prompt's INTENT (not its language). Reply with TWO lowercase words separated by a comma: <category>,<style>
1248
+
1249
+ Category — pick ONE (listed in neutral order, no precedence):
1250
+ analyze — explain / inspect / review existing code (giải thích, phân tích, review)
1251
+ debug — fix a bug or investigate failure (sửa lỗi, fix bug, lỗi, traceback)
1252
+ generate — create new code/file or add new behavior (tạo, sinh code, viết function mới, thêm)
1253
+ refactor — restructure existing code (tái cấu trúc, refactor)
1254
+ plan — design / roadmap / architecture (kế hoạch, thiết kế, kiến trúc)
1255
+ documentation — write docs/comments (viết docs, comment, jsdoc)
1256
+ general — chitchat OR unclear / ambiguous coding intent
1257
+
1258
+ Rules (Phase 4 4P-2 disambiguation):
1259
+ - Only return refactor when the user EXPLICITLY uses one of: rename, restructure, reorganize, extract, inline, move, migrate, reshape — applied to EXISTING code WITHOUT adding new behavior.
1260
+ - Feature additions ('add flag', 'thêm', 'create endpoint', 'thêm option'), changing a DEFAULT value, adding tests, or improving coverage are 'generate' — NOT refactor.
1261
+ - 'improve', 'change', 'update', 'modify', 'đổi', 'cải thiện' alone do NOT imply refactor — pick the specific category by what the change actually does.
1262
+ - When the request is ambiguous, prefer 'general' over guessing refactor.
1263
+
1264
+ Negative examples (NOT refactor):
1265
+ - "đổi default --max-tool-rounds 8 sang 12" → generate
1266
+ - "improve test coverage" → generate
1267
+ - "tại sao X trả empty" → analyze
1268
+ - "fix CI failing" → debug
1269
+
1270
+ Style — pick ONE:
1271
+ concise (ngắn gọn) | balanced (cân bằng) | detailed (chi tiết)
1272
+
1273
+ Examples:
1274
+ "Refactor this function" → refactor,balanced
1275
+ "tại sao test fail" → debug,balanced
1276
+ "thiết kế hệ thống auth" → plan,detailed
1277
+ "thêm flag --foo" → generate,concise
1278
+ "hi" → general,concise
1279
+
1233
1280
  Prompt: "${ctx.raw.slice(0, 500)}"`, 1500);
1234
1281
  if (brainRaw) {
1235
1282
  pass3LegacyTaskSucceeded = true;
@@ -1280,9 +1327,9 @@ Prompt: "${ctx.raw.slice(0, 500)}"`, 1500);
1280
1327
  if (pass3LegacyTaskAttempted) {
1281
1328
  legacyBrainAttempted = true;
1282
1329
  pass3LegacyStyleAttempted = true;
1283
- const brainRawStyle = await classifyViaBrain(`Detect the user's preferred output style. The prompt may be EN or VN.
1284
- Reply with ONE word: concise (ngắn gọn) | balanced (bình thường) | detailed (chi tiết).
1285
-
1330
+ const brainRawStyle = await classifyViaBrain(`Detect the user's preferred output style. The prompt may be EN or VN.
1331
+ Reply with ONE word: concise (ngắn gọn) | balanced (bình thường) | detailed (chi tiết).
1332
+
1286
1333
  Prompt: "${ctx.raw.slice(0, 300)}"`, 800);
1287
1334
  if (brainRawStyle) {
1288
1335
  pass3LegacyStyleSucceeded = true;
@@ -1293,6 +1340,14 @@ Prompt: "${ctx.raw.slice(0, 300)}"`, 800);
1293
1340
  }
1294
1341
  }
1295
1342
  }
1343
+ else if (opts.profileStyleBaseline) {
1344
+ // WhoAmI v4.0 baseline: a standing communication.brevity/decision_speed
1345
+ // preference (computed once in the pipeline from the on-device profile)
1346
+ // fills in when no per-turn signal resolved the style. Per-turn detection
1347
+ // above still wins (override); this only replaces the generic "balanced".
1348
+ outputStyle = opts.profileStyleBaseline;
1349
+ styleSource = "whoami-profile";
1350
+ }
1296
1351
  else {
1297
1352
  outputStyle = "balanced";
1298
1353
  styleSource = "classifier-default";
@@ -12,14 +12,23 @@ vi.mock("./config.js", () => ({
12
12
  // Default OFF so the existing cascade tests below exercise the regex passes.
13
13
  // The model-first gate has its own describe block that flips this to true.
14
14
  isLlmFirstClassifyEnabled: vi.fn(() => false),
15
+ // G3 (b1): default OFF so the existing model-first classification tests don't
16
+ // trigger the unified brain fetch; the brain-path test flips it true.
17
+ isLlmFirstBrainEnabled: vi.fn(() => false),
18
+ // Pass-3 unified reads the client-side budget; provided so the whole-module
19
+ // mock does not drop the export (the model-first tests never hit it, but the
20
+ // cascade tests can).
21
+ getUnifiedPilBudgetMs: vi.fn(() => 3500),
15
22
  }));
16
- import { classifyViaBrain } from "../ee/bridge.js";
23
+ import { classifyViaBrain, pilContext } from "../ee/bridge.js";
17
24
  import { classify } from "../router/classifier/index.js";
18
- import { isLlmFirstClassifyEnabled } from "./config.js";
25
+ import { isLlmFirstBrainEnabled, isLlmFirstClassifyEnabled } from "./config.js";
19
26
  import { hasActionableToolIntent, isGreenfieldBuildTask, isSocialPleasantry, isStatusCheckQuestion, layer1Intent, } from "./layer1-intent.js";
20
27
  const mockedClassify = vi.mocked(classify);
21
28
  const mockedClassifyViaBrain = vi.mocked(classifyViaBrain);
22
29
  const mockedLlmFirst = vi.mocked(isLlmFirstClassifyEnabled);
30
+ const mockedLlmFirstBrain = vi.mocked(isLlmFirstBrainEnabled);
31
+ const mockedPilContext = vi.mocked(pilContext);
23
32
  function makeCtx(raw) {
24
33
  return {
25
34
  raw,
@@ -560,6 +569,10 @@ describe("Pass 2.6 — social pleasantries route to chitchat (drop the tool-sche
560
569
  describe("layer1Intent — model-first gate (MUONROI_LLM_FIRST_CLASSIFY)", () => {
561
570
  beforeEach(() => {
562
571
  mockedLlmFirst.mockReturnValue(true);
572
+ // G3 (b1): brain fetch OFF by default so the classification tests don't
573
+ // trigger pilContext; the brain-path tests flip it on explicitly.
574
+ mockedLlmFirstBrain.mockReturnValue(false);
575
+ mockedPilContext.mockReset();
563
576
  // Make the regex cascade obviously WRONG so passing tests prove the model won.
564
577
  mockedClassify.mockReturnValue({ tier: "hot", reason: "regex:create-file", confidence: 0.9 });
565
578
  });
@@ -582,6 +595,75 @@ describe("layer1Intent — model-first gate (MUONROI_LLM_FIRST_CLASSIFY)", () =>
582
595
  expect(result._intentTrace?.pass1Reason).toBe("llm-first");
583
596
  expect(mockedClassify).not.toHaveBeenCalled();
584
597
  });
598
+ it("G3 (b1): populates _brainData from pilContext on the model-first path (→ layer3 unified)", async () => {
599
+ mockedLlmFirstBrain.mockReturnValue(true);
600
+ // biome-ignore lint/suspicious/noExplicitAny: partial pil-context shape for the test
601
+ mockedPilContext.mockResolvedValue({
602
+ taskType: "general",
603
+ intentKind: "task",
604
+ outputStyle: "concise",
605
+ confidence: 0.9,
606
+ t0_principles: [{ text: "principle-1", score: 0.9 }],
607
+ t1_rules: [{ text: "rule-1", score: 0.8 }],
608
+ t2_patterns: [],
609
+ retrieval_skipped_reason: null,
610
+ });
611
+ const result = await layer1Intent(makeCtx("explain the auth flow"), {
612
+ llmFallback: async () => ({
613
+ taskType: "general",
614
+ outputStyle: "concise",
615
+ confidence: 0.9,
616
+ intentKind: "task",
617
+ deliverableKind: "answer",
618
+ depthTier: null,
619
+ ecosystemScope: null,
620
+ replyLanguage: null,
621
+ }),
622
+ });
623
+ // _brainData now carries the unified brain → layer3 renders source="unified"
624
+ // instead of its legacy dense-only /api/search round-trip.
625
+ expect(result._brainData?.t0_principles).toHaveLength(1);
626
+ expect(result._brainData?.t1_rules).toHaveLength(1);
627
+ expect(result._intentTrace?.pass3UnifiedAttempted).toBe(true);
628
+ expect(result._intentTrace?.pass3UnifiedSucceeded).toBe(true);
629
+ expect(mockedPilContext).toHaveBeenCalledTimes(1);
630
+ });
631
+ it("G3 (b1): chitchat skips the brain fetch (no pilContext round-trip)", async () => {
632
+ mockedLlmFirstBrain.mockReturnValue(true);
633
+ const result = await layer1Intent(makeCtx("cảm ơn nhé"), {
634
+ llmFallback: async () => ({
635
+ taskType: "general",
636
+ outputStyle: "concise",
637
+ confidence: 0.9,
638
+ intentKind: "chitchat",
639
+ deliverableKind: "answer",
640
+ depthTier: null,
641
+ ecosystemScope: null,
642
+ replyLanguage: null,
643
+ }),
644
+ });
645
+ expect(result._brainData).toBeNull();
646
+ expect(mockedPilContext).not.toHaveBeenCalled();
647
+ });
648
+ it("G3 (b1): a failed/empty pilContext leaves _brainData null (layer3 legacy path)", async () => {
649
+ mockedLlmFirstBrain.mockReturnValue(true);
650
+ mockedPilContext.mockResolvedValue(null);
651
+ const result = await layer1Intent(makeCtx("explain the auth flow"), {
652
+ llmFallback: async () => ({
653
+ taskType: "general",
654
+ outputStyle: "concise",
655
+ confidence: 0.9,
656
+ intentKind: "task",
657
+ deliverableKind: "answer",
658
+ depthTier: null,
659
+ ecosystemScope: null,
660
+ replyLanguage: null,
661
+ }),
662
+ });
663
+ expect(result._brainData).toBeNull();
664
+ expect(result.taskType).toBe("general"); // classification still intact
665
+ expect(mockedPilContext).toHaveBeenCalledTimes(1);
666
+ });
585
667
  it("marks chitchat from the model for a pure greeting", async () => {
586
668
  const result = await layer1Intent(makeCtx("cảm ơn bạn nhé"), {
587
669
  llmFallback: async () => ({
@@ -651,4 +733,32 @@ describe("layer1Intent — model-first gate (MUONROI_LLM_FIRST_CLASSIFY)", () =>
651
733
  expect(result.taskType).toBe("debug");
652
734
  });
653
735
  });
736
+ describe("layer1Intent — WhoAmI v4.0 output-style baseline (opts.profileStyleBaseline)", () => {
737
+ // A cheap regex task hit (high conf, unified PIL mocked off) skips the brain and
738
+ // lands on the classifier-default branch — exactly where the profile baseline now
739
+ // applies. The pipeline derives the baseline from the profile; layer1 just consumes
740
+ // the option. The prompt carries no explicit style cue.
741
+ const REFACTOR_PROMPT = "refactor the authentication module to use the new provider";
742
+ it("uses the profile-derived style baseline when no per-turn signal resolves it", async () => {
743
+ mockedClassify.mockReturnValue({ tier: "hot", reason: "regex:refactor", confidence: 0.9 });
744
+ const result = await layer1Intent(makeCtx(REFACTOR_PROMPT), { profileStyleBaseline: "concise" });
745
+ expect(result.taskType).toBe("refactor");
746
+ expect(result.outputStyle).toBe("concise");
747
+ expect(result._intentTrace?.styleSource).toBe("whoami-profile");
748
+ });
749
+ it("falls back to balanced when there is no profile baseline (behaviour unchanged)", async () => {
750
+ mockedClassify.mockReturnValue({ tier: "hot", reason: "regex:refactor", confidence: 0.9 });
751
+ const result = await layer1Intent(makeCtx(REFACTOR_PROMPT), { profileStyleBaseline: null });
752
+ expect(result.outputStyle).toBe("balanced");
753
+ expect(result._intentTrace?.styleSource).toBe("classifier-default");
754
+ });
755
+ it("an explicit per-turn style request still overrides the profile baseline", async () => {
756
+ mockedClassify.mockReturnValue({ tier: "hot", reason: "regex:refactor", confidence: 0.9 });
757
+ const result = await layer1Intent(makeCtx("refactor the auth module and explain step by step"), {
758
+ profileStyleBaseline: "concise",
759
+ });
760
+ expect(result.outputStyle).toBe("detailed");
761
+ expect(result._intentTrace?.styleSource).toBe("explicit-regex");
762
+ });
763
+ });
654
764
  //# sourceMappingURL=layer1-intent.test.js.map
@@ -76,7 +76,7 @@ export function scanBoundedContexts(cwd) {
76
76
  const entryNames = ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"];
77
77
  const entryFiles = entryNames.map((e) => join(dirPath, e)).filter((e) => existsSync(join(cwd, e)));
78
78
  const exportedSymbols = extractExports(cwd, entryFiles).slice(0, 20);
79
- return { path: dirPath + "/", name: d, entryFiles, exportedSymbols };
79
+ return { path: `${dirPath}/`, name: d, entryFiles, exportedSymbols };
80
80
  });
81
81
  }
82
82
  function extractExports(cwd, entryFiles) {
@@ -1,33 +1,29 @@
1
1
  /**
2
2
  * src/pil/layer16-clarity.ts
3
3
  *
4
- * Phase 2 (2026-06-16): `detectClarityGaps` and its keyword option-builders
5
- * (`buildOutcomeOptions` / `buildScopeOptions` / `pickBest*` / recency ranking)
6
- * were removed. The configured chat model now decides every clarification —
7
- * its questions, options, recommended default, and reason — in
8
- * `proposeModelGaps` (`discovery.ts`). There is no regex gap synthesis.
4
+ * Phase 3 (2026-06-23): the model now generates full ModelCard[] directly
5
+ * (questions, options, kinds, cancel/adjust markers) no ClarityGap
6
+ * intermediate, no sentinel strings, no hardcoded "Type something" option.
9
7
  *
10
- * What remains here is gap RENDERING + RESOLUTION (consumed by the model path):
11
- * - the "provide my own details" no-answer sentinel,
12
- * - `buildInterviewQuestion` (ClarityGap askcard),
13
- * - `resolveGapsNonInteractive` (default-answer resolution when headless),
14
- * - `getAutofilledOutcome` / `getDefaultOutcome` (outcome-label polish).
8
+ * What remains here is thin RENDERING + HEADLESS RESOLUTION:
9
+ * - `modelCardToQuestion` (ModelCard CouncilQuestionData)
10
+ * - `resolveGapsNonInteractive` (default-answer resolution when headless)
15
11
  */
16
12
  import type { CouncilQuestionData } from "../types/index.js";
17
- import type { ClarifiedIntent, ClarityGap, ProjectContext } from "./discovery-types.js";
18
- import type { TaskType } from "./types.js";
13
+ import type { ClarifiedIntent, ModelCard, ProjectContext } from "./discovery-types.js";
19
14
  /**
20
- * The default "no specific answer" meta-option offered for a model-generated
21
- * clarification when the model supplies no concrete recommendations. Selecting
22
- * it means "use your judgment / I have nothing specific to add" — it is a
23
- * sentinel, NOT a real outcome, so it must never surface verbatim as the
24
- * resolved outcome. Centralised here so discovery.ts (which presents the
25
- * option) and the outcome-resolution paths agree on the exact strings.
15
+ * Map a model-designed card into a CouncilQuestionData for TUI rendering.
16
+ * The model controls every field; the CLI only assigns questionId and maps
17
+ * the card options 1:1 into CouncilQuestionOptions (keeping isCancel/isAdjust).
26
18
  */
27
- export declare const PROVIDE_OWN_DETAILS_OPTION_EN = "I will provide my own details / constraints";
28
- export declare const PROVIDE_OWN_DETAILS_OPTION_VI = "T\u00F4i s\u1EBD tr\u1EA3 l\u1EDDi t\u1EF1 do / cung c\u1EA5p chi ti\u1EBFt c\u1EA7n thi\u1EBFt";
29
- /** True when an answer is the "I'll provide my own details" meta-option (any locale). */
30
- export declare function isProvideOwnDetailsSentinel(answer: string | null | undefined): boolean;
31
- export declare function buildInterviewQuestion(gap: ClarityGap, questionId: string): CouncilQuestionData;
32
- export declare function resolveGapsNonInteractive(gaps: ClarityGap[], projectContext: ProjectContext, raw: string): ClarifiedIntent;
33
- export declare function getAutofilledOutcome(taskType: TaskType | null, raw?: string): string | null;
19
+ export declare function modelCardToQuestion(card: ModelCard, questionId: string): CouncilQuestionData;
20
+ /**
21
+ * Resolve model cards with best-effort defaults when there is no interactive
22
+ * handler (headless mode). Picks the default option for each card.
23
+ */
24
+ export declare function resolveGapsNonInteractive(cards: ModelCard[], projectContext: ProjectContext, raw: string): ClarifiedIntent;
25
+ /**
26
+ * Get a default outcome label for well-known task types when no outcome
27
+ * was voiced by the user and no card provided one.
28
+ */
29
+ export declare function getDefaultOutcome(taskType: string | null, raw?: string): string;
@@ -1,107 +1,71 @@
1
- import { hasOperationalScope } from "./clarity-gate.js";
2
1
  /**
3
- * The default "no specific answer" meta-option offered for a model-generated
4
- * clarification when the model supplies no concrete recommendations. Selecting
5
- * it means "use your judgment / I have nothing specific to add" — it is a
6
- * sentinel, NOT a real outcome, so it must never surface verbatim as the
7
- * resolved outcome. Centralised here so discovery.ts (which presents the
8
- * option) and the outcome-resolution paths agree on the exact strings.
2
+ * Map a model-designed card into a CouncilQuestionData for TUI rendering.
3
+ * The model controls every field; the CLI only assigns questionId and maps
4
+ * the card options 1:1 into CouncilQuestionOptions (keeping isCancel/isAdjust).
9
5
  */
10
- export const PROVIDE_OWN_DETAILS_OPTION_EN = "I will provide my own details / constraints";
11
- export const PROVIDE_OWN_DETAILS_OPTION_VI = "Tôi sẽ trả lời tự do / cung cấp chi tiết cần thiết";
12
- /** True when an answer is the "I'll provide my own details" meta-option (any locale). */
13
- export function isProvideOwnDetailsSentinel(answer) {
14
- if (!answer)
15
- return false;
16
- const norm = answer.trim().toLowerCase();
17
- return norm === PROVIDE_OWN_DETAILS_OPTION_EN.toLowerCase() || norm === PROVIDE_OWN_DETAILS_OPTION_VI.toLowerCase();
18
- }
19
- export function buildInterviewQuestion(gap, questionId) {
20
- const options = gap.options.map((label) => ({
21
- label,
22
- value: label,
23
- kind: "choice",
6
+ export function modelCardToQuestion(card, questionId) {
7
+ const options = card.options.map((o) => ({
8
+ label: o.label,
9
+ value: o.label,
10
+ kind: o.kind,
11
+ description: o.description,
12
+ isCancel: o.isCancel,
13
+ isAdjust: o.isAdjust,
24
14
  }));
25
- options.push({
26
- label: "Type something",
27
- description: "Enter a custom answer",
28
- value: "",
29
- kind: "freetext",
30
- });
31
15
  return {
32
16
  questionId,
33
- question: gap.suggestedQuestion,
34
- context: gap.description,
35
- isRequired: false,
17
+ question: card.question,
18
+ context: card.context,
19
+ isRequired: true,
36
20
  phase: "pil-interview",
37
21
  options,
38
- defaultIndex: gap.defaultIndex,
22
+ defaultIndex: card.defaultIndex ?? 0,
39
23
  };
40
24
  }
41
- export function resolveGapsNonInteractive(gaps, projectContext, raw) {
42
- let outcome = "";
43
- let scope = [];
44
- const constraints = [];
45
- for (const gap of gaps) {
46
- const defaultAnswer = gap.options[gap.defaultIndex] ?? gap.options[0] ?? "";
47
- switch (gap.dimension) {
48
- case "outcome":
49
- // The "provide my own details" meta-option is a no-answer sentinel —
50
- // leave outcome empty so the inferred/default outcome is used downstream.
51
- outcome = isProvideOwnDetailsSentinel(defaultAnswer) ? "" : defaultAnswer;
52
- break;
53
- case "scope": {
54
- const relevant = projectContext.relevantModules.map((m) => m.path);
55
- scope = relevant.length > 0 ? relevant : [defaultAnswer];
56
- break;
57
- }
58
- case "constraint":
59
- constraints.push(defaultAnswer);
60
- break;
25
+ /**
26
+ * Resolve model cards with best-effort defaults when there is no interactive
27
+ * handler (headless mode). Picks the default option for each card.
28
+ */
29
+ export function resolveGapsNonInteractive(cards, projectContext, raw) {
30
+ const answers = [];
31
+ for (const card of cards) {
32
+ const defaultIdx = card.defaultIndex ?? 0;
33
+ const opt = card.options[defaultIdx];
34
+ if (opt?.kind === "freetext") {
35
+ // Freetext can't be auto-answered; fall back to raw-derived outcome
36
+ answers.push("");
37
+ }
38
+ else {
39
+ answers.push(opt?.label ?? "");
61
40
  }
62
41
  }
63
- if (!outcome)
64
- outcome = getDefaultOutcome(raw);
65
- if (scope.length === 0) {
66
- scope = projectContext.relevantModules.map((m) => m.path);
67
- if (scope.length === 0)
68
- scope = ["project root"];
69
- }
42
+ const outcome = answers.find((a) => a.length > 0) ?? `Complete: ${raw.slice(0, 80)}`;
43
+ const scope = projectContext.relevantModules.length > 0 ? projectContext.relevantModules.map((m) => m.path) : ["project root"];
70
44
  return {
71
45
  outcome,
72
46
  scope,
73
- constraints,
74
- gaps: gaps.map((g) => ({ ...g, answer: null })),
47
+ constraints: [],
48
+ gaps: [],
75
49
  };
76
50
  }
77
- const DEFAULT_OUTCOMES = {
78
- analyze: "Detailed analysis with concrete improvement recommendations",
79
- plan: "Step-by-step plan",
80
- documentation: "Docs updated",
81
- debug: "Error resolved, expected behavior restored",
82
- };
83
- export function getAutofilledOutcome(taskType, raw) {
84
- if (!taskType || !raw)
85
- return null;
86
- const lower = raw.toLowerCase();
87
- const isNativeMeta = /đánh giá|phân tích|cải thiện|fix|native|agent.*inside|cli.*bên trong|phỏng vấn|discovery/i.test(lower);
88
- if (isNativeMeta) {
89
- // Force good outcome for self-review / native meta prompts regardless of L1 taskType (analyze/debug)
90
- // Prevents generic "Local path...", "In prompts/ directory...", "Complete the task..." in [Discovery]
91
- return "Native self-assessment of the CLI with specific, actionable code fixes proposed and verified";
92
- }
93
- // Operational debug tasks (CI/build/deploy) have a stronger default outcome.
94
- if (taskType === "debug" && hasOperationalScope(raw)) {
95
- return "Pipeline green, all checks passing";
96
- }
97
- return DEFAULT_OUTCOMES[taskType] ?? null;
98
- }
99
- function getDefaultOutcome(raw) {
100
- const lower = raw.toLowerCase();
101
- const isNativeMeta = /đánh giá|phân tích|cải thiện|fix|native|agent.*inside|cli.*bên trong|phỏng vấn|discovery/i.test(lower);
102
- if (isNativeMeta) {
103
- return "Native self-assessment of muonroi-cli with concrete improvements identified and implemented";
51
+ /**
52
+ * Get a default outcome label for well-known task types when no outcome
53
+ * was voiced by the user and no card provided one.
54
+ */
55
+ export function getDefaultOutcome(taskType, raw) {
56
+ if (raw) {
57
+ const lower = raw.toLowerCase();
58
+ const isNativeMeta = /đánh giá|phân tích|cải thiện|fix|native|agent.*inside|cli.*bên trong|phỏng vấn|discovery/i.test(lower);
59
+ if (isNativeMeta) {
60
+ return "Native self-assessment of the CLI with specific, actionable improvements";
61
+ }
104
62
  }
105
- return `Complete the task described in: "${raw.slice(0, 80)}"`;
63
+ const map = {
64
+ analyze: "Detailed analysis with concrete improvement recommendations",
65
+ plan: "Step-by-step plan",
66
+ documentation: "Docs updated",
67
+ debug: "Error resolved, expected behavior restored",
68
+ };
69
+ return map[taskType ?? ""] ?? `Complete: ${(raw ?? "").slice(0, 80)}`;
106
70
  }
107
71
  //# sourceMappingURL=layer16-clarity.js.map
@@ -1,4 +1,14 @@
1
- import type { CouncilQuestionData } from "../types/index.js";
1
+ /**
2
+ * src/pil/layer18-acceptance.ts
3
+ *
4
+ * Phase 3 (2026-06-23): acceptance is now model-driven — the model includes
5
+ * acceptance/rejection options in its own ModelCard[]. The CLI no longer
6
+ * builds hardcoded Accept/Adjust/Cancel buttons.
7
+ *
8
+ * What remains here is a simplified summary card builder used by the
9
+ * `showAcceptance` replacement: when feasibility warnings exist, the CLI
10
+ * appends them as context to the model's last card rather than creating a
11
+ * separate acceptance ceremony.
12
+ */
2
13
  import type { AcceptanceCardData, ClarifiedIntent, FeasibilityResult } from "./discovery-types.js";
3
- export declare function buildAcceptanceCard(intentStatement: string, intent: ClarifiedIntent, feasibility: FeasibilityResult, raw?: string): AcceptanceCardData;
4
- export declare function buildAcceptanceQuestion(card: AcceptanceCardData, questionId: string): CouncilQuestionData;
14
+ export declare function buildAcceptanceCard(intentStatement: string, intent: ClarifiedIntent, feasibility: FeasibilityResult, _raw?: string): AcceptanceCardData;