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
@@ -0,0 +1,41 @@
1
+ import { truncateToBudget } from "./budget.js";
2
+ import { isPonytailModeEnabled } from "./config.js";
3
+ const PONYTAIL_INSTRUCTION = `[LAZY SENIOR / PONYTAIL MODE ACTIVE]
4
+ "The best code is the code you never wrote." You MUST adhere to the following Decision Ladder:
5
+ 1. Standard Library first.
6
+ 2. Native platform features next.
7
+ 3. 1-line solution next.
8
+ 4. YAGNI: Do not write it if not strictly necessary.
9
+ 5. Smallest correct change.
10
+
11
+ DOCUMENTATION RULE: If you bypass a complex/scalable pattern in favor of a simple one to adhere to this rule, you MUST add a comment in the code starting exactly with "// Intentional simplification: [Reason]".`;
12
+ export async function layer2_5Ponytail(ctx) {
13
+ // If task is not coding/action-related (e.g., chitchat), we might not need it, but pipeline.ts already skips layers if taskType is null.
14
+ if (!isPonytailModeEnabled()) {
15
+ return {
16
+ ...ctx,
17
+ layers: [
18
+ ...ctx.layers,
19
+ {
20
+ name: "ponytail-mode",
21
+ applied: false,
22
+ delta: "skipped:disabled-by-config",
23
+ },
24
+ ],
25
+ };
26
+ }
27
+ const trimmed = truncateToBudget(PONYTAIL_INSTRUCTION, Math.floor(ctx.tokenBudget * 0.15));
28
+ return {
29
+ ...ctx,
30
+ enriched: `${ctx.enriched}\n\n${trimmed}`,
31
+ layers: [
32
+ ...ctx.layers,
33
+ {
34
+ name: "ponytail-mode",
35
+ applied: true,
36
+ delta: `chars=${trimmed.length}`,
37
+ },
38
+ ],
39
+ };
40
+ }
41
+ //# sourceMappingURL=layer2_5-ponytail.js.map
@@ -17,10 +17,18 @@
17
17
  import { createHash } from "node:crypto";
18
18
  import { searchByText } from "../ee/bridge.js";
19
19
  import { updateLastSurfacedState } from "../ee/intercept.js";
20
+ import { formatPendingReminder, isRecallLedgerEnabled, sessionRecallLedger } from "../ee/recall-ledger.js";
20
21
  import { getRenderSink } from "../ee/render.js";
21
22
  import { logInteraction } from "../storage/interaction-log.js";
22
23
  import { classifyEeError, logEeFailure, readTimeoutEnv } from "../utils/ee-logger.js";
23
24
  import { truncateToBudget } from "./budget.js";
25
+ /**
26
+ * Session-scoped cross-turn dedup: records which pending recall IDs have
27
+ * already been surfaced via the feedback nudge in a previous turn. Prevents
28
+ * the same unrated [id] list from consuming input tokens turn after turn.
29
+ * Cleared on session reset (process exit or ledger.reset()).
30
+ */
31
+ const _surfacedPendingIds = new Map();
24
32
  // Budget for the HTTP/in-process search round-trip. 60ms (legacy) was tuned for
25
33
  // localhost Ollama and routinely tripped the abort on VPS thin-client setups
26
34
  // where embedding goes through SiliconFlow.
@@ -117,8 +125,13 @@ function isT1Proven(p) {
117
125
  return false;
118
126
  }
119
127
  }
