muonroi-cli 1.7.2 → 1.8.1

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 (326) hide show
  1. package/dist/packages/agent-harness-core/src/event-filter.js +1 -0
  2. package/dist/packages/agent-harness-core/src/event-redact.js +8 -0
  3. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +16 -0
  4. package/dist/packages/agent-harness-core/src/mcp-server.js +88 -10
  5. package/dist/packages/agent-harness-core/src/protocol.d.ts +15 -0
  6. package/dist/packages/agent-harness-core/src/selector.js +2 -2
  7. package/dist/packages/agent-harness-core/src/transports/sidechannel.js +1 -1
  8. package/dist/packages/agent-harness-opentui/src/agent-mode.js +1 -1
  9. package/dist/packages/agent-harness-opentui/src/install.js +1 -1
  10. package/dist/src/__test-stubs__/vitest-setup.js +4 -0
  11. package/dist/src/agent-harness/__tests__/mock-model.spec.js +54 -0
  12. package/dist/src/agent-harness/mock-model.d.ts +1 -1
  13. package/dist/src/agent-harness/mock-model.js +143 -6
  14. package/dist/src/agent-harness/test-spawn.js +3 -1
  15. package/dist/src/chat/chat-keychain.js +9 -1
  16. package/dist/src/cli/cost-forensics.js +7 -4
  17. package/dist/src/cli/keys.d.ts +2 -2
  18. package/dist/src/cli/keys.js +107 -28
  19. package/dist/src/cli/usage-report.js +2 -2
  20. package/dist/src/council/__tests__/abort-threading.test.d.ts +1 -0
  21. package/dist/src/council/__tests__/abort-threading.test.js +193 -0
  22. package/dist/src/council/__tests__/clarification-prompt.test.js +21 -1
  23. package/dist/src/council/__tests__/council-turn-length-emit.test.d.ts +1 -0
  24. package/dist/src/council/__tests__/council-turn-length-emit.test.js +130 -0
  25. package/dist/src/council/__tests__/debate-round-budget.test.d.ts +1 -0
  26. package/dist/src/council/__tests__/debate-round-budget.test.js +45 -0
  27. package/dist/src/council/__tests__/evaluator-metrics.test.js +3 -0
  28. package/dist/src/council/__tests__/post-debate-recommendation.test.d.ts +1 -0
  29. package/dist/src/council/__tests__/post-debate-recommendation.test.js +58 -0
  30. package/dist/src/council/__tests__/research-tools.test.js +4 -0
  31. package/dist/src/council/__tests__/round-tools.test.js +6 -0
  32. package/dist/src/council/clarifier-question-cap.test.d.ts +12 -0
  33. package/dist/src/council/clarifier-question-cap.test.js +73 -0
  34. package/dist/src/council/clarifier.d.ts +29 -1
  35. package/dist/src/council/clarifier.js +64 -1
  36. package/dist/src/council/debate-planner.d.ts +2 -1
  37. package/dist/src/council/debate-planner.js +5 -3
  38. package/dist/src/council/debate.d.ts +9 -0
  39. package/dist/src/council/debate.js +88 -8
  40. package/dist/src/council/index.d.ts +46 -1
  41. package/dist/src/council/index.js +125 -19
  42. package/dist/src/council/llm.js +41 -13
  43. package/dist/src/council/prompts.js +36 -1
  44. package/dist/src/council/types.d.ts +14 -1
  45. package/dist/src/ee/__tests__/export-transcripts.test.js +1 -1
  46. package/dist/src/ee/bridge.d.ts +3 -1
  47. package/dist/src/ee/bridge.js +9 -3
  48. package/dist/src/ee/bridge.test.js +1 -2
  49. package/dist/src/ee/client.js +15 -2
  50. package/dist/src/ee/recall-ledger.d.ts +20 -0
  51. package/dist/src/ee/recall-ledger.js +40 -1
  52. package/dist/src/ee/search.d.ts +25 -0
  53. package/dist/src/ee/search.js +73 -0
  54. package/dist/src/ee/types.d.ts +15 -1
  55. package/dist/src/ee/who-am-i.d.ts +41 -0
  56. package/dist/src/ee/who-am-i.js +143 -0
  57. package/dist/src/ee/who-am-i.test.d.ts +1 -0
  58. package/dist/src/ee/who-am-i.test.js +101 -0
  59. package/dist/src/flow/compaction/index.d.ts +1 -0
  60. package/dist/src/flow/compaction/index.js +4 -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 +59 -62
  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 +13 -5
  76. package/dist/src/lsp/manager.test.js +41 -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/auto-setup.js +0 -8
  86. package/dist/src/mcp/client-pool.d.ts +1 -1
  87. package/dist/src/mcp/client-pool.js +25 -9
  88. package/dist/src/mcp/ee-tools.d.ts +10 -0
  89. package/dist/src/mcp/ee-tools.js +34 -0
  90. package/dist/src/mcp/self-verify-jobs.d.ts +1 -0
  91. package/dist/src/mcp/self-verify-jobs.js +1 -3
  92. package/dist/src/mcp/self-verify-runner.js +1 -1
  93. package/dist/src/models/catalog-gemini.test.js +12 -10
  94. package/dist/src/models/catalog.json +106 -38
  95. package/dist/src/orchestrator/__tests__/auto-commit.test.d.ts +1 -0
  96. package/dist/src/orchestrator/__tests__/auto-commit.test.js +142 -0
  97. package/dist/src/orchestrator/__tests__/batch-turn-runner.test.js +3 -0
  98. package/dist/src/orchestrator/__tests__/delegations.test.d.ts +1 -0
  99. package/dist/src/orchestrator/__tests__/delegations.test.js +96 -0
  100. package/dist/src/orchestrator/__tests__/message-processor.test.js +2 -0
  101. package/dist/src/orchestrator/__tests__/prompts.test.d.ts +1 -0
  102. package/dist/src/orchestrator/__tests__/prompts.test.js +88 -0
  103. package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -0
  104. package/dist/src/orchestrator/__tests__/sub-session-delegation.test.d.ts +1 -0
  105. package/dist/src/orchestrator/__tests__/sub-session-delegation.test.js +507 -0
  106. package/dist/src/orchestrator/agent.test.js +5 -2
  107. package/dist/src/orchestrator/auto-commit.d.ts +110 -0
  108. package/dist/src/orchestrator/auto-commit.js +366 -0
  109. package/dist/src/orchestrator/batch-turn-runner.d.ts +3 -2
  110. package/dist/src/orchestrator/batch-turn-runner.js +2 -1
  111. package/dist/src/orchestrator/compaction-proposer-prompt.d.ts +8 -0
  112. package/dist/src/orchestrator/compaction-proposer-prompt.js +43 -0
  113. package/dist/src/orchestrator/compaction.d.ts +22 -0
  114. package/dist/src/orchestrator/compaction.js +42 -0
  115. package/dist/src/orchestrator/delegations.d.ts +1 -0
  116. package/dist/src/orchestrator/delegations.js +41 -0
  117. package/dist/src/orchestrator/error-utils.js +27 -2
  118. package/dist/src/orchestrator/message-processor.d.ts +23 -5
  119. package/dist/src/orchestrator/message-processor.js +226 -2573
  120. package/dist/src/orchestrator/orchestrator.d.ts +11 -2
  121. package/dist/src/orchestrator/orchestrator.js +530 -43
  122. package/dist/src/orchestrator/pending-calls.js +2 -1
  123. package/dist/src/orchestrator/preprocessor.d.ts +13 -0
  124. package/dist/src/orchestrator/preprocessor.js +146 -0
  125. package/dist/src/orchestrator/prompts.d.ts +16 -0
  126. package/dist/src/orchestrator/prompts.js +130 -67
  127. package/dist/src/orchestrator/safety-askcard.d.ts +66 -0
  128. package/dist/src/orchestrator/safety-askcard.js +74 -0
  129. package/dist/src/orchestrator/sandbox.test.js +8 -5
  130. package/dist/src/orchestrator/scope-ceiling.js +1 -1
  131. package/dist/src/orchestrator/stall-watchdog.d.ts +47 -0
  132. package/dist/src/orchestrator/stall-watchdog.js +23 -0
  133. package/dist/src/orchestrator/stall-watchdog.test.js +36 -1
  134. package/dist/src/orchestrator/stream-runner.js +52 -30
  135. package/dist/src/orchestrator/sub-agent-cap.d.ts +2 -0
  136. package/dist/src/orchestrator/sub-agent-cap.js +12 -4
  137. package/dist/src/orchestrator/sub-agent-model-tier.js +1 -1
  138. package/dist/src/orchestrator/subagent-compactor.d.ts +21 -0
  139. package/dist/src/orchestrator/subagent-compactor.js +148 -24
  140. package/dist/src/orchestrator/subagent-compactor.spec.js +249 -1
  141. package/dist/src/orchestrator/text-tool-call-detector.d.ts +9 -5
  142. package/dist/src/orchestrator/text-tool-call-detector.js +16 -12
  143. package/dist/src/orchestrator/text-tool-call-detector.test.js +66 -8
  144. package/dist/src/orchestrator/tool-args-repair.js +1 -1
  145. package/dist/src/orchestrator/tool-engine.d.ts +153 -0
  146. package/dist/src/orchestrator/tool-engine.js +3138 -0
  147. package/dist/src/orchestrator/turn-runner-deps.d.ts +3 -0
  148. package/dist/src/pil/__tests__/discovery-types.test.js +15 -4
  149. package/dist/src/pil/__tests__/discovery.test.js +89 -145
  150. package/dist/src/pil/__tests__/dual-run.test.js +2 -0
  151. package/dist/src/pil/__tests__/layer1-intent-trace.test.js +3 -0
  152. package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
  153. package/dist/src/pil/__tests__/layer16-clarity.test.js +35 -32
  154. package/dist/src/pil/__tests__/layer18-acceptance.test.js +1 -78
  155. package/dist/src/pil/__tests__/layer2_5-ponytail.test.d.ts +1 -0
  156. package/dist/src/pil/__tests__/layer2_5-ponytail.test.js +53 -0
  157. package/dist/src/pil/__tests__/layer3-ee-injection.test.js +67 -4
  158. package/dist/src/pil/__tests__/layer3-injected-chunk.test.js +14 -2
  159. package/dist/src/pil/__tests__/layer4-gsd.test.js +30 -0
  160. package/dist/src/pil/__tests__/layer5-context.test.js +39 -1
  161. package/dist/src/pil/__tests__/layer6-output.test.js +30 -2
  162. package/dist/src/pil/__tests__/llm-classify.test.js +79 -1
  163. package/dist/src/pil/__tests__/orchestrator-integration.test.js +3 -1
  164. package/dist/src/pil/__tests__/pipeline.test.js +11 -8
  165. package/dist/src/pil/__tests__/response-tools.test.js +52 -0
  166. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  167. package/dist/src/pil/agent-operating-contract.js +5 -3
  168. package/dist/src/pil/agent-operating-contract.test.js +2 -2
  169. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  170. package/dist/src/pil/cheap-model-playbook.js +1 -1
  171. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  172. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  173. package/dist/src/pil/config.d.ts +20 -0
  174. package/dist/src/pil/config.js +35 -0
  175. package/dist/src/pil/discovery-types.d.ts +32 -9
  176. package/dist/src/pil/discovery.d.ts +3 -3
  177. package/dist/src/pil/discovery.js +138 -244
  178. package/dist/src/pil/index.d.ts +1 -1
  179. package/dist/src/pil/index.js +1 -1
  180. package/dist/src/pil/layer1-intent.d.ts +9 -1
  181. package/dist/src/pil/layer1-intent.js +62 -7
  182. package/dist/src/pil/layer1-intent.test.js +112 -2
  183. package/dist/src/pil/layer15-context-scan.js +1 -1
  184. package/dist/src/pil/layer16-clarity.d.ts +21 -25
  185. package/dist/src/pil/layer16-clarity.js +52 -88
  186. package/dist/src/pil/layer18-acceptance.d.ts +13 -3
  187. package/dist/src/pil/layer18-acceptance.js +1 -75
  188. package/dist/src/pil/layer2-personality.js +7 -8
  189. package/dist/src/pil/layer2_5-ponytail.d.ts +2 -0
  190. package/dist/src/pil/layer2_5-ponytail.js +41 -0
  191. package/dist/src/pil/layer3-ee-injection.js +180 -14
  192. package/dist/src/pil/layer4-gsd.js +22 -8
  193. package/dist/src/pil/layer5-context.d.ts +1 -0
  194. package/dist/src/pil/layer5-context.js +26 -11
  195. package/dist/src/pil/layer6-output.js +23 -5
  196. package/dist/src/pil/llm-classify.d.ts +11 -0
  197. package/dist/src/pil/llm-classify.js +196 -3
  198. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  199. package/dist/src/pil/native-capabilities-workbook.js +4 -3
  200. package/dist/src/pil/pipeline.js +15 -1
  201. package/dist/src/pil/response-tools.d.ts +16 -0
  202. package/dist/src/pil/response-tools.js +41 -0
  203. package/dist/src/pil/schema.d.ts +8 -0
  204. package/dist/src/pil/schema.js +24 -3
  205. package/dist/src/pil/task-tier-map.js +7 -7
  206. package/dist/src/pil/types.d.ts +9 -3
  207. package/dist/src/product-loop/__tests__/discovery-interview.test.js +103 -1
  208. package/dist/src/product-loop/__tests__/gather-selectable-alts.test.js +34 -0
  209. package/dist/src/product-loop/__tests__/sprint-self-verify.test.js +12 -12
  210. package/dist/src/product-loop/discovery-interview.d.ts +20 -0
  211. package/dist/src/product-loop/discovery-interview.js +121 -7
  212. package/dist/src/product-loop/gather.js +19 -5
  213. package/dist/src/product-loop/loop-driver.js +2 -2
  214. package/dist/src/product-loop/sprint-self-verify.js +2 -2
  215. package/dist/src/providers/__tests__/reasoning-roundtrip.test.js +12 -0
  216. package/dist/src/providers/auth/__tests__/gemini-oauth.test.d.ts +1 -5
  217. package/dist/src/providers/auth/__tests__/gemini-oauth.test.js +1 -5
  218. package/dist/src/providers/auth/gcloud.d.ts +28 -0
  219. package/dist/src/providers/auth/gcloud.js +102 -0
  220. package/dist/src/providers/auth/gemini-oauth.d.ts +46 -19
  221. package/dist/src/providers/auth/gemini-oauth.js +292 -105
  222. package/dist/src/providers/auth/grok-oauth.d.ts +1 -1
  223. package/dist/src/providers/auth/grok-oauth.js +1 -1
  224. package/dist/src/providers/auth/registry.js +28 -6
  225. package/dist/src/providers/gemini.d.ts +1 -1
  226. package/dist/src/providers/gemini.js +10 -3
  227. package/dist/src/providers/keychain.d.ts +2 -2
  228. package/dist/src/providers/keychain.js +20 -2
  229. package/dist/src/providers/openai.js +5 -1
  230. package/dist/src/providers/runtime.d.ts +1 -1
  231. package/dist/src/providers/runtime.js +11 -2
  232. package/dist/src/providers/siliconflow-sse-repair.js +1 -1
  233. package/dist/src/providers/strategies/google.strategy.d.ts +9 -2
  234. package/dist/src/providers/strategies/google.strategy.js +159 -9
  235. package/dist/src/providers/strategies/thinking-mode.js +19 -6
  236. package/dist/src/providers/types.d.ts +1 -1
  237. package/dist/src/router/decide.test.js +40 -8
  238. package/dist/src/router/step-router.d.ts +20 -0
  239. package/dist/src/router/step-router.js +73 -0
  240. package/dist/src/self-qa/agentic-loop.js +30 -21
  241. package/dist/src/self-qa/orchestrator.js +5 -5
  242. package/dist/src/self-qa/scenario-planner.js +1 -1
  243. package/dist/src/self-qa/spec-emitter.js +4 -4
  244. package/dist/src/storage/__tests__/hierarchical-session.test.d.ts +1 -0
  245. package/dist/src/storage/__tests__/hierarchical-session.test.js +161 -0
  246. package/dist/src/storage/__tests__/sweep-stale-pending.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/migrations.js +13 -1
  250. package/dist/src/storage/transcript-response-entry.test.js +18 -5
  251. package/dist/src/storage/transcript-view.js +7 -1
  252. package/dist/src/storage/transcript.d.ts +3 -0
  253. package/dist/src/storage/transcript.js +254 -73
  254. package/dist/src/storage/ui-interaction-log.d.ts +17 -0
  255. package/dist/src/storage/usage.js +22 -5
  256. package/dist/src/tools/__tests__/native-tools.test.js +6 -0
  257. package/dist/src/tools/bash.d.ts +36 -23
  258. package/dist/src/tools/bash.js +59 -301
  259. package/dist/src/tools/bash.test.js +2 -257
  260. package/dist/src/tools/file-tracker.d.ts +5 -0
  261. package/dist/src/tools/file-tracker.js +14 -0
  262. package/dist/src/tools/file.js +2 -0
  263. package/dist/src/tools/git-safety.d.ts +38 -1
  264. package/dist/src/tools/git-safety.js +94 -4
  265. package/dist/src/tools/git-safety.test.js +45 -1
  266. package/dist/src/tools/native-tools.d.ts +1 -1
  267. package/dist/src/tools/native-tools.js +66 -1
  268. package/dist/src/tools/registry-bash-empty-command.test.js +4 -3
  269. package/dist/src/tools/registry-git-safety.test.js +17 -6
  270. package/dist/src/tools/registry.d.ts +6 -0
  271. package/dist/src/tools/registry.js +215 -13
  272. package/dist/src/types/index.d.ts +8 -2
  273. package/dist/src/ui/app.d.ts +1 -0
  274. package/dist/src/ui/app.js +0 -0
  275. package/dist/src/ui/components/council-question-card.js +1 -0
  276. package/dist/src/ui/components/message-view.js +37 -3
  277. package/dist/src/ui/components/structured-response-view.js +7 -0
  278. package/dist/src/ui/components/task-list-panel.js +3 -12
  279. package/dist/src/ui/containers/chat-feed.d.ts +40 -0
  280. package/dist/src/ui/containers/chat-feed.js +66 -0
  281. package/dist/src/ui/containers/modals-layer.d.ts +87 -0
  282. package/dist/src/ui/containers/modals-layer.js +18 -0
  283. package/dist/src/ui/slash/__tests__/menu-parity.test.d.ts +1 -0
  284. package/dist/src/ui/slash/__tests__/menu-parity.test.js +1 -0
  285. package/dist/src/ui/slash/__tests__/ponytail.test.d.ts +1 -0
  286. package/dist/src/ui/slash/__tests__/ponytail.test.js +37 -0
  287. package/dist/src/ui/slash/expand.js +14 -1
  288. package/dist/src/ui/slash/export.js +16 -1
  289. package/dist/src/ui/slash/menu-items.js +1 -0
  290. package/dist/src/ui/slash/ponytail.d.ts +2 -0
  291. package/dist/src/ui/slash/ponytail.js +19 -0
  292. package/dist/src/ui/status-bar/store.js +0 -1
  293. package/dist/src/ui/status-bar/store.test.js +2 -2
  294. package/dist/src/ui/use-app-logic.d.ts +26 -0
  295. package/dist/src/ui/use-app-logic.js +0 -0
  296. package/dist/src/ui/utils/relaunch.js +1 -1
  297. package/dist/src/ui/utils/text.d.ts +11 -0
  298. package/dist/src/ui/utils/text.js +21 -1
  299. package/dist/src/ui/utils/text.test.js +25 -1
  300. package/dist/src/ui/utils/tools.js +3 -1
  301. package/dist/src/usage/cost-log.js +2 -2
  302. package/dist/src/usage/estimator.d.ts +14 -0
  303. package/dist/src/usage/estimator.js +22 -0
  304. package/dist/src/utils/__tests__/compaction-caps.test.d.ts +21 -0
  305. package/dist/src/utils/__tests__/compaction-caps.test.js +187 -0
  306. package/dist/src/utils/__tests__/llm-deadline-abort.test.d.ts +1 -0
  307. package/dist/src/utils/__tests__/llm-deadline-abort.test.js +33 -0
  308. package/dist/src/utils/__tests__/llm-deadline.test.js +16 -4
  309. package/dist/src/utils/__tests__/logger.test.d.ts +1 -0
  310. package/dist/src/utils/__tests__/logger.test.js +115 -0
  311. package/dist/src/utils/__tests__/settings-agent-first.test.d.ts +1 -0
  312. package/dist/src/utils/__tests__/settings-agent-first.test.js +48 -0
  313. package/dist/src/utils/install-manager.d.ts +1 -0
  314. package/dist/src/utils/install-manager.js +76 -7
  315. package/dist/src/utils/install-manager.test.js +43 -1
  316. package/dist/src/utils/llm-deadline.d.ts +9 -1
  317. package/dist/src/utils/llm-deadline.js +31 -2
  318. package/dist/src/utils/logger.d.ts +28 -0
  319. package/dist/src/utils/logger.js +171 -0
  320. package/dist/src/utils/permission-mode.d.ts +33 -0
  321. package/dist/src/utils/permission-mode.js +164 -3
  322. package/dist/src/utils/permission-mode.test.js +92 -1
  323. package/dist/src/utils/settings.d.ts +36 -32
  324. package/dist/src/utils/settings.js +151 -123
  325. package/dist/src/utils/update-checker.test.js +5 -1
  326. 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)