120
- async function queryEeBridge(raw) {
128
+ async function queryEeBridge(raw, taskType) {
121
129
  try {
130
+ // Enrich query with task context so EE embedding is more precise.
131
+ // e.g. "analyze: vậy ở...fix?" vs generic "vậy ở...fix?" — the prefix
132
+ // shifts the embedding toward task-relevant entries and away from generic
133
+ // action-oriented behavioral patterns that score highly on any NL query.
134
+ const queryWithTask = taskType ? `[${taskType}] ${raw}` : raw;
122
135
  // Parallel queries: T0 principles (lower floor, pre-validated abstractions)
123
136
  // and T1/T2 behavioral (standard floor, contextual patterns). Running both
124
137
  // concurrently keeps total latency at ~1500ms rather than ~3000ms.
@@ -126,8 +139,8 @@ async function queryEeBridge(raw) {
126
139
  // prior "Progress ✔ DONE / elided" without the agent having to ask "task finished?".
127
140
  const signal = AbortSignal.timeout(PIL_SEARCH_TIMEOUT_MS);
128
141
  const [principleRaw, behavioralRaw, checkpointRaw] = await Promise.all([
129
- searchByText(raw, ["experience-principles"], 3, signal),
130
- searchByText(raw, ["experience-behavioral"], 4, signal),
142
+ searchByText(queryWithTask, ["experience-principles"], 3, signal),
143
+ searchByText(queryWithTask, ["experience-behavioral"], 4, signal),
131
144
  searchByText('Context checkpoint summary OR "compaction checkpoint" recent Progress DONE elided OR tool-artifact OR "tool result id="', ["experience-behavioral"], 3, signal).catch(() => []),
132
145
  ]);
133
146
  const principlePoints = principleRaw.filter((p) => (p.score ?? 0) >= PIL_PRINCIPLES_FLOOR);
@@ -192,23 +205,113 @@ export async function layer3EeInjection(ctx) {
192
205
  const behavioralBudget = Math.floor(ctx.tokenBudget * 0.15);
193
206
  const parts = [];
194
207
  const deltas = [];
195
- if (ctx._brainData.t0_principles.length > 0) {
196
- const lines = ctx._brainData.t0_principles.map((p) => `- ${p.text.slice(0, 120)}`);
208
+ // Collection fallback by array position mirrors the legacy path's arm→collection
209
+ // mapping (t0 = principles, t2 = behavioral). The server (PIL schema_version
210
+ // 1.1+) may override per-point — e.g. a selfqa hit merged into the behavioral
211
+ // bucket carries collection="experience-selfqa" so ee_feedback resolves it.
212
+ let principleItems = ctx._brainData.t0_principles.map((p) => ({
213
+ ...p,
214
+ collection: p.collection ?? "experience-principles",
215
+ }));
216
+ let behavioralItems = ctx._brainData.t2_patterns.map((p) => ({
217
+ ...p,
218
+ collection: p.collection ?? "experience-behavioral",
219
+ }));
220
+ // Suppress already-fedback entries so they don't re-inject as noise.
221
+ if (isRecallLedgerEnabled()) {
222
+ principleItems = principleItems.filter((p) => !sessionRecallLedger.wasCleared(String(p.id)));
223
+ behavioralItems = behavioralItems.filter((p) => !sessionRecallLedger.wasCleared(String(p.id)));
224
+ }
225
+ // Render the [id:..] handle inline (mirrors formatPrincipleRules/Hints) so the
226
+ // [id collection] reminder below refers to handles the agent can actually see.
227
+ const renderLine = (p) => p.id ? `- ${p.text.slice(0, 120)} [id:${p.id}]` : `- ${p.text.slice(0, 120)}`;
228
+ if (principleItems.length > 0) {
229
+ const lines = principleItems.map(renderLine);
197
230
  const block = truncateToBudget(`[principles: Generalized principles from past work]\n${lines.join("\n")}`, principlesBudget);
198
231
  parts.push(block);
199
- deltas.push(`principles=${ctx._brainData.t0_principles.length}`);
232
+ deltas.push(`principles=${principleItems.length}`);
200
233
  }
201
- if (ctx._brainData.t2_patterns.length > 0) {
202
- const lines = ctx._brainData.t2_patterns.map((p) => `- ${p.text.slice(0, 120)}`);
234
+ if (behavioralItems.length > 0) {
235
+ const lines = behavioralItems.map(renderLine);
203
236
  const block = truncateToBudget(`[experience: Relevant patterns from past work]\n${lines.join("\n")}`, behavioralBudget);
204
237
  parts.push(block);
205
- deltas.push(`behavioral=${ctx._brainData.t2_patterns.length}`);
238
+ deltas.push(`behavioral=${behavioralItems.length}`);
206
239
  }
207
240
  deltas.push(`t1=${ctx._brainData.t1_rules.length}`);
208
241
  deltas.push(`src=unified`);
242
+ // Close BOTH arms of the recall loop on the unified path, symmetric with the
243
+ // legacy path below (previously this formatter rendered text only — invisible
244
+ // to both arms). NEGATIVE arm: register surfaced ids for prompt-stale decay.
245
+ // POSITIVE arm: record the rateable points (those carrying an id from
246
+ // schema_version 1.1+) into the SAME session ledger the native ee_feedback
247
+ // builtin clears, then surface a dynamic [id collection] reminder so an
248
+ // explicit ee_feedback(followed) can credit the injection. No auto verdict is
249
+ // emitted (that would pollute Gate-4 precision). Points without an id (older
250
+ // server) fall through to the static nudge — rendered, but unrateable.
251
+ const ledgerEnabled = isRecallLedgerEnabled();
252
+ let ledgerRecorded = 0;
253
+ const rateable = [...principleItems, ...behavioralItems].filter((p) => p.id);
254
+ if (rateable.length > 0) {
255
+ updateLastSurfacedState(rateable.map((p) => String(p.id)));
256
+ if (ledgerEnabled) {
257
+ sessionRecallLedger.record(rateable.map((p) => ({ id: String(p.id), collection: p.collection })), `passive-injection (unified): ${ctx.raw.slice(0, 80)}`);
258
+ ledgerRecorded = rateable.length;
259
+ }
260
+ }
261
+ if (parts.length > 0 && principleItems.length + behavioralItems.length > 0) {
262
+ const pending = ledgerEnabled ? sessionRecallLedger.pending() : [];
263
+ if (pending.length > 0) {
264
+ // Cross-turn dedup: only surface pending IDs NOT already shown in a prior
265
+ // turn. Once the agent has seen the [id collection] handle, re-listing it
266
+ // every turn is pure input noise.
267
+ const sid = ctx.sessionId ?? "_anon";
268
+ let surfaced = _surfacedPendingIds.get(sid);
269
+ if (!surfaced) {
270
+ surfaced = new Set();
271
+ _surfacedPendingIds.set(sid, surfaced);
272
+ }
273
+ const newPending = pending.filter((p) => !surfaced.has(p.id));
274
+ if (newPending.length > 0) {
275
+ for (const p of newPending)
276
+ surfaced.add(p.id);
277
+ parts.push(formatPendingReminder(newPending, { max: 5 }));
278
+ }
279
+ // else: all pending IDs were already surfaced — skip the nudge.
280
+ }
281
+ else {
282
+ parts.push(RECALL_FEEDBACK_NUDGE);
283
+ }
284
+ }
285
+ const injected = parts.join("\n");
286
+ try {
287
+ if (ctx.sessionId && parts.length > 0) {
288
+ logInteraction(ctx.sessionId, "ee_injection", {
289
+ eventSubtype: "injected",
290
+ data: {
291
+ phase: "pil_enrichment",
292
+ role: "knowledge_retriever",
293
+ source: "unified",
294
+ principleCount: principleItems.length,
295
+ behavioralCount: behavioralItems.length,
296
+ t1RuleCount: ctx._brainData.t1_rules.length,
297
+ pointIds: rateable.map((p) => String(p.id)),
298
+ injectedChars: injected.length,
299
+ // Recall-loop closure observability (harness verification reads these):
300
+ // rateable points recorded as pending debt this turn + total unrated.
301
+ ledgerRecorded,
302
+ ledgerPending: sessionRecallLedger.pendingCount(),
303
+ taskType: ctx.taskType ?? null,
304
+ domain: ctx.domain ?? null,
305
+ },
306
+ });
307
+ }
308
+ }
309
+ catch {
310
+ /* fail-open — never break injection path */
311
+ }
209
312
  return {
210
313
  ...ctx,
211
- enriched: parts.length > 0 ? `${ctx.enriched}\n${parts.join("\n")}` : ctx.enriched,
314
+ enriched: parts.length > 0 ? `${ctx.enriched}\n${injected}` : ctx.enriched,
212
315
  t1Rules: ctx._brainData.t1_rules,
213
316
  layers: [
214
317
  ...ctx.layers,
@@ -221,7 +324,7 @@ export async function layer3EeInjection(ctx) {
221
324
  };
222
325
  }
223
326
  // Legacy path: existing logic continues below — unchanged.
224
- const result = await queryEeBridge(ctx.raw);
327
+ const result = await queryEeBridge(ctx.raw, ctx.taskType);
225
328
  const { principlePoints, behavioralPoints, t1Rules } = result;
226
329
  const totalPoints = principlePoints.length + behavioralPoints.length;
227
330
  if (result.error) {
@@ -294,9 +397,39 @@ export async function layer3EeInjection(ctx) {
294
397
  return text.length === 0 || !checkpointMarkerShas.has(payloadSha16(text));
295
398
  })
296
399
  : result.checkpointPoints || [];
297
- const allPoints = [...deduplicatedPrinciples, ...deduplicatedBehavioral, ...deduplicatedCheckpoints];
400
+ // Feedback-cleared suppression: skip points that were already rated via ee_feedback in an earlier turn.
401
+ // This prevents already-judged entries from being re-injected as noise.
402
+ const ledgerEnabled = isRecallLedgerEnabled();
403
+ const clearedFilteredPrinciples = deduplicatedPrinciples.filter((p) => !(ledgerEnabled && sessionRecallLedger.wasCleared(String(p.id))));
404
+ const clearedFilteredBehavioral = deduplicatedBehavioral.filter((p) => !(ledgerEnabled && sessionRecallLedger.wasCleared(String(p.id))));
405
+ const clearedFilteredCheckpoints = deduplicatedCheckpoints.filter((p) => !(ledgerEnabled && sessionRecallLedger.wasCleared(String(p.id))));
406
+ const allPoints = [...clearedFilteredPrinciples, ...clearedFilteredBehavioral, ...clearedFilteredCheckpoints];
298
407
  // STALE-01: Register injected point IDs for prompt-stale reconciliation.
299
408
  updateLastSurfacedState(allPoints.map((p) => String(p.id)));
409
+ // Close the POSITIVE arm of the recall loop in-process. Record the RATEABLE
410
+ // injected points (principles + behavioral; checkpoints are artifacts, not
411
+ // recall verdicts) into the SAME session ledger the native ee_feedback builtin
412
+ // clears. Before this, the ledger was only ever populated by the external MCP
413
+ // ee.query, so in-CLI ee_feedback.clear() was a guaranteed no-op and nothing
414
+ // surfaced pending debt to the agent — leaving passive injections with an
415
+ // automatic NEGATIVE signal (prompt-stale decay every turn) but no positive
416
+ // one. Recording here makes a passive injection real, rateable debt that the
417
+ // dynamic reminder below names and an explicit ee_feedback(followed) resolves —
418
+ // the signal the brain needs to learn which injections were gold. No auto
419
+ // verdict is emitted (that would pollute Gate-4 precision); the agent rates
420
+ // deliberately. Collection is the search arm (deterministic), which is what
421
+ // ee_feedback requires.
422
+ let ledgerRecorded = 0;
423
+ if (ledgerEnabled) {
424
+ const rateableEntries = [
425
+ ...deduplicatedPrinciples.map((p) => ({ id: String(p.id), collection: "experience-principles" })),
426
+ ...deduplicatedBehavioral.map((p) => ({ id: String(p.id), collection: "experience-behavioral" })),
427
+ ].filter((e) => e.id);
428
+ if (rateableEntries.length > 0) {
429
+ sessionRecallLedger.record(rateableEntries, `passive-injection: ${ctx.raw.slice(0, 80)}`);
430
+ ledgerRecorded = rateableEntries.length;
431
+ }
432
+ }
300
433
  // CQ-16b: Emit experience_injected StreamChunk so TUI can show collapsible block.
301
434
  // Carry per-point {id, title, tier} so the TUI can show WHAT was injected, not
302
435
  // just how many (the data already exists here; previously only the count + ids
@@ -341,7 +474,7 @@ export async function layer3EeInjection(ctx) {
341
474
  if (cpText) {
342
475
  const marker = `<!-- ee-checkpoint-injected:${payloadSha16(cpText)} -->`;
343
476
  // Idea 5: raised from 0.08 to 0.12 for higher fidelity on critical progress + artifact refs.
344
- parts.push(truncateToBudget(cpText + "\n" + marker, Math.floor(ctx.tokenBudget * 0.12)));
477
+ parts.push(truncateToBudget(`${cpText}\n${marker}`, Math.floor(ctx.tokenBudget * 0.12)));
345
478
  }
346
479
  // Close the recall feedback loop at the injection site: passively-injected
347
480
  // experience (the agent did not ee_query for it) otherwise carries no feedback
@@ -350,8 +483,36 @@ export async function layer3EeInjection(ctx) {
350
483
  // on long sessions; this nudge rides next to the [id:..] handles it refers to.
351
484
  // Gated on rateable experience (principles/behavioral) — checkpoints are task
352
485
  // artifacts, not recall verdicts.
486
+ // Dynamic, token-bounded reminder of accumulated unrated debt (this turn's
487
+ // fresh injections + any earlier still-unrated ones), naming the actual
488
+ // [id collection] so ee_feedback is actionable — the static nudge named no ids,
489
+ // so the model could not complete a rating even when willing. Falls back to the
490
+ // static nudge when the ledger is disabled. Gated on rateable experience.
491
+ // Cross-turn dedup: only surface pending IDs NOT already shown in a prior
492
+ // turn. Once surfaced, the agent has seen the [id collection] handle and can
493
+ // act on it (ee_feedback) or ignore it — re-listing the same IDs every turn
494
+ // is pure input noise with no new signal. If nothing is new, skip the nudge.
353
495
  if (deduplicatedPrinciples.length + deduplicatedBehavioral.length > 0) {
354
- parts.push(RECALL_FEEDBACK_NUDGE);
496
+ const pending = ledgerEnabled ? sessionRecallLedger.pending() : [];
497
+ if (pending.length > 0) {
498
+ const sid = ctx.sessionId ?? "_anon";
499
+ let surfaced = _surfacedPendingIds.get(sid);
500
+ if (!surfaced) {
501
+ surfaced = new Set();
502
+ _surfacedPendingIds.set(sid, surfaced);
503
+ }
504
+ const newPending = pending.filter((p) => !surfaced.has(p.id));
505
+ if (newPending.length > 0) {
506
+ for (const p of newPending)
507
+ surfaced.add(p.id);
508
+ parts.push(formatPendingReminder(newPending, { max: 5 }));
509
+ }
510
+ // else: all pending IDs were already surfaced — skip the nudge entirely.
511
+ }
512
+ else {
513
+ // No pending debt at all; fall back to the generic static nudge.
514
+ parts.push(RECALL_FEEDBACK_NUDGE);
515
+ }
355
516
  }
356
517
  const injected = parts.join("\n");
357
518
  try {
@@ -367,6 +528,11 @@ export async function layer3EeInjection(ctx) {
367
528
  t1RuleCount: t1Rules.length,
368
529
  pointIds: allPoints.map((p) => String(p.id)),
369
530
  injectedChars: injected.length,
531
+ // Recall-loop closure observability (harness verification reads these
532
+ // from the interaction log): how many rateable points were recorded as
533
+ // pending debt this turn, and the total still-unrated debt afterwards.
534
+ ledgerRecorded,
535
+ ledgerPending: sessionRecallLedger.pendingCount(),
370
536
  taskType: ctx.taskType ?? null,
371
537
  domain: ctx.domain ?? null,
372
538
  },
@@ -16,7 +16,7 @@
16
16
  * (questions surfaced via AskUserQuestion) is localised — at render time, by
17
17
  * the agent, into the user's language.
18
18
  */
19
- import { routeTask } from "../ee/bridge.js";
19
+ import { getWhoAmIProfile, routeTask } from "../ee/bridge.js";
20
20
  import { buildDirective } from "../playbook/directives.js";
21
21
  import { classifyEeError, logEeFailure } from "../utils/ee-logger.js";
22
22
  import { truncateToBudget } from "./budget.js";
@@ -43,13 +43,16 @@ const DIRECTIVE_BUDGET_FRACTION = 0.25;
43
43
  // value that fits it whole (truncateToBudget multiplies by CHARS_PER_TOKEN=4 →
44
44
  // 700 tokens ≈ 2.8K chars). The fraction still wins when tokenBudget is large.
45
45
  const DIRECTIVE_MIN_TOKENS = 700;
46
- // TODO(WhoAmI-L4): when EE v4.0 Who Am I profile is available:
47
- // - work_patterns.delegation_style="autonomous" bias routeTask toward "direct",
48
- // skip qc-flow discussion phase for familiar task types
49
- // - decision_speed="fast-intuitive"trim heavy-tier directive text,
50
- // remove "consider alternatives" section
51
- // - Cache routeTask result per (taskType, domain) in the profile so the
52
- // EE brain round-trip is skipped for patterns the user has confirmed before
46
+ // WhoAmI v4.0 (wired): work_patterns.delegation_style="autonomous" biases the route
47
+ // toward "direct" a resolved 'discuss' phase is dropped so the agent isn't funnelled
48
+ // into a clarify round the user didn't want (see the phase-resolution block below).
49
+ // Fail-open: absent dim no change; only 'discuss' is affected (execute is untouched —
50
+ // autonomy is about skipping clarification, not skipping execution).
51
+ // TODO(WhoAmI-L4): still deferred decision_speed="fast-intuitive" trim heavy-tier
52
+ // directive: decision_speed IS available via getWhoAmIProfile, but trimming the HEAVY
53
+ // directive is risky (see DIRECTIVE_MIN_TOKENS below — past truncation dropped the
54
+ // rubric), so it stays manual pending a measured approach. Also: cache routeTask result
55
+ // per (taskType, domain) — unblocked by the provider, future.
53
56
  export async function layer4Gsd(ctx) {
54
57
  // Short-circuit: chitchat / pure-greeting inputs (detected by layer1) should
55
58
  // NOT be wrapped in a GSD directive. Injecting "STANDARD task — apply
@@ -86,6 +89,17 @@ export async function layer4Gsd(ctx) {
86
89
  // the directive reads cleanly without a phase hint.
87
90
  routeSource = "none";
88
91
  }
92
+ else if (phase === "discuss") {
93
+ // WhoAmI v4.0: an autonomous delegator ("cứ làm / just do it / don't ask") does
94
+ // not want to be funnelled into a clarify-first 'discuss' round. Drop the hint so
95
+ // the directive reads neutrally (bias toward the 'direct' path). getWhoAmIProfile
96
+ // is cached + fail-open → absent dim leaves the discuss phase exactly as-is.
97
+ const delegation = getWhoAmIProfile()?.dims["work_patterns.delegation_style"]?.value;
98
+ if (delegation === "autonomous") {
99
+ phase = null;
100
+ routeSource = `${routeSource}+autonomous:skip-discuss`;
101
+ }
102
+ }
89
103
  // Work depth is decided by the model in layer1's classify call (the 5th
90
104
  // word → ctx.modelDepthTier). The regex `scoreComplexity` scorer has been
91
105
  // removed from this decision path: depth must reflect what the task actually
@@ -1,2 +1,3 @@
1
1
  import type { PipelineContext } from "./types.js";
2
+ export declare function staleThresholdMsForSessionLength(value: string | undefined | null): number;
2
3
  export declare function layer5Context(ctx: PipelineContext): Promise<PipelineContext>;
@@ -1,15 +1,26 @@
1
1
  import { promises as fs } from "node:fs";
2
2
  import * as path from "node:path";
3
- import { searchByText } from "../ee/bridge.js";
3
+ import { getWhoAmIProfile, searchByText } from "../ee/bridge.js";
4
4
  import { truncateToBudget } from "./budget.js";
5
- // TODO(WhoAmI-L5): when EE v4.0 Who Am I profile is available, replace the
6
- // hardcoded threshold with profile-derived value:
7
- // work_patterns.session_length="medium-long (1-3h)" 120 min stale threshold
8
- // work_patterns.multitasking="sequential-deep" → extend to 2h
9
- // (task-switchers with short sessions keep 15 min)
10
- // Also: deduplicate with L3 — if L3 already injected experience-principles,
11
- // skip that collection here to avoid double-injection.
5
+ // WhoAmI v4.0 (wired): `work_patterns.session_length` (short|medium|long), reachable
6
+ // via ../ee/bridge.js who-am-i.ts, tunes the resume-digest staleness window below.
7
+ // TODO(WhoAmI-L5): `work_patterns.multitasking` (sequential-deep | task-switcher) IS
8
+ // also emitted and could tune recall freshness similarly — deferred until measured.
9
+ // Also: deduplicate with L3 if L3 already injected experience-principles, skip that
10
+ // collection here to avoid double-injection.
12
11
  const STALE_THRESHOLD_MS = 30 * 60 * 1000;
12
+ // session_length → resume-digest staleness window. Long deep-work sessions keep a
13
+ // resume digest relevant longer (relax to 60m); short bursts go stale sooner (tighten
14
+ // to 15m). medium / absent → the 30m default, so a machine with no committed
15
+ // session_length dim (the common case — needs N>=10 sessions) behaves byte-identically
16
+ // to pre-v4.0: zero regression. Pure + exported for deterministic unit testing.
17
+ export function staleThresholdMsForSessionLength(value) {
18
+ if (value === "long")
19
+ return 60 * 60 * 1000;
20
+ if (value === "short")
21
+ return 15 * 60 * 1000;
22
+ return STALE_THRESHOLD_MS;
23
+ }
13
24
  // Single round-trip for both collections via /api/search; topK=5 yields
14
25
  // up to 2 principles + 3 behavioral after server-side scoring.
15
26
  const LAYER5_SEARCH_TIMEOUT_MS = 1500;
@@ -105,15 +116,19 @@ export async function layer5Context(ctx) {
105
116
  else {
106
117
  deltaSegments.push("principles=skipped-l1-unified");
107
118
  }
108
- // 2. Resume digest (5% budget)
119
+ // 2. Resume digest (5% budget). Staleness window is profile-tuned by session_length
120
+ // (fail-open: absent dim → 30m default, identical to pre-v4.0). getWhoAmIProfile
121
+ // is cached + fail-open, so this never adds latency or a failure path here.
109
122
  const digest = ctx.resumeDigest;
110
123
  if (digest?.trim()) {
111
- const isStale = typeof ctx.digestAgeMs === "number" && ctx.digestAgeMs > STALE_THRESHOLD_MS;
124
+ const staleMs = staleThresholdMsForSessionLength(getWhoAmIProfile()?.dims["work_patterns.session_length"]?.value);
125
+ const isStale = typeof ctx.digestAgeMs === "number" && ctx.digestAgeMs > staleMs;
112
126
  const stalePrefix = isStale ? "(stale — verify before relying)\n" : "";
113
127
  const hint = `[flow-context: Resume]\n${stalePrefix}${digest.trim()}`;
114
128
  const trimmed = truncateToBudget(hint, Math.floor(ctx.tokenBudget * 0.05));
115
129
  parts.push(trimmed);
116
- deltaSegments.push(`digest=${trimmed.length}ch${isStale ? ",stale" : ""}`);
130
+ const windowTag = staleMs !== STALE_THRESHOLD_MS ? `,window=${staleMs / 60000}m` : "";
131
+ deltaSegments.push(`digest=${trimmed.length}ch${isStale ? ",stale" : ""}${windowTag}`);
117
132
  }
118
133
  // 3. Flow state (5% budget)
119
134
  const flowBudget = Math.floor(ctx.tokenBudget * 0.05);
@@ -12,7 +12,7 @@
12
12
  * Resolved style is propagated back onto ctx.outputStyle so the orchestrator
13
13
  * picks it up via applyPilSuffix without falling back to the hard "concise" default.
14
14
  */
15
- import { classifyViaBrain } from "../ee/bridge.js";
15
+ import { classifyViaBrain, getWhoAmIProfile } from "../ee/bridge.js";
16
16
  import { getProviderCapabilities } from "../providers/capabilities.js";
17
17
  import { buildResponseTools } from "./response-tools.js";
18
18
  const VALID_STYLES = ["concise", "balanced", "detailed"];
@@ -120,6 +120,12 @@ const NO_PREAMBLE_RULE = `\nFORBIDDEN OPENERS: do not start with "I'll", "I will
120
120
  // noise. Applied only to non-question turns — question turns already get the same
121
121
  // guidance from the Layer 4 QUESTION directive (buildQuestion).
122
122
  const NO_BOOKKEEPING_NOTE = `\nWRITE FOR THE READER: the answer is for the human who asked. Do NOT append a provenance / compliance footer (e.g. "evidence only from this turn", "did not infer unopened files", token-budget notes) and do NOT restate internal rule / contract / layer / tool names as compliance — those are your operating rules, invisible to the reader. End on the answer's last substantive point.`;
123
+ // WhoAmI v4.0: a precise-correction feedback style means the user corrects with
124
+ // surgical edits, not rewrites. Bias code-producing tasks toward an AUDITABLE
125
+ // presentation so a follow-up correction can target one line. This is orthogonal to
126
+ // brevity (answer length) and delegation_style (clarify rounds) — it shapes the FORMAT
127
+ // of changes, not their length or whether to ask first.
128
+ const CORRECTION_AFFORDANCE_RULE = `\nAUDITABLE CHANGES: show exactly what changed — a unified diff or before/after for each edit, file:line anchored — and keep edits small and individually reviewable so a precise correction can target a single change.`;
123
129
  const SUFFIXES = {
124
130
  refactor: {
125
131
  concise: `\nOUTPUT RULES (refactor): Show only changed code. Prefer unified diff or replacement function. No prose unless architecture changes. One sentence max if explanation needed. No preamble.`,
@@ -164,10 +170,13 @@ const SUFFIXES = {
164
170
  detailed: `\nAnswer thoroughly. Use rich markdown structure (headings, lists, bold text, code blocks) to organize complex information so it is easy to scan and read.`,
165
171
  },
166
172
  };
167
- // TODO(WhoAmI-L6): when EE v4.0 Who Am I profile is available, skip
168
- // NO_PREAMBLE_RULE for users with feedback_style="explicit" who prefer
169
- // preamble. Also source TASK_TYPE_DEFAULT_STYLE from profile communication.brevity
170
- // instead of the hardcoded heuristic map above.
173
+ // WhoAmI v4.0 (wired): brevity ctx.outputStyle (layer1-intent.ts) drives answer
174
+ // length; feedback_style="precise-correction" CORRECTION_AFFORDANCE_RULE on code
175
+ // tasks (auditable diffs, see applyPilSuffix below). The originally-assumed
176
+ // feedback_style="explicit" gate-NO_PREAMBLE arm was dropped on purpose: EE emits
177
+ // implicit | precise-correction (no "explicit"), and gating preamble on it would
178
+ // double-count brevity's verbosity axis. The "implicit" value is likewise left
179
+ // unwired — its signal is already carried by brevity + delegation_style.
171
180
  export function applyPilSuffix(systemPrompt, ctx, responseToolsActive = false) {
172
181
  // Chitchat: layer6Output already skipped suffix work; mirror that here so
173
182
  // direct callers (e.g. orchestrator) don't accidentally re-inject rules.
@@ -237,6 +246,15 @@ export function applyPilSuffix(systemPrompt, ctx, responseToolsActive = false) {
237
246
  if (!isQuestionTurn) {
238
247
  result += NO_BOOKKEEPING_NOTE;
239
248
  }
249
+ // WhoAmI v4.0: precise-correctors get an auditable-changes nudge on code tasks.
250
+ // Fail-open: getWhoAmIProfile is cached + returns null when absent → unchanged.
251
+ // We intentionally do NOT wire feedback_style="implicit" — its signal (terseness /
252
+ // proceed-without-confirm) is already carried by brevity + delegation_style, so a
253
+ // second knob would double-count. Only the precise-correction arm is non-redundant.
254
+ if (ACTION_TASKS.has(ctx.taskType) &&
255
+ getWhoAmIProfile()?.dims["communication.feedback_style"]?.value === "precise-correction") {
256
+ result += CORRECTION_AFFORDANCE_RULE;
257
+ }
240
258
  // T1 behavioral rules (proven-tier EE points set by Layer 3). These are
241
259
  // project-specific reflexes the model MUST follow — injected as instructions,
242
260
  // not as context hints, so they carry imperative weight rather than suggestion weight.
@@ -76,3 +76,14 @@ export type LlmClassifyFn = (prompt: string, signal?: AbortSignal) => Promise<Ll
76
76
  * fail-open (keep prior taskType, do not block the turn).
77
77
  */
78
78
  export declare function createLlmClassifier(factory: ProviderFactory, modelId: string): LlmClassifyFn;
79
+ export type SubSessionAction = "DIRECT_ANSWER" | "ROTATE_SESSION" | "SPAWN_SUB_SESSION";
80
+ export interface SubSessionRouteResult {
81
+ action: SubSessionAction;
82
+ confidence: number;
83
+ reason: string;
84
+ }
85
+ export declare function classifySubSessionActionHeuristic(prompt: string): SubSessionRouteResult | null;
86
+ export declare function classifySubSessionAction(factory: ProviderFactory, modelId: string, prompt: string, contextInfo?: {
87
+ currentChars: number;
88
+ threshold: number;
89
+ }, signal?: AbortSignal): Promise<SubSessionRouteResult | null>;