@@ -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;
@@ -1,14 +1,5 @@
1
- export function buildAcceptanceCard(intentStatement, intent, feasibility, raw) {
1
+ export function buildAcceptanceCard(intentStatement, intent, feasibility, _raw) {
2
2
  const warnings = [...feasibility.warnings];
3
- // PIL-L6 fix — detect mismatch between user's raw words and the reframed
4
- // intent statement. If raw says "ci fail / fix" but statement says
5
- // "generate / feature implemented", add a loud warning AND surface a hint
6
- // in the card so the user notices before accepting.
7
- if (raw) {
8
- const mismatch = detectIntentMismatch(raw, intentStatement);
9
- if (mismatch)
10
- warnings.unshift(mismatch);
11
- }
12
3
  return {
13
4
  intentStatement,
14
5
  outcome: intent.outcome,
@@ -16,69 +7,4 @@ export function buildAcceptanceCard(intentStatement, intent, feasibility, raw) {
16
7
  warnings,
17
8
  };
18
9
  }
19
- /**
20
- * PIL-L6 fix — heuristic mismatch detector. Returns a human-readable warning
21
- * string when the reframed intent looks wrong relative to the raw prompt.
22
- * Returns null when no mismatch is detected. Cheap regex-based — no LLM.
23
- */
24
- function detectIntentMismatch(raw, intentStatement) {
25
- const r = raw.toLowerCase();
26
- const s = intentStatement.toLowerCase();
27
- // Debug signals in raw vs. non-debug intent
28
- const debugSignals = /\b(fail(?:s|ed|ing)?|error|exception|crash|broken|bug|fix\s+(?:the\s+)?(?:ci|build|test|action|workflow|deploy)|sửa\s+lỗi|lỗi|hỏng)\b/i;
29
- if (debugSignals.test(raw) && !/^debug:/.test(s) && !/(fix|debug|repair|resolve|error|fail)/.test(s)) {
30
- return `Detected debug/bug-fix signals in your prompt ("${matchedWord(r, debugSignals)}") but intent reframed as "${intentStatement}". Verify before accepting.`;
31
- }
32
- return null;
33
- }
34
- function matchedWord(haystack, re) {
35
- const m = re.exec(haystack);
36
- return m?.[0] ?? "";
37
- }
38
- /**
39
- * Classify a card warning as blocking (should force Adjust default) or
40
- * informational (Accept default is fine). Intent-mismatch detector output
41
- * is informational — the card body already surfaces it so the user can see
42
- * the hint without being routed back into another interview round.
43
- */
44
- function isBlockingWarning(warning) {
45
- const w = warning.toLowerCase();
46
- if (w.includes("detected") && (w.includes("signals") || w.includes("verify before accepting"))) {
47
- return false;
48
- }
49
- return true;
50
- }
51
- export function buildAcceptanceQuestion(card, questionId) {
52
- const contextLines = [];
53
- contextLines.push(`Outcome: ${card.outcome}`);
54
- contextLines.push(`Scope: ${card.scope.join(", ")}`);
55
- if (card.warnings.length > 0) {
56
- contextLines.push(`⚠ ${card.warnings.join("; ")}`);
57
- }
58
- const options = [
59
- { label: "Accept", value: "accept", kind: "choice", description: "Proceed with this understanding" },
60
- { label: "Adjust", value: "adjust", kind: "choice", description: "Let me clarify further" },
61
- { label: "Cancel", value: "cancel", kind: "choice", description: "Never mind" },
62
- ];
63
- // Default-index policy:
64
- // - No warnings → Accept (0).
65
- // - Any blocking warning (feasibility text like "infeasible", "blocked by",
66
- // "cannot") → Adjust (1).
67
- // - Only informational warnings (intent-mismatch detector output —
68
- // "Detected ... signals" / "Verify before accepting") → Accept (0).
69
- // Evidence (session 1f29e238a816): intent-mismatch detector fired on a
70
- // 4-word follow-up ("Can you fix it?"), forcing Adjust default and a
71
- // PIL chain that burned 35s on questions the user had already answered.
72
- const hasBlockingWarning = card.warnings.some((w) => isBlockingWarning(w));
73
- const defaultIndex = hasBlockingWarning ? 1 : 0;
74
- return {
75
- questionId,
76
- question: `I understand you want to: ${card.intentStatement}`,
77
- context: contextLines.join("\n"),
78
- isRequired: true,
79
- phase: "pil-acceptance",
80
- options,
81
- defaultIndex,
82
- };
83
- }
84
10
  //# sourceMappingURL=layer18-acceptance.js.map
@@ -1,12 +1,11 @@
1
1
  import { truncateToBudget } from "./budget.js";
2
- // TODO(WhoAmI-L2): when EE v4.0 Who Am I profile is available, replace
3
- // static PERSONALITY_HINTS with a dynamically-built hint from the user profile:
4
- // communication.brevity → concise/detailed bias
5
- // decision_speed → skip exhaustive options, recommend once
6
- // feedback_style → implicit = don't re-explain on "ok"
7
- // work_patterns.delegation → autonomous = lead with action, not question
8
- // ctx.outputStyle from L1 becomes an override signal, not the primary source.
9
- // Until WhoAmI ships, L2 relies entirely on L1's per-turn detection.
2
+ // WhoAmI v4.0 (partly wired): ctx.outputStyle now carries the profile baseline
3
+ // from communication.brevity / decision_speed (resolved in layer1-intent.ts via
4
+ // ../ee/who-am-i.ts), so the concise/detailed bias below is already profile-aware
5
+ // without changing this layer — L1's per-turn detection overrides it. Still future:
6
+ // a richer hint built directly from feedback_style (EE emits implicit |
7
+ // precise-correction). NOTE: `work_patterns.delegation_style` is NOT emitted by EE
8
+ // slice-1 do not wire it until a later EE slice adds it.
10
9
  const DEFAULT_PERSONALITY = "balanced";
11
10
  const PERSONALITY_HINTS = {
12
11
  concise: "[personality: concise — Be direct and terse. Lead with the answer. Skip preamble. " +
@@ -0,0 +1,2 @@
1
+ import type { PipelineContext } from "./types.js";
2
+ export declare function layer2_5Ponytail(ctx: PipelineContext): Promise<PipelineContext>;