@caupulican/pi-adaptative 0.81.26 → 0.81.29

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 (286) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/cli/args.d.ts.map +1 -1
  3. package/dist/cli/args.js +3 -2
  4. package/dist/cli/args.js.map +1 -1
  5. package/dist/core/agent-session-services.d.ts +4 -0
  6. package/dist/core/agent-session-services.d.ts.map +1 -1
  7. package/dist/core/agent-session-services.js +19 -2
  8. package/dist/core/agent-session-services.js.map +1 -1
  9. package/dist/core/agent-session.d.ts +30 -12
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +128 -36
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/auth-storage.d.ts +6 -0
  14. package/dist/core/auth-storage.d.ts.map +1 -1
  15. package/dist/core/auth-storage.js +35 -0
  16. package/dist/core/auth-storage.js.map +1 -1
  17. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
  18. package/dist/core/autonomy/bounded-completion.js +59 -19
  19. package/dist/core/autonomy/bounded-completion.js.map +1 -1
  20. package/dist/core/autonomy/contracts.d.ts +2 -0
  21. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  22. package/dist/core/autonomy/contracts.js.map +1 -1
  23. package/dist/core/autonomy/lane-tool-surface.d.ts +31 -0
  24. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -0
  25. package/dist/core/autonomy/lane-tool-surface.js +96 -0
  26. package/dist/core/autonomy/lane-tool-surface.js.map +1 -0
  27. package/dist/core/autonomy/lane-tracker.d.ts +5 -0
  28. package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
  29. package/dist/core/autonomy/lane-tracker.js +14 -3
  30. package/dist/core/autonomy/lane-tracker.js.map +1 -1
  31. package/dist/core/background-lane-controller.d.ts +28 -4
  32. package/dist/core/background-lane-controller.d.ts.map +1 -1
  33. package/dist/core/background-lane-controller.js +237 -28
  34. package/dist/core/background-lane-controller.js.map +1 -1
  35. package/dist/core/billing-failover-controller.d.ts.map +1 -1
  36. package/dist/core/billing-failover-controller.js +1 -1
  37. package/dist/core/billing-failover-controller.js.map +1 -1
  38. package/dist/core/context/current-work-memory.d.ts +9 -0
  39. package/dist/core/context/current-work-memory.d.ts.map +1 -0
  40. package/dist/core/context/current-work-memory.js +61 -0
  41. package/dist/core/context/current-work-memory.js.map +1 -0
  42. package/dist/core/context/file-store-memory-provider.d.ts +9 -0
  43. package/dist/core/context/file-store-memory-provider.d.ts.map +1 -0
  44. package/dist/core/context/file-store-memory-provider.js +106 -0
  45. package/dist/core/context/file-store-memory-provider.js.map +1 -0
  46. package/dist/core/context/long-term-memory-trigger.d.ts +15 -0
  47. package/dist/core/context/long-term-memory-trigger.d.ts.map +1 -0
  48. package/dist/core/context/long-term-memory-trigger.js +39 -0
  49. package/dist/core/context/long-term-memory-trigger.js.map +1 -0
  50. package/dist/core/context/memory-prompt-block.d.ts +3 -1
  51. package/dist/core/context/memory-prompt-block.d.ts.map +1 -1
  52. package/dist/core/context/memory-prompt-block.js +20 -3
  53. package/dist/core/context/memory-prompt-block.js.map +1 -1
  54. package/dist/core/context/memory-prompt-budget.d.ts +17 -0
  55. package/dist/core/context/memory-prompt-budget.d.ts.map +1 -0
  56. package/dist/core/context/memory-prompt-budget.js +64 -0
  57. package/dist/core/context/memory-prompt-budget.js.map +1 -0
  58. package/dist/core/context/memory-provider-contract.d.ts.map +1 -1
  59. package/dist/core/context/memory-provider-contract.js +2 -5
  60. package/dist/core/context/memory-provider-contract.js.map +1 -1
  61. package/dist/core/context/memory-tier-composer.d.ts +27 -0
  62. package/dist/core/context/memory-tier-composer.d.ts.map +1 -0
  63. package/dist/core/context/memory-tier-composer.js +79 -0
  64. package/dist/core/context/memory-tier-composer.js.map +1 -0
  65. package/dist/core/context-gc.d.ts.map +1 -1
  66. package/dist/core/context-gc.js +2 -0
  67. package/dist/core/context-gc.js.map +1 -1
  68. package/dist/core/context-pipeline.d.ts +5 -3
  69. package/dist/core/context-pipeline.d.ts.map +1 -1
  70. package/dist/core/context-pipeline.js +15 -5
  71. package/dist/core/context-pipeline.js.map +1 -1
  72. package/dist/core/cost/cost-summary.d.ts +3 -1
  73. package/dist/core/cost/cost-summary.d.ts.map +1 -1
  74. package/dist/core/cost/cost-summary.js +3 -3
  75. package/dist/core/cost/cost-summary.js.map +1 -1
  76. package/dist/core/cost-guard.d.ts +17 -7
  77. package/dist/core/cost-guard.d.ts.map +1 -1
  78. package/dist/core/cost-guard.js +33 -10
  79. package/dist/core/cost-guard.js.map +1 -1
  80. package/dist/core/default-tool-surface.d.ts +9 -0
  81. package/dist/core/default-tool-surface.d.ts.map +1 -0
  82. package/dist/core/default-tool-surface.js +18 -0
  83. package/dist/core/default-tool-surface.js.map +1 -0
  84. package/dist/core/delegation/worker-result.d.ts.map +1 -1
  85. package/dist/core/delegation/worker-result.js +3 -0
  86. package/dist/core/delegation/worker-result.js.map +1 -1
  87. package/dist/core/delegation/worker-runner.d.ts +11 -5
  88. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  89. package/dist/core/delegation/worker-runner.js +77 -16
  90. package/dist/core/delegation/worker-runner.js.map +1 -1
  91. package/dist/core/extensions/loader.d.ts.map +1 -1
  92. package/dist/core/extensions/loader.js +15 -0
  93. package/dist/core/extensions/loader.js.map +1 -1
  94. package/dist/core/extensions/runner.d.ts +2 -0
  95. package/dist/core/extensions/runner.d.ts.map +1 -1
  96. package/dist/core/extensions/runner.js +7 -0
  97. package/dist/core/extensions/runner.js.map +1 -1
  98. package/dist/core/extensions/types.d.ts +9 -0
  99. package/dist/core/extensions/types.d.ts.map +1 -1
  100. package/dist/core/extensions/types.js.map +1 -1
  101. package/dist/core/failure-corpus.d.ts.map +1 -1
  102. package/dist/core/failure-corpus.js +2 -4
  103. package/dist/core/failure-corpus.js.map +1 -1
  104. package/dist/core/footer-data-provider.d.ts.map +1 -1
  105. package/dist/core/footer-data-provider.js +9 -1
  106. package/dist/core/footer-data-provider.js.map +1 -1
  107. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
  108. package/dist/core/goals/goal-continuation-prompt.js +4 -3
  109. package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
  110. package/dist/core/http-dispatcher.d.ts +12 -0
  111. package/dist/core/http-dispatcher.d.ts.map +1 -1
  112. package/dist/core/http-dispatcher.js +42 -4
  113. package/dist/core/http-dispatcher.js.map +1 -1
  114. package/dist/core/local-runtime-controller.d.ts +16 -0
  115. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  116. package/dist/core/local-runtime-controller.js +107 -28
  117. package/dist/core/local-runtime-controller.js.map +1 -1
  118. package/dist/core/memory/memory-manager.d.ts +8 -3
  119. package/dist/core/memory/memory-manager.d.ts.map +1 -1
  120. package/dist/core/memory/memory-manager.js +27 -11
  121. package/dist/core/memory/memory-manager.js.map +1 -1
  122. package/dist/core/memory/memory-provider.d.ts +5 -1
  123. package/dist/core/memory/memory-provider.d.ts.map +1 -1
  124. package/dist/core/memory/memory-provider.js.map +1 -1
  125. package/dist/core/memory/providers/file-store.d.ts +3 -1
  126. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  127. package/dist/core/memory/providers/file-store.js +7 -2
  128. package/dist/core/memory/providers/file-store.js.map +1 -1
  129. package/dist/core/memory/providers/transcript-recall.d.ts +1 -0
  130. package/dist/core/memory/providers/transcript-recall.d.ts.map +1 -1
  131. package/dist/core/memory/providers/transcript-recall.js +4 -7
  132. package/dist/core/memory/providers/transcript-recall.js.map +1 -1
  133. package/dist/core/memory-controller.d.ts +36 -10
  134. package/dist/core/memory-controller.d.ts.map +1 -1
  135. package/dist/core/memory-controller.js +137 -21
  136. package/dist/core/memory-controller.js.map +1 -1
  137. package/dist/core/model-registry.d.ts +25 -0
  138. package/dist/core/model-registry.d.ts.map +1 -1
  139. package/dist/core/model-registry.js +91 -8
  140. package/dist/core/model-registry.js.map +1 -1
  141. package/dist/core/model-resolver.d.ts.map +1 -1
  142. package/dist/core/model-resolver.js +30 -11
  143. package/dist/core/model-resolver.js.map +1 -1
  144. package/dist/core/model-router-controller.d.ts.map +1 -1
  145. package/dist/core/model-router-controller.js +33 -29
  146. package/dist/core/model-router-controller.js.map +1 -1
  147. package/dist/core/model-selection-controller.d.ts +3 -0
  148. package/dist/core/model-selection-controller.d.ts.map +1 -1
  149. package/dist/core/model-selection-controller.js +8 -1
  150. package/dist/core/model-selection-controller.js.map +1 -1
  151. package/dist/core/models/local-runtime.d.ts +2 -0
  152. package/dist/core/models/local-runtime.d.ts.map +1 -1
  153. package/dist/core/models/local-runtime.js +10 -4
  154. package/dist/core/models/local-runtime.js.map +1 -1
  155. package/dist/core/models/perf-profile.d.ts.map +1 -1
  156. package/dist/core/models/perf-profile.js +95 -33
  157. package/dist/core/models/perf-profile.js.map +1 -1
  158. package/dist/core/models/runtime-arbiter.d.ts +11 -0
  159. package/dist/core/models/runtime-arbiter.d.ts.map +1 -1
  160. package/dist/core/models/runtime-arbiter.js +35 -13
  161. package/dist/core/models/runtime-arbiter.js.map +1 -1
  162. package/dist/core/profile-filter-controller.d.ts +11 -0
  163. package/dist/core/profile-filter-controller.d.ts.map +1 -1
  164. package/dist/core/profile-filter-controller.js +56 -11
  165. package/dist/core/profile-filter-controller.js.map +1 -1
  166. package/dist/core/profile-registry.d.ts +2 -2
  167. package/dist/core/profile-registry.d.ts.map +1 -1
  168. package/dist/core/profile-registry.js +58 -7
  169. package/dist/core/profile-registry.js.map +1 -1
  170. package/dist/core/reflection-controller.d.ts +9 -7
  171. package/dist/core/reflection-controller.d.ts.map +1 -1
  172. package/dist/core/reflection-controller.js +81 -12
  173. package/dist/core/reflection-controller.js.map +1 -1
  174. package/dist/core/research/research-runner.d.ts +2 -2
  175. package/dist/core/research/research-runner.d.ts.map +1 -1
  176. package/dist/core/research/research-runner.js +8 -6
  177. package/dist/core/research/research-runner.js.map +1 -1
  178. package/dist/core/resource-loader.d.ts +4 -2
  179. package/dist/core/resource-loader.d.ts.map +1 -1
  180. package/dist/core/resource-loader.js +11 -1
  181. package/dist/core/resource-loader.js.map +1 -1
  182. package/dist/core/runtime-builder.d.ts +30 -3
  183. package/dist/core/runtime-builder.d.ts.map +1 -1
  184. package/dist/core/runtime-builder.js +140 -40
  185. package/dist/core/runtime-builder.js.map +1 -1
  186. package/dist/core/sdk.d.ts +8 -7
  187. package/dist/core/sdk.d.ts.map +1 -1
  188. package/dist/core/sdk.js +38 -5
  189. package/dist/core/sdk.js.map +1 -1
  190. package/dist/core/security/secret-text.d.ts +3 -0
  191. package/dist/core/security/secret-text.d.ts.map +1 -0
  192. package/dist/core/security/secret-text.js +26 -0
  193. package/dist/core/security/secret-text.js.map +1 -0
  194. package/dist/core/settings-manager.d.ts +59 -17
  195. package/dist/core/settings-manager.d.ts.map +1 -1
  196. package/dist/core/settings-manager.js +300 -51
  197. package/dist/core/settings-manager.js.map +1 -1
  198. package/dist/core/skills.d.ts.map +1 -1
  199. package/dist/core/skills.js +1 -1
  200. package/dist/core/skills.js.map +1 -1
  201. package/dist/core/system-prompt-builder.d.ts +7 -0
  202. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  203. package/dist/core/system-prompt-builder.js +23 -1
  204. package/dist/core/system-prompt-builder.js.map +1 -1
  205. package/dist/core/tool-gate-controller.d.ts +3 -0
  206. package/dist/core/tool-gate-controller.d.ts.map +1 -1
  207. package/dist/core/tool-gate-controller.js +20 -15
  208. package/dist/core/tool-gate-controller.js.map +1 -1
  209. package/dist/core/tool-selection/expected-utility.d.ts +51 -0
  210. package/dist/core/tool-selection/expected-utility.d.ts.map +1 -0
  211. package/dist/core/tool-selection/expected-utility.js +85 -0
  212. package/dist/core/tool-selection/expected-utility.js.map +1 -0
  213. package/dist/core/tool-selection/tool-performance-store.d.ts +82 -0
  214. package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -0
  215. package/dist/core/tool-selection/tool-performance-store.js +249 -0
  216. package/dist/core/tool-selection/tool-performance-store.js.map +1 -0
  217. package/dist/core/tool-selection/tool-selection-controller.d.ts +41 -0
  218. package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -0
  219. package/dist/core/tool-selection/tool-selection-controller.js +199 -0
  220. package/dist/core/tool-selection/tool-selection-controller.js.map +1 -0
  221. package/dist/core/tools/delegate-status.d.ts +9 -0
  222. package/dist/core/tools/delegate-status.d.ts.map +1 -0
  223. package/dist/core/tools/delegate-status.js +52 -0
  224. package/dist/core/tools/delegate-status.js.map +1 -0
  225. package/dist/core/tools/delegate.d.ts +10 -0
  226. package/dist/core/tools/delegate.d.ts.map +1 -1
  227. package/dist/core/tools/delegate.js +36 -13
  228. package/dist/core/tools/delegate.js.map +1 -1
  229. package/dist/core/tools/run-toolkit-script.d.ts +3 -0
  230. package/dist/core/tools/run-toolkit-script.d.ts.map +1 -1
  231. package/dist/core/tools/run-toolkit-script.js +17 -7
  232. package/dist/core/tools/run-toolkit-script.js.map +1 -1
  233. package/dist/main.d.ts.map +1 -1
  234. package/dist/main.js +2 -0
  235. package/dist/main.js.map +1 -1
  236. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  237. package/dist/modes/interactive/components/footer.js +8 -1
  238. package/dist/modes/interactive/components/footer.js.map +1 -1
  239. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  240. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  241. package/dist/modes/interactive/components/settings-selector.js +69 -29
  242. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  243. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  244. package/dist/modes/interactive/components/thinking-selector.js +2 -0
  245. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  246. package/dist/modes/interactive/config-backup.d.ts +7 -1
  247. package/dist/modes/interactive/config-backup.d.ts.map +1 -1
  248. package/dist/modes/interactive/config-backup.js +196 -32
  249. package/dist/modes/interactive/config-backup.js.map +1 -1
  250. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  251. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  252. package/dist/modes/interactive/interactive-mode.js +22 -28
  253. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  254. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  255. package/dist/modes/interactive/local-model-commands.js +2 -0
  256. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  257. package/dist/modes/interactive/profile-menu-controller.d.ts +6 -4
  258. package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
  259. package/dist/modes/interactive/profile-menu-controller.js +248 -65
  260. package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
  261. package/dist/modes/interactive/report-commands.d.ts.map +1 -1
  262. package/dist/modes/interactive/report-commands.js +8 -2
  263. package/dist/modes/interactive/report-commands.js.map +1 -1
  264. package/dist/modes/interactive/settings-selector-flow.d.ts.map +1 -1
  265. package/dist/modes/interactive/settings-selector-flow.js +8 -0
  266. package/dist/modes/interactive/settings-selector-flow.js.map +1 -1
  267. package/dist/modes/interactive/theme/theme-schema.json +1 -1
  268. package/dist/modes/interactive/theme/theme.d.ts +1 -1
  269. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  270. package/dist/modes/interactive/theme/theme.js +3 -1
  271. package/dist/modes/interactive/theme/theme.js.map +1 -1
  272. package/docs/extensions.md +32 -0
  273. package/docs/integration-sweep-builder-blueprint-2026-07-09.md +365 -0
  274. package/docs/integration-sweep-resume-2026-07-09.md +407 -0
  275. package/docs/resources.md +15 -7
  276. package/docs/settings.md +56 -5
  277. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  278. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  279. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  280. package/examples/extensions/preset.ts +2 -2
  281. package/examples/extensions/sandbox/package-lock.json +2 -2
  282. package/examples/extensions/sandbox/package.json +1 -1
  283. package/examples/extensions/with-deps/package-lock.json +2 -2
  284. package/examples/extensions/with-deps/package.json +1 -1
  285. package/npm-shrinkwrap.json +44 -35
  286. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"profile-filter-controller.d.ts","sourceRoot":"","sources":["../../src/core/profile-filter-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAEN,KAAK,6BAA6B,EAClC,KAAK,eAAe,EACpB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,2BAA2B;IAC3C,kBAAkB,IAAI,eAAe,CAAC;IACtC,iBAAiB,IAAI,cAAc,CAAC;IACpC,gBAAgB,IAAI,aAAa,CAAC;IAClC,MAAM,IAAI,MAAM,CAAC;IACjB,QAAQ,IAAI,KAAK,CAAC;IAClB,iBAAiB,IAAI,cAAc,CAAC;IACpC,yEAAyE;IACzE,mBAAmB,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAC/C,0CAA0C;IAC1C,oBAAoB,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAChD,kFAAkF;IAClF,oBAAoB,IAAI,QAAQ,CAAC,6BAA6B,CAAC,GAAG,SAAS,CAAC;IAC5E,0FAA0F;IAC1F,eAAe,IAAI,OAAO,CAAC;IAC3B,qEAAqE;IACrE,kBAAkB,IAAI,OAAO,CAAC;IAC9B,6DAA6D;IAC7D,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;CAC7C;AAED,qBAAa,uBAAuB;IACnC,qFAAqF;IACrF,OAAO,CAAC,uBAAuB,CAAgB;IAC/C,kGAAkG;IAClG,OAAO,CAAC,4BAA4B,CAAK;IAEzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA8B;IAEnD,YAAY,IAAI,EAAE,2BAA2B,EAE5C;IAED,4FAA4F;IAC5F,yBAAyB,IAAI,MAAM,EAAE,CAEpC;IAED;;;;OAIG;IACH,uBAAuB,IAAI,QAAQ,CAAC,6BAA6B,CAAC,CAGjE;IAED,+BAA+B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUrD;IAED,OAAO,CAAC,4BAA4B;IASpC,0BAA0B,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAuC/D;IAED;;;;;;;;;;;;;OAaG;IACH,iCAAiC,IAAI,MAAM,EAAE,CA0B5C;IAED;;;;;OAKG;IACG,iCAAiC,IAAI,OAAO,CAAC,IAAI,CAAC,CA2BvD;CACD","sourcesContent":["/**\n * Resource-profile tool/extension gating + reload-time profile model re-application.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Resolves the active resource\n * profile's tool allow/block filter, filters the runtime extension set (tracking the withheld/inert\n * tallies it owns), reports the /context \"withheld by profile\" observations, and — on reload —\n * re-applies a profile-bound model/thinking level unless an explicit launch flag set them. Owns the\n * inert-extension warnings and profile-denied-extension count; everything else is read through deps.\n */\n\nimport type { Agent, ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { Extension } from \"./extensions/index.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { resolveProfileModelSettings } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport {\n\tmatchesResourceProfilePattern,\n\ttype ResourceProfileFilterSettings,\n\ttype SettingsManager,\n} from \"./settings-manager.ts\";\n\nexport interface ProfileFilterControllerDeps {\n\tgetSettingsManager(): SettingsManager;\n\tgetResourceLoader(): ResourceLoader;\n\tgetModelRegistry(): ModelRegistry;\n\tgetCwd(): string;\n\tgetAgent(): Agent;\n\tgetSessionManager(): SessionManager;\n\t/** Construction-time tool allow-set (undefined = no allow-list gate). */\n\tgetAllowedToolNames(): Set<string> | undefined;\n\t/** Construction-time tool exclude-set. */\n\tgetExcludedToolNames(): Set<string> | undefined;\n\t/** Live resource-profile tool filter (re-derived on reload; may be undefined). */\n\tgetToolProfileFilter(): Required<ResourceProfileFilterSettings> | undefined;\n\t/** Whether the model was set by an explicit launch flag (profile must not clobber it). */\n\tisExplicitModel(): boolean;\n\t/** Whether the thinking level was set by an explicit launch flag. */\n\tisExplicitThinking(): boolean;\n\t/** Apply a thinking level (delegates to model-selection). */\n\tsetThinkingLevel(level: ThinkingLevel): void;\n}\n\nexport class ProfileFilterController {\n\t/** G12: extensions loaded but rendered fully inert by the profile's tools filter. */\n\tprivate _inertExtensionWarnings: string[] = [];\n\t/** Count of extensions withheld by the active resource profile (for the /context observation). */\n\tprivate _profileDeniedExtensionCount = 0;\n\n\tprivate readonly deps: ProfileFilterControllerDeps;\n\n\tconstructor(deps: ProfileFilterControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/** Inert-extension warnings tracked by the last {@link filterExtensionsForRuntime} pass. */\n\tgetInertExtensionWarnings(): string[] {\n\t\treturn this._inertExtensionWarnings;\n\t}\n\n\t/**\n\t * Resolve the active resource-profile tool allow/block filter from current settings.\n\t * Mirrors the construction-time derivation (settingsManager.getResourceProfileFilter(\"tools\"))\n\t * so reload() can re-apply it after a live settings/profile edit.\n\t */\n\tderiveToolProfileFilter(): Required<ResourceProfileFilterSettings> {\n\t\tconst filter = this.deps.getSettingsManager().getResourceProfileFilter(\"tools\");\n\t\treturn { allow: filter.allow ?? [], block: filter.block ?? [] };\n\t}\n\n\tisToolOrCommandAllowedByProfile(name: string): boolean {\n\t\tconst allowedToolNames = this.deps.getAllowedToolNames();\n\t\tconst excludedToolNames = this.deps.getExcludedToolNames();\n\t\tif (allowedToolNames && !allowedToolNames.has(name)) return false;\n\t\tif (excludedToolNames?.has(name)) return false;\n\t\tconst filter = this.deps.getToolProfileFilter();\n\t\tif (!filter) return true;\n\t\tif (filter.allow.length > 0 && !matchesResourceProfilePattern(name, filter.allow)) return false;\n\t\tif (matchesResourceProfilePattern(name, filter.block)) return false;\n\t\treturn true;\n\t}\n\n\tprivate _hasToolOrCommandProfileGate(): boolean {\n\t\tconst toolProfileFilter = this.deps.getToolProfileFilter();\n\t\treturn Boolean(\n\t\t\tthis.deps.getAllowedToolNames() ||\n\t\t\t\tthis.deps.getExcludedToolNames() ||\n\t\t\t\t(toolProfileFilter && (toolProfileFilter.allow.length > 0 || toolProfileFilter.block.length > 0)),\n\t\t);\n\t}\n\n\tfilterExtensionsForRuntime(extensions: Extension[]): Extension[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tthis._inertExtensionWarnings = [];\n\t\tthis._profileDeniedExtensionCount = 0;\n\t\tif (settingsManager.getActiveResourceProfileNames().length === 0) {\n\t\t\tif (settingsManager.hasExplicitActiveResourceProfileSelection()) {\n\t\t\t\t// An explicit profile selection that resolves to no active profile is a deliberate\n\t\t\t\t// deny-all — every extension is withheld by that choice.\n\t\t\t\tthis._profileDeniedExtensionCount = extensions.length;\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\t// No profile in play: only inline/SDK extensions load by default. That is the baseline, not\n\t\t\t// a profile denial, so it is not counted as withheld.\n\t\t\treturn extensions.filter((extension) => extension.sourceInfo.source === \"inline\");\n\t\t}\n\t\tconst hasToolOrCommandGate = this._hasToolOrCommandProfileGate();\n\t\tconst allowedExtensions = extensions.filter((extension) =>\n\t\t\tsettingsManager.isResourceAllowedByProfile(\"extensions\", extension.path, extension.sourceInfo.baseDir),\n\t\t);\n\t\tthis._profileDeniedExtensionCount = extensions.length - allowedExtensions.length;\n\t\treturn allowedExtensions.map((extension) => {\n\t\t\tif (!hasToolOrCommandGate) return extension;\n\t\t\tconst tools = new Map(\n\t\t\t\tArray.from(extension.tools.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\tconst commands = new Map(\n\t\t\t\tArray.from(extension.commands.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\t// G12: an extension the profile ALLOWS whose every tool and command the tools filter\n\t\t\t// then denies loads and runs lifecycle hooks but is completely uninvocable — surface\n\t\t\t// it instead of presenting a \"loaded\" extension that silently does nothing.\n\t\t\tif (extension.tools.size + extension.commands.size > 0 && tools.size + commands.size === 0) {\n\t\t\t\tconst name = extension.path.split(/[\\\\/]/).pop() ?? extension.path;\n\t\t\t\tthis._inertExtensionWarnings.push(\n\t\t\t\t\t`extension \"${name}\" is loaded but fully inert: the active profile's tools filter denies all ${extension.tools.size} tool(s) and ${extension.commands.size} command(s) it contributes`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn { ...extension, tools, commands };\n\t\t});\n\t}\n\n\t/**\n\t * /context observations for skills/prompts/extensions the active resource profile removed from\n\t * listings — the analog of the withheld-AGENTS.md warning. Strict UAC makes these silently absent,\n\t * so a lean profile's effect on the resource surface stays visible. Counts are profile-scoped\n\t * (skills/prompts via the profile-independent discovery universe filtered by the live profile\n\t * filter; extensions via the runtime filter's denied tally). Empty when nothing is withheld.\n\t *\n\t * Uses `isResourceDeniedByActiveProfile` (profile-only), not `isResourceAllowedByProfile` (which\n\t * also folds in the user's own legacy `disabledResources` list): a plain user-disabled resource\n\t * must never be misattributed to \"the active resource profile\" — that case is already surfaced by\n\t * the G14 disable-wins warning. With no active profile at all, the helper always reports nothing\n\t * denied, so this naturally stays silent (extensions keep their own runtime-filter-derived count,\n\t * which is already correctly zero absent a profile).\n\t */\n\tprofileDeniedResourceObservations(): string[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst resourceLoader = this.deps.getResourceLoader();\n\t\tconst cwd = this.deps.getCwd();\n\t\tconst observations: string[] = [];\n\t\tconst withheld = (kind: \"skills\" | \"prompts\", paths: string[]): number =>\n\t\t\tpaths.filter((path) => settingsManager.isResourceDeniedByActiveProfile(kind, path, cwd)).length;\n\n\t\tconst skillsWithheld = withheld(\"skills\", resourceLoader.getDiscoverableSkillPaths());\n\t\tif (skillsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${skillsWithheld} skill(s) withheld by the active resource profile — grant the \"skills\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tconst promptsWithheld = withheld(\"prompts\", resourceLoader.getDiscoverablePromptPaths());\n\t\tif (promptsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${promptsWithheld} prompt(s) withheld by the active resource profile — grant the \"prompts\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tif (this._profileDeniedExtensionCount > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${this._profileDeniedExtensionCount} extension(s) withheld by the active resource profile — grant the \"extensions\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\treturn observations;\n\t}\n\n\t/**\n\t * Re-resolve the active resource profile's model/thinking from current settings and apply it.\n\t * Only acts when the profile actually binds model/thinking AND that field was not set by an\n\t * explicit launch flag — so live profile edits apply on reload without clobbering an explicit\n\t * --model/--thinking. A no-op for profiles that don't bind a model.\n\t */\n\tasync reapplyActiveProfileModelSettings(): Promise<void> {\n\t\tif (this.deps.isExplicitModel() && this.deps.isExplicitThinking()) return;\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst activeProfileNames = settingsManager.getActiveResourceProfileNames();\n\t\tif (activeProfileNames.length === 0) return;\n\t\tconst profileSettings = resolveProfileModelSettings({\n\t\t\tactiveProfileNames,\n\t\t\tregistry: settingsManager.getProfileRegistry(),\n\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\tcwd: this.deps.getCwd(),\n\t\t});\n\t\tif (!this.deps.isExplicitModel() && profileSettings.model) {\n\t\t\tconst current = this.deps.getAgent().state.model;\n\t\t\tconst next = profileSettings.model;\n\t\t\tif (!current || current.provider !== next.provider || current.id !== next.id) {\n\t\t\t\t// Mirror the startup/cycle path: set the model directly (no auth gate, no settings\n\t\t\t\t// persist) so re-applying the profile model behaves like initial resolution rather\n\t\t\t\t// than a runtime model switch. No model_select emit here — reload rebuilds the\n\t\t\t\t// extension runtime and emits session_start(\"reload\") right after, and the UI\n\t\t\t\t// re-renders from session.model.\n\t\t\t\tthis.deps.getAgent().state.model = next;\n\t\t\t\tthis.deps.getSessionManager().appendModelChange(next.provider, next.id);\n\t\t\t}\n\t\t}\n\t\tif (!this.deps.isExplicitThinking() && profileSettings.thinkingLevel) {\n\t\t\tthis.deps.setThinkingLevel(profileSettings.thinkingLevel);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"profile-filter-controller.d.ts","sourceRoot":"","sources":["../../src/core/profile-filter-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAEN,KAAK,6BAA6B,EAClC,KAAK,eAAe,EACpB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,2BAA2B;IAC3C,kBAAkB,IAAI,eAAe,CAAC;IACtC,iBAAiB,IAAI,cAAc,CAAC;IACpC,gBAAgB,IAAI,aAAa,CAAC;IAClC,MAAM,IAAI,MAAM,CAAC;IACjB,QAAQ,IAAI,KAAK,CAAC;IAClB,iBAAiB,IAAI,cAAc,CAAC;IACpC,yEAAyE;IACzE,mBAAmB,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAC/C,0CAA0C;IAC1C,oBAAoB,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAChD,kFAAkF;IAClF,oBAAoB,IAAI,QAAQ,CAAC,6BAA6B,CAAC,GAAG,SAAS,CAAC;IAC5E,0FAA0F;IAC1F,eAAe,IAAI,OAAO,CAAC;IAC3B,qEAAqE;IACrE,kBAAkB,IAAI,OAAO,CAAC;IAC9B,6DAA6D;IAC7D,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,2BAA2B;IAC3C,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,2BAA2B,EAAE,MAAM,CAAC;IACpC,sBAAsB,EAAE,OAAO,CAAC;IAChC,yBAAyB,EAAE,OAAO,CAAC;CACnC;AAED,qBAAa,uBAAuB;IACnC,qFAAqF;IACrF,OAAO,CAAC,uBAAuB,CAAgB;IAC/C,kGAAkG;IAClG,OAAO,CAAC,4BAA4B,CAAK;IACzC,2GAA2G;IAC3G,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,0BAA0B,CAAS;IAE3C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA8B;IAEnD,YAAY,IAAI,EAAE,2BAA2B,EAE5C;IAED,4FAA4F;IAC5F,yBAAyB,IAAI,MAAM,EAAE,CAEpC;IAED,oBAAoB,IAAI,2BAA2B,CAOlD;IAED,qBAAqB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,IAAI,CAKjE;IAED;;;;OAIG;IACH,uBAAuB,IAAI,QAAQ,CAAC,6BAA6B,CAAC,CAGjE;IAED,+BAA+B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUrD;IAED,OAAO,CAAC,4BAA4B;IASpC,0BAA0B,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAuC/D;IAED;;;;;;;;;;;;;OAaG;IACH,iCAAiC,IAAI,MAAM,EAAE,CA0B5C;IAED;;;;;OAKG;IACG,iCAAiC,IAAI,OAAO,CAAC,IAAI,CAAC,CA0DvD;CACD","sourcesContent":["/**\n * Resource-profile tool/extension gating + reload-time profile model re-application.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Resolves the active resource\n * profile's tool allow/block filter, filters the runtime extension set (tracking the withheld/inert\n * tallies it owns), reports the /context \"withheld by profile\" observations, and — on reload —\n * re-applies a profile-bound model/thinking level unless an explicit launch flag set them. Owns the\n * inert-extension warnings and profile-denied-extension count; everything else is read through deps.\n */\n\nimport type { Agent, ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { Extension } from \"./extensions/index.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel, resolveProfileModelSettings } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport {\n\tmatchesResourceProfilePattern,\n\ttype ResourceProfileFilterSettings,\n\ttype SettingsManager,\n} from \"./settings-manager.ts\";\n\nexport interface ProfileFilterControllerDeps {\n\tgetSettingsManager(): SettingsManager;\n\tgetResourceLoader(): ResourceLoader;\n\tgetModelRegistry(): ModelRegistry;\n\tgetCwd(): string;\n\tgetAgent(): Agent;\n\tgetSessionManager(): SessionManager;\n\t/** Construction-time tool allow-set (undefined = no allow-list gate). */\n\tgetAllowedToolNames(): Set<string> | undefined;\n\t/** Construction-time tool exclude-set. */\n\tgetExcludedToolNames(): Set<string> | undefined;\n\t/** Live resource-profile tool filter (re-derived on reload; may be undefined). */\n\tgetToolProfileFilter(): Required<ResourceProfileFilterSettings> | undefined;\n\t/** Whether the model was set by an explicit launch flag (profile must not clobber it). */\n\tisExplicitModel(): boolean;\n\t/** Whether the thinking level was set by an explicit launch flag. */\n\tisExplicitThinking(): boolean;\n\t/** Apply a thinking level (delegates to model-selection). */\n\tsetThinkingLevel(level: ThinkingLevel): void;\n}\n\nexport interface ProfileFilterReloadSnapshot {\n\tinertExtensionWarnings: string[];\n\tprofileDeniedExtensionCount: number;\n\tprofileControlledModel: boolean;\n\tprofileControlledThinking: boolean;\n}\n\nexport class ProfileFilterController {\n\t/** G12: extensions loaded but rendered fully inert by the profile's tools filter. */\n\tprivate _inertExtensionWarnings: string[] = [];\n\t/** Count of extensions withheld by the active resource profile (for the /context observation). */\n\tprivate _profileDeniedExtensionCount = 0;\n\t/** Whether the last applied profile generation, rather than the base session selection, owns the field. */\n\tprivate _profileControlledModel = false;\n\tprivate _profileControlledThinking = false;\n\n\tprivate readonly deps: ProfileFilterControllerDeps;\n\n\tconstructor(deps: ProfileFilterControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/** Inert-extension warnings tracked by the last {@link filterExtensionsForRuntime} pass. */\n\tgetInertExtensionWarnings(): string[] {\n\t\treturn this._inertExtensionWarnings;\n\t}\n\n\tcreateReloadSnapshot(): ProfileFilterReloadSnapshot {\n\t\treturn {\n\t\t\tinertExtensionWarnings: [...this._inertExtensionWarnings],\n\t\t\tprofileDeniedExtensionCount: this._profileDeniedExtensionCount,\n\t\t\tprofileControlledModel: this._profileControlledModel,\n\t\t\tprofileControlledThinking: this._profileControlledThinking,\n\t\t};\n\t}\n\n\trestoreReloadSnapshot(snapshot: ProfileFilterReloadSnapshot): void {\n\t\tthis._inertExtensionWarnings = [...snapshot.inertExtensionWarnings];\n\t\tthis._profileDeniedExtensionCount = snapshot.profileDeniedExtensionCount;\n\t\tthis._profileControlledModel = snapshot.profileControlledModel;\n\t\tthis._profileControlledThinking = snapshot.profileControlledThinking;\n\t}\n\n\t/**\n\t * Resolve the active resource-profile tool allow/block filter from current settings.\n\t * Mirrors the construction-time derivation (settingsManager.getResourceProfileFilter(\"tools\"))\n\t * so reload() can re-apply it after a live settings/profile edit.\n\t */\n\tderiveToolProfileFilter(): Required<ResourceProfileFilterSettings> {\n\t\tconst filter = this.deps.getSettingsManager().getResourceProfileFilter(\"tools\");\n\t\treturn { allow: filter.allow ?? [], block: filter.block ?? [] };\n\t}\n\n\tisToolOrCommandAllowedByProfile(name: string): boolean {\n\t\tconst allowedToolNames = this.deps.getAllowedToolNames();\n\t\tconst excludedToolNames = this.deps.getExcludedToolNames();\n\t\tif (allowedToolNames && !allowedToolNames.has(name)) return false;\n\t\tif (excludedToolNames?.has(name)) return false;\n\t\tconst filter = this.deps.getToolProfileFilter();\n\t\tif (!filter) return true;\n\t\tif (filter.allow.length > 0 && !matchesResourceProfilePattern(name, filter.allow)) return false;\n\t\tif (matchesResourceProfilePattern(name, filter.block)) return false;\n\t\treturn true;\n\t}\n\n\tprivate _hasToolOrCommandProfileGate(): boolean {\n\t\tconst toolProfileFilter = this.deps.getToolProfileFilter();\n\t\treturn Boolean(\n\t\t\tthis.deps.getAllowedToolNames() ||\n\t\t\t\tthis.deps.getExcludedToolNames() ||\n\t\t\t\t(toolProfileFilter && (toolProfileFilter.allow.length > 0 || toolProfileFilter.block.length > 0)),\n\t\t);\n\t}\n\n\tfilterExtensionsForRuntime(extensions: Extension[]): Extension[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tthis._inertExtensionWarnings = [];\n\t\tthis._profileDeniedExtensionCount = 0;\n\t\tif (settingsManager.getActiveResourceProfileNames().length === 0) {\n\t\t\tif (settingsManager.hasExplicitActiveResourceProfileSelection()) {\n\t\t\t\t// An explicit profile selection that resolves to no active profile is a deliberate\n\t\t\t\t// deny-all — every extension is withheld by that choice.\n\t\t\t\tthis._profileDeniedExtensionCount = extensions.length;\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\t// No profile in play: only inline/SDK extensions load by default. That is the baseline, not\n\t\t\t// a profile denial, so it is not counted as withheld.\n\t\t\treturn extensions.filter((extension) => extension.sourceInfo.source === \"inline\");\n\t\t}\n\t\tconst hasToolOrCommandGate = this._hasToolOrCommandProfileGate();\n\t\tconst allowedExtensions = extensions.filter((extension) =>\n\t\t\tsettingsManager.isResourceAllowedByProfile(\"extensions\", extension.path, extension.sourceInfo.baseDir),\n\t\t);\n\t\tthis._profileDeniedExtensionCount = extensions.length - allowedExtensions.length;\n\t\treturn allowedExtensions.map((extension) => {\n\t\t\tif (!hasToolOrCommandGate) return extension;\n\t\t\tconst tools = new Map(\n\t\t\t\tArray.from(extension.tools.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\tconst commands = new Map(\n\t\t\t\tArray.from(extension.commands.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\t// G12: an extension the profile ALLOWS whose every tool and command the tools filter\n\t\t\t// then denies loads and runs lifecycle hooks but is completely uninvocable — surface\n\t\t\t// it instead of presenting a \"loaded\" extension that silently does nothing.\n\t\t\tif (extension.tools.size + extension.commands.size > 0 && tools.size + commands.size === 0) {\n\t\t\t\tconst name = extension.path.split(/[\\\\/]/).pop() ?? extension.path;\n\t\t\t\tthis._inertExtensionWarnings.push(\n\t\t\t\t\t`extension \"${name}\" is loaded but fully inert: the active profile's tools filter denies all ${extension.tools.size} tool(s) and ${extension.commands.size} command(s) it contributes`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn { ...extension, tools, commands };\n\t\t});\n\t}\n\n\t/**\n\t * /context observations for skills/prompts/extensions the active resource profile removed from\n\t * listings — the analog of the withheld-AGENTS.md warning. Strict UAC makes these silently absent,\n\t * so a lean profile's effect on the resource surface stays visible. Counts are profile-scoped\n\t * (skills/prompts via the profile-independent discovery universe filtered by the live profile\n\t * filter; extensions via the runtime filter's denied tally). Empty when nothing is withheld.\n\t *\n\t * Uses `isResourceDeniedByActiveProfile` (profile-only), not `isResourceAllowedByProfile` (which\n\t * also folds in the user's own legacy `disabledResources` list): a plain user-disabled resource\n\t * must never be misattributed to \"the active resource profile\" — that case is already surfaced by\n\t * the G14 disable-wins warning. With no active profile at all, the helper always reports nothing\n\t * denied, so this naturally stays silent (extensions keep their own runtime-filter-derived count,\n\t * which is already correctly zero absent a profile).\n\t */\n\tprofileDeniedResourceObservations(): string[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst resourceLoader = this.deps.getResourceLoader();\n\t\tconst cwd = this.deps.getCwd();\n\t\tconst observations: string[] = [];\n\t\tconst withheld = (kind: \"skills\" | \"prompts\", paths: string[]): number =>\n\t\t\tpaths.filter((path) => settingsManager.isResourceDeniedByActiveProfile(kind, path, cwd)).length;\n\n\t\tconst skillsWithheld = withheld(\"skills\", resourceLoader.getDiscoverableSkillPaths());\n\t\tif (skillsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${skillsWithheld} skill(s) withheld by the active resource profile — grant the \"skills\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tconst promptsWithheld = withheld(\"prompts\", resourceLoader.getDiscoverablePromptPaths());\n\t\tif (promptsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${promptsWithheld} prompt(s) withheld by the active resource profile — grant the \"prompts\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tif (this._profileDeniedExtensionCount > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${this._profileDeniedExtensionCount} extension(s) withheld by the active resource profile — grant the \"extensions\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\treturn observations;\n\t}\n\n\t/**\n\t * Re-resolve the active resource profile's model/thinking from current settings and apply it.\n\t * Only acts when the profile actually binds model/thinking AND that field was not set by an\n\t * explicit launch flag — so live profile edits apply on reload without clobbering an explicit\n\t * --model/--thinking. A no-op for profiles that don't bind a model.\n\t */\n\tasync reapplyActiveProfileModelSettings(): Promise<void> {\n\t\tif (this.deps.isExplicitModel() && this.deps.isExplicitThinking()) return;\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst activeProfileNames = settingsManager.getActiveResourceProfileNames();\n\t\tconst profileSettings =\n\t\t\tactiveProfileNames.length > 0\n\t\t\t\t? resolveProfileModelSettings({\n\t\t\t\t\t\tactiveProfileNames,\n\t\t\t\t\t\tregistry: settingsManager.getProfileRegistry(),\n\t\t\t\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\t\t\t\tcwd: this.deps.getCwd(),\n\t\t\t\t\t})\n\t\t\t\t: {};\n\t\tif (profileSettings.error) {\n\t\t\tthrow new Error(`Profile model resolution error: ${profileSettings.error}`);\n\t\t}\n\t\tconst mustRestoreBaseModel =\n\t\t\t!this.deps.isExplicitModel() && this._profileControlledModel && !profileSettings.model;\n\t\tconst mustRestoreBaseThinking =\n\t\t\t!this.deps.isExplicitThinking() && this._profileControlledThinking && !profileSettings.thinkingLevel;\n\t\tconst baseSelection =\n\t\t\tmustRestoreBaseModel || mustRestoreBaseThinking\n\t\t\t\t? await findInitialModel({\n\t\t\t\t\t\tscopedModels: [],\n\t\t\t\t\t\tisContinuing: true,\n\t\t\t\t\t\tdefaultProvider: settingsManager.getDefaultProvider(),\n\t\t\t\t\t\tdefaultModelId: settingsManager.getDefaultModel(),\n\t\t\t\t\t\tdefaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n\t\t\t\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\t\t\t})\n\t\t\t\t: undefined;\n\t\tlet modelChanged = false;\n\t\tconst nextModel = profileSettings.model ?? (mustRestoreBaseModel ? baseSelection?.model : undefined);\n\t\tif (!this.deps.isExplicitModel() && nextModel) {\n\t\t\tconst current = this.deps.getAgent().state.model;\n\t\t\tconst next = nextModel;\n\t\t\tif (!current || current.provider !== next.provider || current.id !== next.id) {\n\t\t\t\t// Mirror the startup/cycle path: set the model directly (no auth gate, no settings\n\t\t\t\t// persist) so re-applying the profile model behaves like initial resolution rather\n\t\t\t\t// than a runtime model switch. No model_select emit here — reload rebuilds the\n\t\t\t\t// extension runtime and emits session_start(\"reload\") right after, and the UI\n\t\t\t\t// re-renders from session.model.\n\t\t\t\tthis.deps.getAgent().state.model = next;\n\t\t\t\tthis.deps.getSessionManager().appendModelChange(next.provider, next.id);\n\t\t\t\tmodelChanged = true;\n\t\t\t}\n\t\t}\n\t\tthis._profileControlledModel = !this.deps.isExplicitModel() && Boolean(profileSettings.model);\n\t\tif (!this.deps.isExplicitThinking() && profileSettings.thinkingLevel) {\n\t\t\tthis.deps.setThinkingLevel(profileSettings.thinkingLevel);\n\t\t} else if (mustRestoreBaseThinking && baseSelection) {\n\t\t\tthis.deps.setThinkingLevel(baseSelection.thinkingLevel);\n\t\t} else if (modelChanged) {\n\t\t\t// A model-only situation switch still has to clamp the inherited/explicit level. This\n\t\t\t// prevents Sol/Terra Ultra from leaking into Luna or another model that stops at Max.\n\t\t\tthis.deps.setThinkingLevel(this.deps.getAgent().state.thinkingLevel);\n\t\t}\n\t\tthis._profileControlledThinking = !this.deps.isExplicitThinking() && Boolean(profileSettings.thinkingLevel);\n\t}\n}\n"]}
@@ -7,13 +7,16 @@
7
7
  * re-applies a profile-bound model/thinking level unless an explicit launch flag set them. Owns the
8
8
  * inert-extension warnings and profile-denied-extension count; everything else is read through deps.
9
9
  */
10
- import { resolveProfileModelSettings } from "./model-resolver.js";
10
+ import { findInitialModel, resolveProfileModelSettings } from "./model-resolver.js";
11
11
  import { matchesResourceProfilePattern, } from "./settings-manager.js";
12
12
  export class ProfileFilterController {
13
13
  /** G12: extensions loaded but rendered fully inert by the profile's tools filter. */
14
14
  _inertExtensionWarnings = [];
15
15
  /** Count of extensions withheld by the active resource profile (for the /context observation). */
16
16
  _profileDeniedExtensionCount = 0;
17
+ /** Whether the last applied profile generation, rather than the base session selection, owns the field. */
18
+ _profileControlledModel = false;
19
+ _profileControlledThinking = false;
17
20
  deps;
18
21
  constructor(deps) {
19
22
  this.deps = deps;
@@ -22,6 +25,20 @@ export class ProfileFilterController {
22
25
  getInertExtensionWarnings() {
23
26
  return this._inertExtensionWarnings;
24
27
  }
28
+ createReloadSnapshot() {
29
+ return {
30
+ inertExtensionWarnings: [...this._inertExtensionWarnings],
31
+ profileDeniedExtensionCount: this._profileDeniedExtensionCount,
32
+ profileControlledModel: this._profileControlledModel,
33
+ profileControlledThinking: this._profileControlledThinking,
34
+ };
35
+ }
36
+ restoreReloadSnapshot(snapshot) {
37
+ this._inertExtensionWarnings = [...snapshot.inertExtensionWarnings];
38
+ this._profileDeniedExtensionCount = snapshot.profileDeniedExtensionCount;
39
+ this._profileControlledModel = snapshot.profileControlledModel;
40
+ this._profileControlledThinking = snapshot.profileControlledThinking;
41
+ }
25
42
  /**
26
43
  * Resolve the active resource-profile tool allow/block filter from current settings.
27
44
  * Mirrors the construction-time derivation (settingsManager.getResourceProfileFilter("tools"))
@@ -130,17 +147,34 @@ export class ProfileFilterController {
130
147
  return;
131
148
  const settingsManager = this.deps.getSettingsManager();
132
149
  const activeProfileNames = settingsManager.getActiveResourceProfileNames();
133
- if (activeProfileNames.length === 0)
134
- return;
135
- const profileSettings = resolveProfileModelSettings({
136
- activeProfileNames,
137
- registry: settingsManager.getProfileRegistry(),
138
- modelRegistry: this.deps.getModelRegistry(),
139
- cwd: this.deps.getCwd(),
140
- });
141
- if (!this.deps.isExplicitModel() && profileSettings.model) {
150
+ const profileSettings = activeProfileNames.length > 0
151
+ ? resolveProfileModelSettings({
152
+ activeProfileNames,
153
+ registry: settingsManager.getProfileRegistry(),
154
+ modelRegistry: this.deps.getModelRegistry(),
155
+ cwd: this.deps.getCwd(),
156
+ })
157
+ : {};
158
+ if (profileSettings.error) {
159
+ throw new Error(`Profile model resolution error: ${profileSettings.error}`);
160
+ }
161
+ const mustRestoreBaseModel = !this.deps.isExplicitModel() && this._profileControlledModel && !profileSettings.model;
162
+ const mustRestoreBaseThinking = !this.deps.isExplicitThinking() && this._profileControlledThinking && !profileSettings.thinkingLevel;
163
+ const baseSelection = mustRestoreBaseModel || mustRestoreBaseThinking
164
+ ? await findInitialModel({
165
+ scopedModels: [],
166
+ isContinuing: true,
167
+ defaultProvider: settingsManager.getDefaultProvider(),
168
+ defaultModelId: settingsManager.getDefaultModel(),
169
+ defaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),
170
+ modelRegistry: this.deps.getModelRegistry(),
171
+ })
172
+ : undefined;
173
+ let modelChanged = false;
174
+ const nextModel = profileSettings.model ?? (mustRestoreBaseModel ? baseSelection?.model : undefined);
175
+ if (!this.deps.isExplicitModel() && nextModel) {
142
176
  const current = this.deps.getAgent().state.model;
143
- const next = profileSettings.model;
177
+ const next = nextModel;
144
178
  if (!current || current.provider !== next.provider || current.id !== next.id) {
145
179
  // Mirror the startup/cycle path: set the model directly (no auth gate, no settings
146
180
  // persist) so re-applying the profile model behaves like initial resolution rather
@@ -149,11 +183,22 @@ export class ProfileFilterController {
149
183
  // re-renders from session.model.
150
184
  this.deps.getAgent().state.model = next;
151
185
  this.deps.getSessionManager().appendModelChange(next.provider, next.id);
186
+ modelChanged = true;
152
187
  }
153
188
  }
189
+ this._profileControlledModel = !this.deps.isExplicitModel() && Boolean(profileSettings.model);
154
190
  if (!this.deps.isExplicitThinking() && profileSettings.thinkingLevel) {
155
191
  this.deps.setThinkingLevel(profileSettings.thinkingLevel);
156
192
  }
193
+ else if (mustRestoreBaseThinking && baseSelection) {
194
+ this.deps.setThinkingLevel(baseSelection.thinkingLevel);
195
+ }
196
+ else if (modelChanged) {
197
+ // A model-only situation switch still has to clamp the inherited/explicit level. This
198
+ // prevents Sol/Terra Ultra from leaking into Luna or another model that stops at Max.
199
+ this.deps.setThinkingLevel(this.deps.getAgent().state.thinkingLevel);
200
+ }
201
+ this._profileControlledThinking = !this.deps.isExplicitThinking() && Boolean(profileSettings.thinkingLevel);
157
202
  }
158
203
  }
159
204
  //# sourceMappingURL=profile-filter-controller.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"profile-filter-controller.js","sourceRoot":"","sources":["../../src/core/profile-filter-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Resource-profile tool/extension gating + reload-time profile model re-application.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Resolves the active resource\n * profile's tool allow/block filter, filters the runtime extension set (tracking the withheld/inert\n * tallies it owns), reports the /context \"withheld by profile\" observations, and — on reload —\n * re-applies a profile-bound model/thinking level unless an explicit launch flag set them. Owns the\n * inert-extension warnings and profile-denied-extension count; everything else is read through deps.\n */\n\nimport type { Agent, ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { Extension } from \"./extensions/index.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { resolveProfileModelSettings } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport {\n\tmatchesResourceProfilePattern,\n\ttype ResourceProfileFilterSettings,\n\ttype SettingsManager,\n} from \"./settings-manager.ts\";\n\nexport interface ProfileFilterControllerDeps {\n\tgetSettingsManager(): SettingsManager;\n\tgetResourceLoader(): ResourceLoader;\n\tgetModelRegistry(): ModelRegistry;\n\tgetCwd(): string;\n\tgetAgent(): Agent;\n\tgetSessionManager(): SessionManager;\n\t/** Construction-time tool allow-set (undefined = no allow-list gate). */\n\tgetAllowedToolNames(): Set<string> | undefined;\n\t/** Construction-time tool exclude-set. */\n\tgetExcludedToolNames(): Set<string> | undefined;\n\t/** Live resource-profile tool filter (re-derived on reload; may be undefined). */\n\tgetToolProfileFilter(): Required<ResourceProfileFilterSettings> | undefined;\n\t/** Whether the model was set by an explicit launch flag (profile must not clobber it). */\n\tisExplicitModel(): boolean;\n\t/** Whether the thinking level was set by an explicit launch flag. */\n\tisExplicitThinking(): boolean;\n\t/** Apply a thinking level (delegates to model-selection). */\n\tsetThinkingLevel(level: ThinkingLevel): void;\n}\n\nexport class ProfileFilterController {\n\t/** G12: extensions loaded but rendered fully inert by the profile's tools filter. */\n\tprivate _inertExtensionWarnings: string[] = [];\n\t/** Count of extensions withheld by the active resource profile (for the /context observation). */\n\tprivate _profileDeniedExtensionCount = 0;\n\n\tprivate readonly deps: ProfileFilterControllerDeps;\n\n\tconstructor(deps: ProfileFilterControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/** Inert-extension warnings tracked by the last {@link filterExtensionsForRuntime} pass. */\n\tgetInertExtensionWarnings(): string[] {\n\t\treturn this._inertExtensionWarnings;\n\t}\n\n\t/**\n\t * Resolve the active resource-profile tool allow/block filter from current settings.\n\t * Mirrors the construction-time derivation (settingsManager.getResourceProfileFilter(\"tools\"))\n\t * so reload() can re-apply it after a live settings/profile edit.\n\t */\n\tderiveToolProfileFilter(): Required<ResourceProfileFilterSettings> {\n\t\tconst filter = this.deps.getSettingsManager().getResourceProfileFilter(\"tools\");\n\t\treturn { allow: filter.allow ?? [], block: filter.block ?? [] };\n\t}\n\n\tisToolOrCommandAllowedByProfile(name: string): boolean {\n\t\tconst allowedToolNames = this.deps.getAllowedToolNames();\n\t\tconst excludedToolNames = this.deps.getExcludedToolNames();\n\t\tif (allowedToolNames && !allowedToolNames.has(name)) return false;\n\t\tif (excludedToolNames?.has(name)) return false;\n\t\tconst filter = this.deps.getToolProfileFilter();\n\t\tif (!filter) return true;\n\t\tif (filter.allow.length > 0 && !matchesResourceProfilePattern(name, filter.allow)) return false;\n\t\tif (matchesResourceProfilePattern(name, filter.block)) return false;\n\t\treturn true;\n\t}\n\n\tprivate _hasToolOrCommandProfileGate(): boolean {\n\t\tconst toolProfileFilter = this.deps.getToolProfileFilter();\n\t\treturn Boolean(\n\t\t\tthis.deps.getAllowedToolNames() ||\n\t\t\t\tthis.deps.getExcludedToolNames() ||\n\t\t\t\t(toolProfileFilter && (toolProfileFilter.allow.length > 0 || toolProfileFilter.block.length > 0)),\n\t\t);\n\t}\n\n\tfilterExtensionsForRuntime(extensions: Extension[]): Extension[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tthis._inertExtensionWarnings = [];\n\t\tthis._profileDeniedExtensionCount = 0;\n\t\tif (settingsManager.getActiveResourceProfileNames().length === 0) {\n\t\t\tif (settingsManager.hasExplicitActiveResourceProfileSelection()) {\n\t\t\t\t// An explicit profile selection that resolves to no active profile is a deliberate\n\t\t\t\t// deny-all — every extension is withheld by that choice.\n\t\t\t\tthis._profileDeniedExtensionCount = extensions.length;\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\t// No profile in play: only inline/SDK extensions load by default. That is the baseline, not\n\t\t\t// a profile denial, so it is not counted as withheld.\n\t\t\treturn extensions.filter((extension) => extension.sourceInfo.source === \"inline\");\n\t\t}\n\t\tconst hasToolOrCommandGate = this._hasToolOrCommandProfileGate();\n\t\tconst allowedExtensions = extensions.filter((extension) =>\n\t\t\tsettingsManager.isResourceAllowedByProfile(\"extensions\", extension.path, extension.sourceInfo.baseDir),\n\t\t);\n\t\tthis._profileDeniedExtensionCount = extensions.length - allowedExtensions.length;\n\t\treturn allowedExtensions.map((extension) => {\n\t\t\tif (!hasToolOrCommandGate) return extension;\n\t\t\tconst tools = new Map(\n\t\t\t\tArray.from(extension.tools.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\tconst commands = new Map(\n\t\t\t\tArray.from(extension.commands.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\t// G12: an extension the profile ALLOWS whose every tool and command the tools filter\n\t\t\t// then denies loads and runs lifecycle hooks but is completely uninvocable — surface\n\t\t\t// it instead of presenting a \"loaded\" extension that silently does nothing.\n\t\t\tif (extension.tools.size + extension.commands.size > 0 && tools.size + commands.size === 0) {\n\t\t\t\tconst name = extension.path.split(/[\\\\/]/).pop() ?? extension.path;\n\t\t\t\tthis._inertExtensionWarnings.push(\n\t\t\t\t\t`extension \"${name}\" is loaded but fully inert: the active profile's tools filter denies all ${extension.tools.size} tool(s) and ${extension.commands.size} command(s) it contributes`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn { ...extension, tools, commands };\n\t\t});\n\t}\n\n\t/**\n\t * /context observations for skills/prompts/extensions the active resource profile removed from\n\t * listings — the analog of the withheld-AGENTS.md warning. Strict UAC makes these silently absent,\n\t * so a lean profile's effect on the resource surface stays visible. Counts are profile-scoped\n\t * (skills/prompts via the profile-independent discovery universe filtered by the live profile\n\t * filter; extensions via the runtime filter's denied tally). Empty when nothing is withheld.\n\t *\n\t * Uses `isResourceDeniedByActiveProfile` (profile-only), not `isResourceAllowedByProfile` (which\n\t * also folds in the user's own legacy `disabledResources` list): a plain user-disabled resource\n\t * must never be misattributed to \"the active resource profile\" — that case is already surfaced by\n\t * the G14 disable-wins warning. With no active profile at all, the helper always reports nothing\n\t * denied, so this naturally stays silent (extensions keep their own runtime-filter-derived count,\n\t * which is already correctly zero absent a profile).\n\t */\n\tprofileDeniedResourceObservations(): string[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst resourceLoader = this.deps.getResourceLoader();\n\t\tconst cwd = this.deps.getCwd();\n\t\tconst observations: string[] = [];\n\t\tconst withheld = (kind: \"skills\" | \"prompts\", paths: string[]): number =>\n\t\t\tpaths.filter((path) => settingsManager.isResourceDeniedByActiveProfile(kind, path, cwd)).length;\n\n\t\tconst skillsWithheld = withheld(\"skills\", resourceLoader.getDiscoverableSkillPaths());\n\t\tif (skillsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${skillsWithheld} skill(s) withheld by the active resource profile — grant the \"skills\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tconst promptsWithheld = withheld(\"prompts\", resourceLoader.getDiscoverablePromptPaths());\n\t\tif (promptsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${promptsWithheld} prompt(s) withheld by the active resource profile — grant the \"prompts\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tif (this._profileDeniedExtensionCount > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${this._profileDeniedExtensionCount} extension(s) withheld by the active resource profile — grant the \"extensions\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\treturn observations;\n\t}\n\n\t/**\n\t * Re-resolve the active resource profile's model/thinking from current settings and apply it.\n\t * Only acts when the profile actually binds model/thinking AND that field was not set by an\n\t * explicit launch flag — so live profile edits apply on reload without clobbering an explicit\n\t * --model/--thinking. A no-op for profiles that don't bind a model.\n\t */\n\tasync reapplyActiveProfileModelSettings(): Promise<void> {\n\t\tif (this.deps.isExplicitModel() && this.deps.isExplicitThinking()) return;\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst activeProfileNames = settingsManager.getActiveResourceProfileNames();\n\t\tif (activeProfileNames.length === 0) return;\n\t\tconst profileSettings = resolveProfileModelSettings({\n\t\t\tactiveProfileNames,\n\t\t\tregistry: settingsManager.getProfileRegistry(),\n\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\tcwd: this.deps.getCwd(),\n\t\t});\n\t\tif (!this.deps.isExplicitModel() && profileSettings.model) {\n\t\t\tconst current = this.deps.getAgent().state.model;\n\t\t\tconst next = profileSettings.model;\n\t\t\tif (!current || current.provider !== next.provider || current.id !== next.id) {\n\t\t\t\t// Mirror the startup/cycle path: set the model directly (no auth gate, no settings\n\t\t\t\t// persist) so re-applying the profile model behaves like initial resolution rather\n\t\t\t\t// than a runtime model switch. No model_select emit here — reload rebuilds the\n\t\t\t\t// extension runtime and emits session_start(\"reload\") right after, and the UI\n\t\t\t\t// re-renders from session.model.\n\t\t\t\tthis.deps.getAgent().state.model = next;\n\t\t\t\tthis.deps.getSessionManager().appendModelChange(next.provider, next.id);\n\t\t\t}\n\t\t}\n\t\tif (!this.deps.isExplicitThinking() && profileSettings.thinkingLevel) {\n\t\t\tthis.deps.setThinkingLevel(profileSettings.thinkingLevel);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"profile-filter-controller.js","sourceRoot":"","sources":["../../src/core/profile-filter-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Resource-profile tool/extension gating + reload-time profile model re-application.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Resolves the active resource\n * profile's tool allow/block filter, filters the runtime extension set (tracking the withheld/inert\n * tallies it owns), reports the /context \"withheld by profile\" observations, and — on reload —\n * re-applies a profile-bound model/thinking level unless an explicit launch flag set them. Owns the\n * inert-extension warnings and profile-denied-extension count; everything else is read through deps.\n */\n\nimport type { Agent, ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport type { SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { Extension } from \"./extensions/index.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel, resolveProfileModelSettings } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport {\n\tmatchesResourceProfilePattern,\n\ttype ResourceProfileFilterSettings,\n\ttype SettingsManager,\n} from \"./settings-manager.ts\";\n\nexport interface ProfileFilterControllerDeps {\n\tgetSettingsManager(): SettingsManager;\n\tgetResourceLoader(): ResourceLoader;\n\tgetModelRegistry(): ModelRegistry;\n\tgetCwd(): string;\n\tgetAgent(): Agent;\n\tgetSessionManager(): SessionManager;\n\t/** Construction-time tool allow-set (undefined = no allow-list gate). */\n\tgetAllowedToolNames(): Set<string> | undefined;\n\t/** Construction-time tool exclude-set. */\n\tgetExcludedToolNames(): Set<string> | undefined;\n\t/** Live resource-profile tool filter (re-derived on reload; may be undefined). */\n\tgetToolProfileFilter(): Required<ResourceProfileFilterSettings> | undefined;\n\t/** Whether the model was set by an explicit launch flag (profile must not clobber it). */\n\tisExplicitModel(): boolean;\n\t/** Whether the thinking level was set by an explicit launch flag. */\n\tisExplicitThinking(): boolean;\n\t/** Apply a thinking level (delegates to model-selection). */\n\tsetThinkingLevel(level: ThinkingLevel): void;\n}\n\nexport interface ProfileFilterReloadSnapshot {\n\tinertExtensionWarnings: string[];\n\tprofileDeniedExtensionCount: number;\n\tprofileControlledModel: boolean;\n\tprofileControlledThinking: boolean;\n}\n\nexport class ProfileFilterController {\n\t/** G12: extensions loaded but rendered fully inert by the profile's tools filter. */\n\tprivate _inertExtensionWarnings: string[] = [];\n\t/** Count of extensions withheld by the active resource profile (for the /context observation). */\n\tprivate _profileDeniedExtensionCount = 0;\n\t/** Whether the last applied profile generation, rather than the base session selection, owns the field. */\n\tprivate _profileControlledModel = false;\n\tprivate _profileControlledThinking = false;\n\n\tprivate readonly deps: ProfileFilterControllerDeps;\n\n\tconstructor(deps: ProfileFilterControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/** Inert-extension warnings tracked by the last {@link filterExtensionsForRuntime} pass. */\n\tgetInertExtensionWarnings(): string[] {\n\t\treturn this._inertExtensionWarnings;\n\t}\n\n\tcreateReloadSnapshot(): ProfileFilterReloadSnapshot {\n\t\treturn {\n\t\t\tinertExtensionWarnings: [...this._inertExtensionWarnings],\n\t\t\tprofileDeniedExtensionCount: this._profileDeniedExtensionCount,\n\t\t\tprofileControlledModel: this._profileControlledModel,\n\t\t\tprofileControlledThinking: this._profileControlledThinking,\n\t\t};\n\t}\n\n\trestoreReloadSnapshot(snapshot: ProfileFilterReloadSnapshot): void {\n\t\tthis._inertExtensionWarnings = [...snapshot.inertExtensionWarnings];\n\t\tthis._profileDeniedExtensionCount = snapshot.profileDeniedExtensionCount;\n\t\tthis._profileControlledModel = snapshot.profileControlledModel;\n\t\tthis._profileControlledThinking = snapshot.profileControlledThinking;\n\t}\n\n\t/**\n\t * Resolve the active resource-profile tool allow/block filter from current settings.\n\t * Mirrors the construction-time derivation (settingsManager.getResourceProfileFilter(\"tools\"))\n\t * so reload() can re-apply it after a live settings/profile edit.\n\t */\n\tderiveToolProfileFilter(): Required<ResourceProfileFilterSettings> {\n\t\tconst filter = this.deps.getSettingsManager().getResourceProfileFilter(\"tools\");\n\t\treturn { allow: filter.allow ?? [], block: filter.block ?? [] };\n\t}\n\n\tisToolOrCommandAllowedByProfile(name: string): boolean {\n\t\tconst allowedToolNames = this.deps.getAllowedToolNames();\n\t\tconst excludedToolNames = this.deps.getExcludedToolNames();\n\t\tif (allowedToolNames && !allowedToolNames.has(name)) return false;\n\t\tif (excludedToolNames?.has(name)) return false;\n\t\tconst filter = this.deps.getToolProfileFilter();\n\t\tif (!filter) return true;\n\t\tif (filter.allow.length > 0 && !matchesResourceProfilePattern(name, filter.allow)) return false;\n\t\tif (matchesResourceProfilePattern(name, filter.block)) return false;\n\t\treturn true;\n\t}\n\n\tprivate _hasToolOrCommandProfileGate(): boolean {\n\t\tconst toolProfileFilter = this.deps.getToolProfileFilter();\n\t\treturn Boolean(\n\t\t\tthis.deps.getAllowedToolNames() ||\n\t\t\t\tthis.deps.getExcludedToolNames() ||\n\t\t\t\t(toolProfileFilter && (toolProfileFilter.allow.length > 0 || toolProfileFilter.block.length > 0)),\n\t\t);\n\t}\n\n\tfilterExtensionsForRuntime(extensions: Extension[]): Extension[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tthis._inertExtensionWarnings = [];\n\t\tthis._profileDeniedExtensionCount = 0;\n\t\tif (settingsManager.getActiveResourceProfileNames().length === 0) {\n\t\t\tif (settingsManager.hasExplicitActiveResourceProfileSelection()) {\n\t\t\t\t// An explicit profile selection that resolves to no active profile is a deliberate\n\t\t\t\t// deny-all — every extension is withheld by that choice.\n\t\t\t\tthis._profileDeniedExtensionCount = extensions.length;\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\t// No profile in play: only inline/SDK extensions load by default. That is the baseline, not\n\t\t\t// a profile denial, so it is not counted as withheld.\n\t\t\treturn extensions.filter((extension) => extension.sourceInfo.source === \"inline\");\n\t\t}\n\t\tconst hasToolOrCommandGate = this._hasToolOrCommandProfileGate();\n\t\tconst allowedExtensions = extensions.filter((extension) =>\n\t\t\tsettingsManager.isResourceAllowedByProfile(\"extensions\", extension.path, extension.sourceInfo.baseDir),\n\t\t);\n\t\tthis._profileDeniedExtensionCount = extensions.length - allowedExtensions.length;\n\t\treturn allowedExtensions.map((extension) => {\n\t\t\tif (!hasToolOrCommandGate) return extension;\n\t\t\tconst tools = new Map(\n\t\t\t\tArray.from(extension.tools.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\tconst commands = new Map(\n\t\t\t\tArray.from(extension.commands.entries()).filter(([name]) => this.isToolOrCommandAllowedByProfile(name)),\n\t\t\t);\n\t\t\t// G12: an extension the profile ALLOWS whose every tool and command the tools filter\n\t\t\t// then denies loads and runs lifecycle hooks but is completely uninvocable — surface\n\t\t\t// it instead of presenting a \"loaded\" extension that silently does nothing.\n\t\t\tif (extension.tools.size + extension.commands.size > 0 && tools.size + commands.size === 0) {\n\t\t\t\tconst name = extension.path.split(/[\\\\/]/).pop() ?? extension.path;\n\t\t\t\tthis._inertExtensionWarnings.push(\n\t\t\t\t\t`extension \"${name}\" is loaded but fully inert: the active profile's tools filter denies all ${extension.tools.size} tool(s) and ${extension.commands.size} command(s) it contributes`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn { ...extension, tools, commands };\n\t\t});\n\t}\n\n\t/**\n\t * /context observations for skills/prompts/extensions the active resource profile removed from\n\t * listings — the analog of the withheld-AGENTS.md warning. Strict UAC makes these silently absent,\n\t * so a lean profile's effect on the resource surface stays visible. Counts are profile-scoped\n\t * (skills/prompts via the profile-independent discovery universe filtered by the live profile\n\t * filter; extensions via the runtime filter's denied tally). Empty when nothing is withheld.\n\t *\n\t * Uses `isResourceDeniedByActiveProfile` (profile-only), not `isResourceAllowedByProfile` (which\n\t * also folds in the user's own legacy `disabledResources` list): a plain user-disabled resource\n\t * must never be misattributed to \"the active resource profile\" — that case is already surfaced by\n\t * the G14 disable-wins warning. With no active profile at all, the helper always reports nothing\n\t * denied, so this naturally stays silent (extensions keep their own runtime-filter-derived count,\n\t * which is already correctly zero absent a profile).\n\t */\n\tprofileDeniedResourceObservations(): string[] {\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst resourceLoader = this.deps.getResourceLoader();\n\t\tconst cwd = this.deps.getCwd();\n\t\tconst observations: string[] = [];\n\t\tconst withheld = (kind: \"skills\" | \"prompts\", paths: string[]): number =>\n\t\t\tpaths.filter((path) => settingsManager.isResourceDeniedByActiveProfile(kind, path, cwd)).length;\n\n\t\tconst skillsWithheld = withheld(\"skills\", resourceLoader.getDiscoverableSkillPaths());\n\t\tif (skillsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${skillsWithheld} skill(s) withheld by the active resource profile — grant the \"skills\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tconst promptsWithheld = withheld(\"prompts\", resourceLoader.getDiscoverablePromptPaths());\n\t\tif (promptsWithheld > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${promptsWithheld} prompt(s) withheld by the active resource profile — grant the \"prompts\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\tif (this._profileDeniedExtensionCount > 0) {\n\t\t\tobservations.push(\n\t\t\t\t`${this._profileDeniedExtensionCount} extension(s) withheld by the active resource profile — grant the \"extensions\" kind to restore them`,\n\t\t\t);\n\t\t}\n\t\treturn observations;\n\t}\n\n\t/**\n\t * Re-resolve the active resource profile's model/thinking from current settings and apply it.\n\t * Only acts when the profile actually binds model/thinking AND that field was not set by an\n\t * explicit launch flag — so live profile edits apply on reload without clobbering an explicit\n\t * --model/--thinking. A no-op for profiles that don't bind a model.\n\t */\n\tasync reapplyActiveProfileModelSettings(): Promise<void> {\n\t\tif (this.deps.isExplicitModel() && this.deps.isExplicitThinking()) return;\n\t\tconst settingsManager = this.deps.getSettingsManager();\n\t\tconst activeProfileNames = settingsManager.getActiveResourceProfileNames();\n\t\tconst profileSettings =\n\t\t\tactiveProfileNames.length > 0\n\t\t\t\t? resolveProfileModelSettings({\n\t\t\t\t\t\tactiveProfileNames,\n\t\t\t\t\t\tregistry: settingsManager.getProfileRegistry(),\n\t\t\t\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\t\t\t\tcwd: this.deps.getCwd(),\n\t\t\t\t\t})\n\t\t\t\t: {};\n\t\tif (profileSettings.error) {\n\t\t\tthrow new Error(`Profile model resolution error: ${profileSettings.error}`);\n\t\t}\n\t\tconst mustRestoreBaseModel =\n\t\t\t!this.deps.isExplicitModel() && this._profileControlledModel && !profileSettings.model;\n\t\tconst mustRestoreBaseThinking =\n\t\t\t!this.deps.isExplicitThinking() && this._profileControlledThinking && !profileSettings.thinkingLevel;\n\t\tconst baseSelection =\n\t\t\tmustRestoreBaseModel || mustRestoreBaseThinking\n\t\t\t\t? await findInitialModel({\n\t\t\t\t\t\tscopedModels: [],\n\t\t\t\t\t\tisContinuing: true,\n\t\t\t\t\t\tdefaultProvider: settingsManager.getDefaultProvider(),\n\t\t\t\t\t\tdefaultModelId: settingsManager.getDefaultModel(),\n\t\t\t\t\t\tdefaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n\t\t\t\t\t\tmodelRegistry: this.deps.getModelRegistry(),\n\t\t\t\t\t})\n\t\t\t\t: undefined;\n\t\tlet modelChanged = false;\n\t\tconst nextModel = profileSettings.model ?? (mustRestoreBaseModel ? baseSelection?.model : undefined);\n\t\tif (!this.deps.isExplicitModel() && nextModel) {\n\t\t\tconst current = this.deps.getAgent().state.model;\n\t\t\tconst next = nextModel;\n\t\t\tif (!current || current.provider !== next.provider || current.id !== next.id) {\n\t\t\t\t// Mirror the startup/cycle path: set the model directly (no auth gate, no settings\n\t\t\t\t// persist) so re-applying the profile model behaves like initial resolution rather\n\t\t\t\t// than a runtime model switch. No model_select emit here — reload rebuilds the\n\t\t\t\t// extension runtime and emits session_start(\"reload\") right after, and the UI\n\t\t\t\t// re-renders from session.model.\n\t\t\t\tthis.deps.getAgent().state.model = next;\n\t\t\t\tthis.deps.getSessionManager().appendModelChange(next.provider, next.id);\n\t\t\t\tmodelChanged = true;\n\t\t\t}\n\t\t}\n\t\tthis._profileControlledModel = !this.deps.isExplicitModel() && Boolean(profileSettings.model);\n\t\tif (!this.deps.isExplicitThinking() && profileSettings.thinkingLevel) {\n\t\t\tthis.deps.setThinkingLevel(profileSettings.thinkingLevel);\n\t\t} else if (mustRestoreBaseThinking && baseSelection) {\n\t\t\tthis.deps.setThinkingLevel(baseSelection.thinkingLevel);\n\t\t} else if (modelChanged) {\n\t\t\t// A model-only situation switch still has to clamp the inherited/explicit level. This\n\t\t\t// prevents Sol/Terra Ultra from leaking into Luna or another model that stops at Max.\n\t\t\tthis.deps.setThinkingLevel(this.deps.getAgent().state.thinkingLevel);\n\t\t}\n\t\tthis._profileControlledThinking = !this.deps.isExplicitThinking() && Boolean(profileSettings.thinkingLevel);\n\t}\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { ThinkingLevel } from "@caupulican/pi-agent-core";
2
- import type { ModelRouterSettings, ResourceProfileSettings, Settings } from "./settings-manager.ts";
2
+ import type { ModelRouterSettings, ProfileDefinitionInput, ResourceProfileSettings, Settings } from "./settings-manager.ts";
3
3
  export type ProfileSource = "global-settings" | "project-settings" | "external-settings" | "profile-file" | "directory-overlay" | "inline" | "embedded" | "bundle";
4
4
  export interface NormalizedProfile {
5
5
  name: string;
@@ -23,7 +23,7 @@ export interface ProfileRegistryOptions {
23
23
  globalSettings: Settings;
24
24
  projectSettings: Settings;
25
25
  directoryProfileSettings: Settings;
26
- inlineResourceProfileDefinitions: Record<string, ResourceProfileSettings>;
26
+ inlineResourceProfileDefinitions: Record<string, ProfileDefinitionInput>;
27
27
  discoveredResourceProfileDefinitions: Record<string, ResourceProfileSettings>;
28
28
  profilesDir?: string;
29
29
  externalResourceRoots?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"profile-registry.d.ts","sourceRoot":"","sources":["../../src/core/profile-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAM/D,OAAO,KAAK,EACX,mBAAmB,EAEnB,uBAAuB,EACvB,QAAQ,EACR,MAAM,uBAAuB,CAAC;AAG/B,MAAM,MAAM,aAAa,GACtB,iBAAiB,GACjB,kBAAkB,GAClB,mBAAmB,GACnB,cAAc,GACd,mBAAmB,GACnB,QAAQ,GACR,UAAU,GACV,QAAQ,CAAC;AAEZ,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,8FAA8F;IAC9F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACzC,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACtC,cAAc,EAAE,QAAQ,CAAC;IACzB,eAAe,EAAE,QAAQ,CAAC;IAC1B,wBAAwB,EAAE,QAAQ,CAAC;IACnC,gCAAgC,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;IAC1E,oCAAoC,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAyKD,qBAAa,eAAe;IAC3B,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,WAAW,CAAmC;IAEtD,YAAY,OAAO,EAAE,sBAAsB,EAE1C;IAED,eAAe,IAAI,yBAAyB,EAAE,CAG7C;IAED,YAAY,IAAI,iBAAiB,EAAE,CAgBlC;IAED,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAItD;IAED,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAQ7E;IAED,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,gBAAgB;IA8CxB,OAAO,CAAC,4BAA4B;IAQpC,OAAO,CAAC,eAAe;CAuBvB","sourcesContent":["import type { ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join, resolve } from \"path\";\nimport { isValidThinkingLevel } from \"../cli/args.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { mergeResourceProfileSettings } from \"./resource-profile-blocks.ts\";\nimport type {\n\tModelRouterSettings,\n\tResourceProfileKind,\n\tResourceProfileSettings,\n\tSettings,\n} from \"./settings-manager.ts\";\nimport { validateSkillName } from \"./skills.ts\";\n\nexport type ProfileSource =\n\t| \"global-settings\"\n\t| \"project-settings\"\n\t| \"external-settings\"\n\t| \"profile-file\"\n\t| \"directory-overlay\"\n\t| \"inline\"\n\t| \"embedded\"\n\t| \"bundle\";\n\nexport interface NormalizedProfile {\n\tname: string;\n\tdescription?: string;\n\tmodel?: string;\n\tthinking?: ThinkingLevel;\n\tmodelRouter?: ModelRouterSettings;\n\t/** Situational identity injected into the system prompt while this profile is active (R6). */\n\tsoul?: string;\n\tresources: ResourceProfileSettings;\n\tsource: ProfileSource;\n\tsourcePath?: string;\n\tbaseDir?: string;\n}\n\nexport interface ProfileRegistryDiagnostic {\n\tsource: ProfileSource;\n\tpath?: string;\n\tmessage: string;\n}\n\nexport interface ProfileRegistryOptions {\n\tglobalSettings: Settings;\n\tprojectSettings: Settings;\n\tdirectoryProfileSettings: Settings;\n\tinlineResourceProfileDefinitions: Record<string, ResourceProfileSettings>;\n\tdiscoveredResourceProfileDefinitions: Record<string, ResourceProfileSettings>;\n\tprofilesDir?: string;\n\texternalResourceRoots?: string[];\n}\n\nconst RESOURCE_PROFILE_KINDS: ResourceProfileKind[] = [\"extensions\", \"skills\", \"prompts\", \"themes\", \"agents\", \"tools\"];\n\ninterface ProfileCandidate {\n\tprofile: NormalizedProfile;\n\tprecedence: number;\n\torder: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction asNonEmptyString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" && value.trim().length > 0 ? value.trim() : undefined;\n}\n\nfunction asStringArray(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\tconst strings = value.filter((item): item is string => typeof item === \"string\" && item.trim().length > 0);\n\treturn strings.length > 0 ? strings : undefined;\n}\n\nfunction shouldResolveAgainstBaseDir(pattern: string): boolean {\n\treturn pattern.startsWith(\"./\") || pattern.startsWith(\"../\");\n}\n\nfunction normalizePattern(pattern: string, baseDir: string | undefined): string {\n\tconst trimmed = pattern.trim();\n\tif (!baseDir || !shouldResolveAgainstBaseDir(trimmed)) return trimmed;\n\treturn resolvePath(trimmed, baseDir, { trim: true });\n}\n\nfunction normalizeStringArray(value: unknown, baseDir: string | undefined): string[] | undefined {\n\tconst strings = asStringArray(value);\n\tif (!strings) return undefined;\n\treturn strings.map((pattern) => normalizePattern(pattern, baseDir));\n}\n\nfunction normalizeResourceProfileSettings(value: unknown, baseDir: string | undefined): ResourceProfileSettings {\n\tif (!isRecord(value)) {\n\t\tthrow new Error(\"resources must be an object\");\n\t}\n\tconst result: ResourceProfileSettings = {};\n\tfor (const kind of RESOURCE_PROFILE_KINDS) {\n\t\tconst filterValue = value[kind];\n\t\tif (filterValue === undefined) continue;\n\t\tif (!isRecord(filterValue)) {\n\t\t\tthrow new Error(`${kind} filter must be an object`);\n\t\t}\n\t\tconst allow = normalizeStringArray(filterValue.allow, baseDir);\n\t\tconst block = normalizeStringArray(filterValue.block, baseDir);\n\t\tresult[kind] = { allow, block };\n\t}\n\treturn result;\n}\n\nfunction validateProfileName(name: string): string[] {\n\treturn validateSkillName(name);\n}\n\nfunction normalizeThinking(value: unknown): ThinkingLevel | undefined {\n\tconst thinking = asNonEmptyString(value);\n\tif (!thinking) return undefined;\n\tif (!isValidThinkingLevel(thinking)) {\n\t\tthrow new Error(`thinking must be one of off, minimal, low, medium, high, xhigh`);\n\t}\n\treturn thinking;\n}\n\nfunction normalizeModelRouterSettings(value: unknown): ModelRouterSettings | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst settings: ModelRouterSettings = {};\n\tif (typeof value.enabled === \"boolean\") settings.enabled = value.enabled;\n\tfor (const key of [\"cheapModel\", \"mediumModel\", \"expensiveModel\", \"learningModel\"] as const) {\n\t\tconst candidate = asNonEmptyString(value[key]);\n\t\tif (candidate) settings[key] = candidate;\n\t}\n\treturn Object.keys(settings).length > 0 ? settings : undefined;\n}\n\nfunction normalizeWrapperProfile(options: {\n\tvalue: unknown;\n\tsource: ProfileSource;\n\tsourcePath?: string;\n\tbaseDir?: string;\n\tfallbackName?: string;\n}): NormalizedProfile {\n\tif (!isRecord(options.value)) {\n\t\tthrow new Error(\"profile JSON must be an object\");\n\t}\n\tconst name = asNonEmptyString(options.value.name) ?? options.fallbackName;\n\tif (!name) {\n\t\tthrow new Error(\"profile name is required\");\n\t}\n\tconst nameErrors = validateProfileName(name);\n\tif (nameErrors.length > 0) {\n\t\tthrow new Error(`invalid profile name \"${name}\": ${nameErrors.join(\", \")}`);\n\t}\n\tconst resources = normalizeResourceProfileSettings(options.value.resources ?? {}, options.baseDir);\n\tconst description = asNonEmptyString(options.value.description);\n\tconst model = asNonEmptyString(options.value.model);\n\tconst thinking = normalizeThinking(options.value.thinking);\n\tconst modelRouter = normalizeModelRouterSettings(options.value.modelRouter);\n\tconst soul = asNonEmptyString(options.value.soul);\n\treturn {\n\t\tname,\n\t\tdescription,\n\t\tmodel,\n\t\tthinking,\n\t\tmodelRouter,\n\t\tsoul,\n\t\tresources,\n\t\tsource: options.source,\n\t\tsourcePath: options.sourcePath,\n\t\tbaseDir: options.baseDir,\n\t};\n}\n\nfunction normalizeSettingsProfiles(\n\tsettings: Settings,\n\tsource: ProfileSource,\n\tbaseDir?: string,\n\tsourcePath?: string,\n): Array<Omit<NormalizedProfile, \"source\"> & { source?: ProfileSource }> {\n\tconst profiles: Array<Omit<NormalizedProfile, \"source\"> & { source?: ProfileSource }> = [];\n\tfor (const [name, resources] of Object.entries(settings.resourceProfiles ?? {})) {\n\t\tconst nameErrors = validateProfileName(name);\n\t\tif (nameErrors.length > 0) continue;\n\t\tprofiles.push({\n\t\t\tname,\n\t\t\tresources: mergeResourceProfileSettings(undefined, resources),\n\t\t\tsourcePath,\n\t\t\tbaseDir,\n\t\t});\n\t}\n\treturn profiles.map((profile) => ({ ...profile, source }));\n}\n\nfunction loadSettingsFileProfiles(sourcePath: string, source: ProfileSource): NormalizedProfile[] {\n\ttry {\n\t\tconst stats = statSync(sourcePath);\n\t\tif (!stats.isFile()) return [];\n\t\tconst parsed = JSON.parse(readFileSync(sourcePath, \"utf-8\")) as Settings;\n\t\treturn normalizeSettingsProfiles(parsed, source, dirname(resolve(sourcePath)), resolve(sourcePath)).map(\n\t\t\t(profile) => ({\n\t\t\t\t...profile,\n\t\t\t\tsource,\n\t\t\t}),\n\t\t);\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction normalizeDefinitions(\n\tdefinitions: Record<string, ResourceProfileSettings>,\n\tsource: ProfileSource,\n): NormalizedProfile[] {\n\tconst profiles: NormalizedProfile[] = [];\n\tfor (const [name, resources] of Object.entries(definitions)) {\n\t\tconst nameErrors = validateProfileName(name);\n\t\tif (nameErrors.length > 0) continue;\n\t\tprofiles.push({ name, resources: mergeResourceProfileSettings(undefined, resources), source });\n\t}\n\treturn profiles;\n}\n\nexport class ProfileRegistry {\n\tprivate options: ProfileRegistryOptions;\n\tprivate diagnostics: ProfileRegistryDiagnostic[] = [];\n\n\tconstructor(options: ProfileRegistryOptions) {\n\t\tthis.options = options;\n\t}\n\n\tlistDiagnostics(): ProfileRegistryDiagnostic[] {\n\t\tthis.collectCandidates();\n\t\treturn [...this.diagnostics];\n\t}\n\n\tlistProfiles(): NormalizedProfile[] {\n\t\tconst candidates = this.collectCandidates();\n\t\tconst winners = new Map<string, ProfileCandidate>();\n\t\tfor (const candidate of candidates) {\n\t\t\tconst existing = winners.get(candidate.profile.name);\n\t\t\tif (\n\t\t\t\t!existing ||\n\t\t\t\tcandidate.precedence < existing.precedence ||\n\t\t\t\t(candidate.precedence === existing.precedence && candidate.order < existing.order)\n\t\t\t) {\n\t\t\t\twinners.set(candidate.profile.name, candidate);\n\t\t\t}\n\t\t}\n\t\treturn Array.from(winners.values())\n\t\t\t.sort((a, b) => a.profile.name.localeCompare(b.profile.name))\n\t\t\t.map((candidate) => candidate.profile);\n\t}\n\n\tgetProfile(name: string): NormalizedProfile | undefined {\n\t\tconst trimmed = name.trim();\n\t\tif (!trimmed) return undefined;\n\t\treturn this.listProfiles().find((profile) => profile.name === trimmed);\n\t}\n\n\tresolveProfileRef(ref: string, fromDir: string): NormalizedProfile | undefined {\n\t\tconst trimmed = ref.trim();\n\t\tif (!trimmed) return undefined;\n\t\tif (trimmed.startsWith(\"./\") || trimmed.startsWith(\"../\")) {\n\t\t\tconst sourcePath = resolvePath(trimmed, fromDir, { trim: true });\n\t\t\treturn this.loadProfileFile(sourcePath, \"profile-file\", 0)?.profile;\n\t\t}\n\t\treturn this.getProfile(trimmed);\n\t}\n\n\tprivate collectCandidates(): ProfileCandidate[] {\n\t\tthis.diagnostics = [];\n\t\tconst candidates: ProfileCandidate[] = [];\n\t\tlet order = 0;\n\t\tconst add = (profile: NormalizedProfile, precedence: number): void => {\n\t\t\tcandidates.push({ profile, precedence, order: order++ });\n\t\t};\n\n\t\tfor (const profile of normalizeDefinitions(this.options.inlineResourceProfileDefinitions, \"inline\")) {\n\t\t\tadd(profile, 1);\n\t\t}\n\t\tfor (const profile of normalizeSettingsProfiles(this.options.directoryProfileSettings, \"directory-overlay\")) {\n\t\t\tadd({ ...profile, source: \"directory-overlay\" }, 2);\n\t\t}\n\t\tfor (const profile of normalizeSettingsProfiles(this.options.projectSettings, \"project-settings\")) {\n\t\t\tadd({ ...profile, source: \"project-settings\" }, 3);\n\t\t}\n\t\tfor (const profile of this.loadProfileFiles()) {\n\t\t\tadd(profile.profile, profile.precedence);\n\t\t}\n\t\tfor (const profile of this.loadExternalSettingsProfiles()) {\n\t\t\tadd(profile, 4.2);\n\t\t}\n\t\tfor (const profile of normalizeSettingsProfiles(this.options.globalSettings, \"global-settings\")) {\n\t\t\tadd({ ...profile, source: \"global-settings\" }, 5);\n\t\t}\n\t\tfor (const profile of normalizeDefinitions(this.options.discoveredResourceProfileDefinitions, \"embedded\")) {\n\t\t\tadd(profile, 6);\n\t\t}\n\n\t\treturn candidates;\n\t}\n\n\tprivate loadProfileFiles(): ProfileCandidate[] {\n\t\tconst candidates: ProfileCandidate[] = [];\n\t\tlet order = 0;\n\n\t\tconst profilesDir = this.options.profilesDir;\n\t\tif (profilesDir && existsSync(profilesDir)) {\n\t\t\ttry {\n\t\t\t\tconst entries = readdirSync(profilesDir)\n\t\t\t\t\t.filter((entry) => entry.endsWith(\".json\"))\n\t\t\t\t\t.sort();\n\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\tconst sourcePath = join(profilesDir, entry);\n\t\t\t\t\tconst loaded = this.loadProfileFile(sourcePath, \"profile-file\", order++);\n\t\t\t\t\tif (loaded) {\n\t\t\t\t\t\tcandidates.push({ ...loaded, precedence: 4 });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis.diagnostics.push({ source: \"profile-file\", path: profilesDir, message: String(error) });\n\t\t\t}\n\t\t}\n\n\t\tconst externalRoots = this.options.externalResourceRoots ?? [];\n\t\tfor (const root of externalRoots) {\n\t\t\tconst extProfilesDir = join(root, \"profiles\");\n\t\t\tif (existsSync(extProfilesDir)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst entries = readdirSync(extProfilesDir)\n\t\t\t\t\t\t.filter((entry) => entry.endsWith(\".json\"))\n\t\t\t\t\t\t.sort();\n\t\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\t\tconst sourcePath = join(extProfilesDir, entry);\n\t\t\t\t\t\tconst loaded = this.loadProfileFile(sourcePath, \"profile-file\", order++);\n\t\t\t\t\t\tif (loaded) {\n\t\t\t\t\t\t\tcandidates.push({ ...loaded, precedence: 4.1 });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.diagnostics.push({ source: \"profile-file\", path: extProfilesDir, message: String(error) });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn candidates;\n\t}\n\n\tprivate loadExternalSettingsProfiles(): NormalizedProfile[] {\n\t\tconst profiles: NormalizedProfile[] = [];\n\t\tfor (const root of this.options.externalResourceRoots ?? []) {\n\t\t\tprofiles.push(...loadSettingsFileProfiles(join(root, \"settings.json\"), \"external-settings\"));\n\t\t}\n\t\treturn profiles;\n\t}\n\n\tprivate loadProfileFile(sourcePath: string, source: ProfileSource, order: number): ProfileCandidate | undefined {\n\t\ttry {\n\t\t\tconst stats = statSync(sourcePath);\n\t\t\tif (!stats.isFile()) return undefined;\n\t\t\tconst parsed = JSON.parse(readFileSync(sourcePath, \"utf-8\"));\n\t\t\tconst fallbackName = basename(sourcePath, \".json\");\n\t\t\treturn {\n\t\t\t\tprofile: normalizeWrapperProfile({\n\t\t\t\t\tvalue: parsed,\n\t\t\t\t\tsource,\n\t\t\t\t\tsourcePath: resolve(sourcePath),\n\t\t\t\t\tbaseDir: dirname(resolve(sourcePath)),\n\t\t\t\t\tfallbackName,\n\t\t\t\t}),\n\t\t\t\tprecedence: 0,\n\t\t\t\torder,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthis.diagnostics.push({ source, path: sourcePath, message });\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"profile-registry.d.ts","sourceRoot":"","sources":["../../src/core/profile-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAM/D,OAAO,KAAK,EACX,mBAAmB,EACnB,sBAAsB,EAEtB,uBAAuB,EACvB,QAAQ,EACR,MAAM,uBAAuB,CAAC;AAG/B,MAAM,MAAM,aAAa,GACtB,iBAAiB,GACjB,kBAAkB,GAClB,mBAAmB,GACnB,cAAc,GACd,mBAAmB,GACnB,QAAQ,GACR,UAAU,GACV,QAAQ,CAAC;AAEZ,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,8FAA8F;IAC9F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACzC,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACtC,cAAc,EAAE,QAAQ,CAAC;IACzB,eAAe,EAAE,QAAQ,CAAC;IAC1B,wBAAwB,EAAE,QAAQ,CAAC;IACnC,gCAAgC,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IACzE,oCAAoC,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAkOD,qBAAa,eAAe;IAC3B,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,WAAW,CAAmC;IAEtD,YAAY,OAAO,EAAE,sBAAsB,EAE1C;IAED,eAAe,IAAI,yBAAyB,EAAE,CAG7C;IAED,YAAY,IAAI,iBAAiB,EAAE,CAgBlC;IAED,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAItD;IAED,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAQ7E;IAED,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,gBAAgB;IA8CxB,OAAO,CAAC,4BAA4B;IAQpC,OAAO,CAAC,eAAe;CAuBvB","sourcesContent":["import type { ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join, resolve } from \"path\";\nimport { isValidThinkingLevel } from \"../cli/args.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { mergeResourceProfileSettings } from \"./resource-profile-blocks.ts\";\nimport type {\n\tModelRouterSettings,\n\tProfileDefinitionInput,\n\tResourceProfileKind,\n\tResourceProfileSettings,\n\tSettings,\n} from \"./settings-manager.ts\";\nimport { validateSkillName } from \"./skills.ts\";\n\nexport type ProfileSource =\n\t| \"global-settings\"\n\t| \"project-settings\"\n\t| \"external-settings\"\n\t| \"profile-file\"\n\t| \"directory-overlay\"\n\t| \"inline\"\n\t| \"embedded\"\n\t| \"bundle\";\n\nexport interface NormalizedProfile {\n\tname: string;\n\tdescription?: string;\n\tmodel?: string;\n\tthinking?: ThinkingLevel;\n\tmodelRouter?: ModelRouterSettings;\n\t/** Situational identity injected into the system prompt while this profile is active (R6). */\n\tsoul?: string;\n\tresources: ResourceProfileSettings;\n\tsource: ProfileSource;\n\tsourcePath?: string;\n\tbaseDir?: string;\n}\n\nexport interface ProfileRegistryDiagnostic {\n\tsource: ProfileSource;\n\tpath?: string;\n\tmessage: string;\n}\n\nexport interface ProfileRegistryOptions {\n\tglobalSettings: Settings;\n\tprojectSettings: Settings;\n\tdirectoryProfileSettings: Settings;\n\tinlineResourceProfileDefinitions: Record<string, ProfileDefinitionInput>;\n\tdiscoveredResourceProfileDefinitions: Record<string, ResourceProfileSettings>;\n\tprofilesDir?: string;\n\texternalResourceRoots?: string[];\n}\n\nconst RESOURCE_PROFILE_KINDS: ResourceProfileKind[] = [\"extensions\", \"skills\", \"prompts\", \"themes\", \"agents\", \"tools\"];\n\ninterface ProfileCandidate {\n\tprofile: NormalizedProfile;\n\tprecedence: number;\n\torder: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction asNonEmptyString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" && value.trim().length > 0 ? value.trim() : undefined;\n}\n\nfunction asStringArray(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\tconst strings = value.filter((item): item is string => typeof item === \"string\" && item.trim().length > 0);\n\treturn strings.length > 0 ? strings : undefined;\n}\n\nfunction shouldResolveAgainstBaseDir(pattern: string): boolean {\n\treturn pattern.startsWith(\"./\") || pattern.startsWith(\"../\");\n}\n\nfunction normalizePattern(pattern: string, baseDir: string | undefined): string {\n\tconst trimmed = pattern.trim();\n\tif (!baseDir || !shouldResolveAgainstBaseDir(trimmed)) return trimmed;\n\treturn resolvePath(trimmed, baseDir, { trim: true });\n}\n\nfunction normalizeStringArray(value: unknown, baseDir: string | undefined): string[] | undefined {\n\tconst strings = asStringArray(value);\n\tif (!strings) return undefined;\n\treturn strings.map((pattern) => normalizePattern(pattern, baseDir));\n}\n\nfunction normalizeResourceProfileSettings(value: unknown, baseDir: string | undefined): ResourceProfileSettings {\n\tif (!isRecord(value)) {\n\t\tthrow new Error(\"resources must be an object\");\n\t}\n\tconst result: ResourceProfileSettings = {};\n\tfor (const kind of RESOURCE_PROFILE_KINDS) {\n\t\tconst filterValue = value[kind];\n\t\tif (filterValue === undefined) continue;\n\t\tif (!isRecord(filterValue)) {\n\t\t\tthrow new Error(`${kind} filter must be an object`);\n\t\t}\n\t\tconst allow = normalizeStringArray(filterValue.allow, baseDir);\n\t\tconst block = normalizeStringArray(filterValue.block, baseDir);\n\t\tresult[kind] = { allow, block };\n\t}\n\treturn result;\n}\n\nfunction validateProfileName(name: string): string[] {\n\treturn validateSkillName(name);\n}\n\nfunction normalizeThinking(value: unknown): ThinkingLevel | undefined {\n\tconst thinking = asNonEmptyString(value);\n\tif (!thinking) return undefined;\n\tif (!isValidThinkingLevel(thinking)) {\n\t\tthrow new Error(`thinking must be one of off, minimal, low, medium, high, xhigh, max, ultra`);\n\t}\n\treturn thinking;\n}\n\nfunction normalizeModelRouterSettings(value: unknown): ModelRouterSettings | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst settings: ModelRouterSettings = {};\n\tfor (const key of [\"enabled\", \"judgeEnabled\", \"fitnessGate\"] as const) {\n\t\tconst candidate = value[key];\n\t\tif (typeof candidate === \"boolean\") settings[key] = candidate;\n\t}\n\tfor (const key of [\n\t\t\"cheapModel\",\n\t\t\"mediumModel\",\n\t\t\"expensiveModel\",\n\t\t\"learningModel\",\n\t\t\"judgeModel\",\n\t\t\"executorModel\",\n\t] as const) {\n\t\tconst candidate = asNonEmptyString(value[key]);\n\t\tif (candidate) settings[key] = candidate;\n\t}\n\tfor (const key of [\n\t\t\"cheapThinking\",\n\t\t\"mediumThinking\",\n\t\t\"expensiveThinking\",\n\t\t\"executorThinking\",\n\t\t\"judgeThinking\",\n\t] as const) {\n\t\tconst candidate = asNonEmptyString(value[key]);\n\t\tif (!candidate) continue;\n\t\tif (!isValidThinkingLevel(candidate)) {\n\t\t\tthrow new Error(`${key} must be one of off, minimal, low, medium, high, xhigh, max, ultra`);\n\t\t}\n\t\tsettings[key] = candidate;\n\t}\n\treturn Object.keys(settings).length > 0 ? settings : undefined;\n}\n\nfunction normalizeWrapperProfile(options: {\n\tvalue: unknown;\n\tsource: ProfileSource;\n\tsourcePath?: string;\n\tbaseDir?: string;\n\tfallbackName?: string;\n}): NormalizedProfile {\n\tif (!isRecord(options.value)) {\n\t\tthrow new Error(\"profile JSON must be an object\");\n\t}\n\tconst name = asNonEmptyString(options.value.name) ?? options.fallbackName;\n\tif (!name) {\n\t\tthrow new Error(\"profile name is required\");\n\t}\n\tconst nameErrors = validateProfileName(name);\n\tif (nameErrors.length > 0) {\n\t\tthrow new Error(`invalid profile name \"${name}\": ${nameErrors.join(\", \")}`);\n\t}\n\tconst resources = normalizeResourceProfileSettings(options.value.resources ?? {}, options.baseDir);\n\tconst description = asNonEmptyString(options.value.description);\n\tconst model = asNonEmptyString(options.value.model);\n\tconst thinking = normalizeThinking(options.value.thinking);\n\tconst modelRouter = normalizeModelRouterSettings(options.value.modelRouter);\n\tconst soul = asNonEmptyString(options.value.soul);\n\treturn {\n\t\tname,\n\t\tdescription,\n\t\tmodel,\n\t\tthinking,\n\t\tmodelRouter,\n\t\tsoul,\n\t\tresources,\n\t\tsource: options.source,\n\t\tsourcePath: options.sourcePath,\n\t\tbaseDir: options.baseDir,\n\t};\n}\n\nfunction normalizeSettingsProfiles(\n\tsettings: Settings,\n\tsource: ProfileSource,\n\tbaseDir?: string,\n\tsourcePath?: string,\n): Array<Omit<NormalizedProfile, \"source\"> & { source?: ProfileSource }> {\n\tconst profiles: Array<Omit<NormalizedProfile, \"source\"> & { source?: ProfileSource }> = [];\n\tfor (const [name, definition] of Object.entries(settings.resourceProfiles ?? {})) {\n\t\tconst nameErrors = validateProfileName(name);\n\t\tif (nameErrors.length > 0) continue;\n\t\tif (isRecord(definition) && Object.hasOwn(definition, \"resources\")) {\n\t\t\tprofiles.push(\n\t\t\t\tnormalizeWrapperProfile({\n\t\t\t\t\tvalue: { ...definition, name },\n\t\t\t\t\tsource,\n\t\t\t\t\tsourcePath,\n\t\t\t\t\tbaseDir,\n\t\t\t\t\tfallbackName: name,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\tprofiles.push({\n\t\t\tname,\n\t\t\tresources: mergeResourceProfileSettings(undefined, definition as ResourceProfileSettings),\n\t\t\tsourcePath,\n\t\t\tbaseDir,\n\t\t});\n\t}\n\treturn profiles.map((profile) => ({ ...profile, source }));\n}\n\nfunction normalizeInlineDefinitions(\n\tdefinitions: Record<string, ProfileDefinitionInput>,\n\tsource: ProfileSource,\n): NormalizedProfile[] {\n\tconst profiles: NormalizedProfile[] = [];\n\tfor (const [name, definition] of Object.entries(definitions)) {\n\t\ttry {\n\t\t\tprofiles.push(\n\t\t\t\tnormalizeWrapperProfile({\n\t\t\t\t\tvalue: { ...definition, name },\n\t\t\t\t\tsource,\n\t\t\t\t\tfallbackName: name,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch {\n\t\t\t// Inline names/definitions are validated at the SettingsManager API boundary.\n\t\t}\n\t}\n\treturn profiles;\n}\n\nfunction loadSettingsFileProfiles(sourcePath: string, source: ProfileSource): NormalizedProfile[] {\n\ttry {\n\t\tconst stats = statSync(sourcePath);\n\t\tif (!stats.isFile()) return [];\n\t\tconst parsed = JSON.parse(readFileSync(sourcePath, \"utf-8\")) as Settings;\n\t\treturn normalizeSettingsProfiles(parsed, source, dirname(resolve(sourcePath)), resolve(sourcePath)).map(\n\t\t\t(profile) => ({\n\t\t\t\t...profile,\n\t\t\t\tsource,\n\t\t\t}),\n\t\t);\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction normalizeDefinitions(\n\tdefinitions: Record<string, ResourceProfileSettings>,\n\tsource: ProfileSource,\n): NormalizedProfile[] {\n\tconst profiles: NormalizedProfile[] = [];\n\tfor (const [name, resources] of Object.entries(definitions)) {\n\t\tconst nameErrors = validateProfileName(name);\n\t\tif (nameErrors.length > 0) continue;\n\t\tprofiles.push({ name, resources: mergeResourceProfileSettings(undefined, resources), source });\n\t}\n\treturn profiles;\n}\n\nexport class ProfileRegistry {\n\tprivate options: ProfileRegistryOptions;\n\tprivate diagnostics: ProfileRegistryDiagnostic[] = [];\n\n\tconstructor(options: ProfileRegistryOptions) {\n\t\tthis.options = options;\n\t}\n\n\tlistDiagnostics(): ProfileRegistryDiagnostic[] {\n\t\tthis.collectCandidates();\n\t\treturn [...this.diagnostics];\n\t}\n\n\tlistProfiles(): NormalizedProfile[] {\n\t\tconst candidates = this.collectCandidates();\n\t\tconst winners = new Map<string, ProfileCandidate>();\n\t\tfor (const candidate of candidates) {\n\t\t\tconst existing = winners.get(candidate.profile.name);\n\t\t\tif (\n\t\t\t\t!existing ||\n\t\t\t\tcandidate.precedence < existing.precedence ||\n\t\t\t\t(candidate.precedence === existing.precedence && candidate.order < existing.order)\n\t\t\t) {\n\t\t\t\twinners.set(candidate.profile.name, candidate);\n\t\t\t}\n\t\t}\n\t\treturn Array.from(winners.values())\n\t\t\t.sort((a, b) => a.profile.name.localeCompare(b.profile.name))\n\t\t\t.map((candidate) => candidate.profile);\n\t}\n\n\tgetProfile(name: string): NormalizedProfile | undefined {\n\t\tconst trimmed = name.trim();\n\t\tif (!trimmed) return undefined;\n\t\treturn this.listProfiles().find((profile) => profile.name === trimmed);\n\t}\n\n\tresolveProfileRef(ref: string, fromDir: string): NormalizedProfile | undefined {\n\t\tconst trimmed = ref.trim();\n\t\tif (!trimmed) return undefined;\n\t\tif (trimmed.startsWith(\"./\") || trimmed.startsWith(\"../\")) {\n\t\t\tconst sourcePath = resolvePath(trimmed, fromDir, { trim: true });\n\t\t\treturn this.loadProfileFile(sourcePath, \"profile-file\", 0)?.profile;\n\t\t}\n\t\treturn this.getProfile(trimmed);\n\t}\n\n\tprivate collectCandidates(): ProfileCandidate[] {\n\t\tthis.diagnostics = [];\n\t\tconst candidates: ProfileCandidate[] = [];\n\t\tlet order = 0;\n\t\tconst add = (profile: NormalizedProfile, precedence: number): void => {\n\t\t\tcandidates.push({ profile, precedence, order: order++ });\n\t\t};\n\n\t\tfor (const profile of normalizeInlineDefinitions(this.options.inlineResourceProfileDefinitions, \"inline\")) {\n\t\t\tadd(profile, 1);\n\t\t}\n\t\tfor (const profile of normalizeSettingsProfiles(this.options.directoryProfileSettings, \"directory-overlay\")) {\n\t\t\tadd({ ...profile, source: \"directory-overlay\" }, 2);\n\t\t}\n\t\tfor (const profile of normalizeSettingsProfiles(this.options.projectSettings, \"project-settings\")) {\n\t\t\tadd({ ...profile, source: \"project-settings\" }, 3);\n\t\t}\n\t\tfor (const profile of this.loadProfileFiles()) {\n\t\t\tadd(profile.profile, profile.precedence);\n\t\t}\n\t\tfor (const profile of this.loadExternalSettingsProfiles()) {\n\t\t\tadd(profile, 4.2);\n\t\t}\n\t\tfor (const profile of normalizeSettingsProfiles(this.options.globalSettings, \"global-settings\")) {\n\t\t\tadd({ ...profile, source: \"global-settings\" }, 5);\n\t\t}\n\t\tfor (const profile of normalizeDefinitions(this.options.discoveredResourceProfileDefinitions, \"embedded\")) {\n\t\t\tadd(profile, 6);\n\t\t}\n\n\t\treturn candidates;\n\t}\n\n\tprivate loadProfileFiles(): ProfileCandidate[] {\n\t\tconst candidates: ProfileCandidate[] = [];\n\t\tlet order = 0;\n\n\t\tconst profilesDir = this.options.profilesDir;\n\t\tif (profilesDir && existsSync(profilesDir)) {\n\t\t\ttry {\n\t\t\t\tconst entries = readdirSync(profilesDir)\n\t\t\t\t\t.filter((entry) => entry.endsWith(\".json\"))\n\t\t\t\t\t.sort();\n\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\tconst sourcePath = join(profilesDir, entry);\n\t\t\t\t\tconst loaded = this.loadProfileFile(sourcePath, \"profile-file\", order++);\n\t\t\t\t\tif (loaded) {\n\t\t\t\t\t\tcandidates.push({ ...loaded, precedence: 4 });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis.diagnostics.push({ source: \"profile-file\", path: profilesDir, message: String(error) });\n\t\t\t}\n\t\t}\n\n\t\tconst externalRoots = this.options.externalResourceRoots ?? [];\n\t\tfor (const root of externalRoots) {\n\t\t\tconst extProfilesDir = join(root, \"profiles\");\n\t\t\tif (existsSync(extProfilesDir)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst entries = readdirSync(extProfilesDir)\n\t\t\t\t\t\t.filter((entry) => entry.endsWith(\".json\"))\n\t\t\t\t\t\t.sort();\n\t\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\t\tconst sourcePath = join(extProfilesDir, entry);\n\t\t\t\t\t\tconst loaded = this.loadProfileFile(sourcePath, \"profile-file\", order++);\n\t\t\t\t\t\tif (loaded) {\n\t\t\t\t\t\t\tcandidates.push({ ...loaded, precedence: 4.1 });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.diagnostics.push({ source: \"profile-file\", path: extProfilesDir, message: String(error) });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn candidates;\n\t}\n\n\tprivate loadExternalSettingsProfiles(): NormalizedProfile[] {\n\t\tconst profiles: NormalizedProfile[] = [];\n\t\tfor (const root of this.options.externalResourceRoots ?? []) {\n\t\t\tprofiles.push(...loadSettingsFileProfiles(join(root, \"settings.json\"), \"external-settings\"));\n\t\t}\n\t\treturn profiles;\n\t}\n\n\tprivate loadProfileFile(sourcePath: string, source: ProfileSource, order: number): ProfileCandidate | undefined {\n\t\ttry {\n\t\t\tconst stats = statSync(sourcePath);\n\t\t\tif (!stats.isFile()) return undefined;\n\t\t\tconst parsed = JSON.parse(readFileSync(sourcePath, \"utf-8\"));\n\t\t\tconst fallbackName = basename(sourcePath, \".json\");\n\t\t\treturn {\n\t\t\t\tprofile: normalizeWrapperProfile({\n\t\t\t\t\tvalue: parsed,\n\t\t\t\t\tsource,\n\t\t\t\t\tsourcePath: resolve(sourcePath),\n\t\t\t\t\tbaseDir: dirname(resolve(sourcePath)),\n\t\t\t\t\tfallbackName,\n\t\t\t\t}),\n\t\t\t\tprecedence: 0,\n\t\t\t\torder,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthis.diagnostics.push({ source, path: sourcePath, message });\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n"]}
@@ -58,7 +58,7 @@ function normalizeThinking(value) {
58
58
  if (!thinking)
59
59
  return undefined;
60
60
  if (!isValidThinkingLevel(thinking)) {
61
- throw new Error(`thinking must be one of off, minimal, low, medium, high, xhigh`);
61
+ throw new Error(`thinking must be one of off, minimal, low, medium, high, xhigh, max, ultra`);
62
62
  }
63
63
  return thinking;
64
64
  }
@@ -66,13 +66,38 @@ function normalizeModelRouterSettings(value) {
66
66
  if (!isRecord(value))
67
67
  return undefined;
68
68
  const settings = {};
69
- if (typeof value.enabled === "boolean")
70
- settings.enabled = value.enabled;
71
- for (const key of ["cheapModel", "mediumModel", "expensiveModel", "learningModel"]) {
69
+ for (const key of ["enabled", "judgeEnabled", "fitnessGate"]) {
70
+ const candidate = value[key];
71
+ if (typeof candidate === "boolean")
72
+ settings[key] = candidate;
73
+ }
74
+ for (const key of [
75
+ "cheapModel",
76
+ "mediumModel",
77
+ "expensiveModel",
78
+ "learningModel",
79
+ "judgeModel",
80
+ "executorModel",
81
+ ]) {
72
82
  const candidate = asNonEmptyString(value[key]);
73
83
  if (candidate)
74
84
  settings[key] = candidate;
75
85
  }
86
+ for (const key of [
87
+ "cheapThinking",
88
+ "mediumThinking",
89
+ "expensiveThinking",
90
+ "executorThinking",
91
+ "judgeThinking",
92
+ ]) {
93
+ const candidate = asNonEmptyString(value[key]);
94
+ if (!candidate)
95
+ continue;
96
+ if (!isValidThinkingLevel(candidate)) {
97
+ throw new Error(`${key} must be one of off, minimal, low, medium, high, xhigh, max, ultra`);
98
+ }
99
+ settings[key] = candidate;
100
+ }
76
101
  return Object.keys(settings).length > 0 ? settings : undefined;
77
102
  }
78
103
  function normalizeWrapperProfile(options) {
@@ -108,19 +133,45 @@ function normalizeWrapperProfile(options) {
108
133
  }
109
134
  function normalizeSettingsProfiles(settings, source, baseDir, sourcePath) {
110
135
  const profiles = [];
111
- for (const [name, resources] of Object.entries(settings.resourceProfiles ?? {})) {
136
+ for (const [name, definition] of Object.entries(settings.resourceProfiles ?? {})) {
112
137
  const nameErrors = validateProfileName(name);
113
138
  if (nameErrors.length > 0)
114
139
  continue;
140
+ if (isRecord(definition) && Object.hasOwn(definition, "resources")) {
141
+ profiles.push(normalizeWrapperProfile({
142
+ value: { ...definition, name },
143
+ source,
144
+ sourcePath,
145
+ baseDir,
146
+ fallbackName: name,
147
+ }));
148
+ continue;
149
+ }
115
150
  profiles.push({
116
151
  name,
117
- resources: mergeResourceProfileSettings(undefined, resources),
152
+ resources: mergeResourceProfileSettings(undefined, definition),
118
153
  sourcePath,
119
154
  baseDir,
120
155
  });
121
156
  }
122
157
  return profiles.map((profile) => ({ ...profile, source }));
123
158
  }
159
+ function normalizeInlineDefinitions(definitions, source) {
160
+ const profiles = [];
161
+ for (const [name, definition] of Object.entries(definitions)) {
162
+ try {
163
+ profiles.push(normalizeWrapperProfile({
164
+ value: { ...definition, name },
165
+ source,
166
+ fallbackName: name,
167
+ }));
168
+ }
169
+ catch {
170
+ // Inline names/definitions are validated at the SettingsManager API boundary.
171
+ }
172
+ }
173
+ return profiles;
174
+ }
124
175
  function loadSettingsFileProfiles(sourcePath, source) {
125
176
  try {
126
177
  const stats = statSync(sourcePath);
@@ -194,7 +245,7 @@ export class ProfileRegistry {
194
245
  const add = (profile, precedence) => {
195
246
  candidates.push({ profile, precedence, order: order++ });
196
247
  };
197
- for (const profile of normalizeDefinitions(this.options.inlineResourceProfileDefinitions, "inline")) {
248
+ for (const profile of normalizeInlineDefinitions(this.options.inlineResourceProfileDefinitions, "inline")) {
198
249
  add(profile, 1);
199
250
  }
200
251
  for (const profile of normalizeSettingsProfiles(this.options.directoryProfileSettings, "directory-overlay")) {
@@ -1 +1 @@
1
- {"version":3,"file":"profile-registry.js","sourceRoot":"","sources":["../../src/core/profile-registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAO5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA0ChD,MAAM,sBAAsB,GAA0B,CAAC,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAQvH,SAAS,QAAQ,CAAC,KAAc,EAAoC;IACnE,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAAA,CAC5E;AAED,SAAS,gBAAgB,CAAC,KAAc,EAAsB;IAC7D,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CACvF;AAED,SAAS,aAAa,CAAC,KAAc,EAAwB;IAC5D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3G,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAChD;AAED,SAAS,2BAA2B,CAAC,OAAe,EAAW;IAC9D,OAAO,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AAAA,CAC7D;AAED,SAAS,gBAAgB,CAAC,OAAe,EAAE,OAA2B,EAAU;IAC/E,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,CAAC,OAAO,IAAI,CAAC,2BAA2B,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IACtE,OAAO,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AAAA,CACrD;AAED,SAAS,oBAAoB,CAAC,KAAc,EAAE,OAA2B,EAAwB;IAChG,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAAA,CACpE;AAED,SAAS,gCAAgC,CAAC,KAAc,EAAE,OAA2B,EAA2B;IAC/G,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IAChD,CAAC;IACD,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,sBAAsB,EAAE,CAAC;QAC3C,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,WAAW,KAAK,SAAS;YAAE,SAAS;QACxC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,2BAA2B,CAAC,CAAC;QACrD,CAAC;QACD,MAAM,KAAK,GAAG,oBAAoB,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC/D,MAAM,KAAK,GAAG,oBAAoB,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC/D,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACjC,CAAC;IACD,OAAO,MAAM,CAAC;AAAA,CACd;AAED,SAAS,mBAAmB,CAAC,IAAY,EAAY;IACpD,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC;AAAA,CAC/B;AAED,SAAS,iBAAiB,CAAC,KAAc,EAA6B;IACrE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACzC,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,QAAQ,CAAC;AAAA,CAChB;AAED,SAAS,4BAA4B,CAAC,KAAc,EAAmC;IACtF,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACvC,MAAM,QAAQ,GAAwB,EAAE,CAAC;IACzC,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IACzE,KAAK,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,CAAU,EAAE,CAAC;QAC7F,MAAM,SAAS,GAAG,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/C,IAAI,SAAS;YAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC1C,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAC/D;AAED,SAAS,uBAAuB,CAAC,OAMhC,EAAqB;IACrB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACnD,CAAC;IACD,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,YAAY,CAAC;IAC1E,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC7C,CAAC;IACD,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,MAAM,SAAS,GAAG,gCAAgC,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACnG,MAAM,WAAW,GAAG,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC3D,MAAM,WAAW,GAAG,4BAA4B,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO;QACN,IAAI;QACJ,WAAW;QACX,KAAK;QACL,QAAQ;QACR,WAAW;QACX,IAAI;QACJ,SAAS;QACT,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,OAAO,EAAE,OAAO,CAAC,OAAO;KACxB,CAAC;AAAA,CACF;AAED,SAAS,yBAAyB,CACjC,QAAkB,EAClB,MAAqB,EACrB,OAAgB,EAChB,UAAmB,EACqD;IACxE,MAAM,QAAQ,GAA0E,EAAE,CAAC;IAC3F,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,IAAI,EAAE,CAAC,EAAE,CAAC;QACjF,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QACpC,QAAQ,CAAC,IAAI,CAAC;YACb,IAAI;YACJ,SAAS,EAAE,4BAA4B,CAAC,SAAS,EAAE,SAAS,CAAC;YAC7D,UAAU;YACV,OAAO;SACP,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAAA,CAC3D;AAED,SAAS,wBAAwB,CAAC,UAAkB,EAAE,MAAqB,EAAuB;IACjG,IAAI,CAAC;QACJ,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;YAAE,OAAO,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAa,CAAC;QACzE,OAAO,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CACtG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACb,GAAG,OAAO;YACV,MAAM;SACN,CAAC,CACF,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,CAAC;IACX,CAAC;AAAA,CACD;AAED,SAAS,oBAAoB,CAC5B,WAAoD,EACpD,MAAqB,EACC;IACtB,MAAM,QAAQ,GAAwB,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7D,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,4BAA4B,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,QAAQ,CAAC;AAAA,CAChB;AAED,MAAM,OAAO,eAAe;IACnB,OAAO,CAAyB;IAChC,WAAW,GAAgC,EAAE,CAAC;IAEtD,YAAY,OAA+B,EAAE;QAC5C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAAA,CACvB;IAED,eAAe,GAAgC;QAC9C,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC;IAAA,CAC7B;IAED,YAAY,GAAwB;QACnC,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,IAAI,GAAG,EAA4B,CAAC;QACpD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACpC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACrD,IACC,CAAC,QAAQ;gBACT,SAAS,CAAC,UAAU,GAAG,QAAQ,CAAC,UAAU;gBAC1C,CAAC,SAAS,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU,IAAI,SAAS,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,EACjF,CAAC;gBACF,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAChD,CAAC;QACF,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;aACjC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;aAC5D,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAAA,CACxC;IAED,UAAU,CAAC,IAAY,EAAiC;QACvD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC;QAC/B,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;IAAA,CACvE;IAED,iBAAiB,CAAC,GAAW,EAAE,OAAe,EAAiC;QAC9E,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC;QAC/B,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3D,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACjE,OAAO,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,cAAc,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC;QACrE,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAAA,CAChC;IAEO,iBAAiB,GAAuB;QAC/C,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QACtB,MAAM,UAAU,GAAuB,EAAE,CAAC;QAC1C,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,MAAM,GAAG,GAAG,CAAC,OAA0B,EAAE,UAAkB,EAAQ,EAAE,CAAC;YACrE,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAAA,CACzD,CAAC;QAEF,KAAK,MAAM,OAAO,IAAI,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,gCAAgC,EAAE,QAAQ,CAAC,EAAE,CAAC;YACrG,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACjB,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,wBAAwB,EAAE,mBAAmB,CAAC,EAAE,CAAC;YAC7G,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAC,CAAC;QACrD,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,kBAAkB,CAAC,EAAE,CAAC;YACnG,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC,CAAC;QACpD,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC;YAC/C,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;QAC1C,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,4BAA4B,EAAE,EAAE,CAAC;YAC3D,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACnB,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,iBAAiB,CAAC,EAAE,CAAC;YACjG,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC,CAAC;QACnD,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,oCAAoC,EAAE,UAAU,CAAC,EAAE,CAAC;YAC3G,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACjB,CAAC;QAED,OAAO,UAAU,CAAC;IAAA,CAClB;IAEO,gBAAgB,GAAuB;QAC9C,MAAM,UAAU,GAAuB,EAAE,CAAC;QAC1C,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;QAC7C,IAAI,WAAW,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC5C,IAAI,CAAC;gBACJ,MAAM,OAAO,GAAG,WAAW,CAAC,WAAW,CAAC;qBACtC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;qBAC1C,IAAI,EAAE,CAAC;gBACT,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;oBAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;oBAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;oBACzE,IAAI,MAAM,EAAE,CAAC;wBACZ,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;oBAC/C,CAAC;gBACF,CAAC;YACF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC9F,CAAC;QACF,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,qBAAqB,IAAI,EAAE,CAAC;QAC/D,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;YAClC,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC9C,IAAI,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;gBAChC,IAAI,CAAC;oBACJ,MAAM,OAAO,GAAG,WAAW,CAAC,cAAc,CAAC;yBACzC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;yBAC1C,IAAI,EAAE,CAAC;oBACT,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;wBAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;wBAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;wBACzE,IAAI,MAAM,EAAE,CAAC;4BACZ,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;wBACjD,CAAC;oBACF,CAAC;gBACF,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBAChB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACjG,CAAC;YACF,CAAC;QACF,CAAC;QAED,OAAO,UAAU,CAAC;IAAA,CAClB;IAEO,4BAA4B,GAAwB;QAC3D,MAAM,QAAQ,GAAwB,EAAE,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,qBAAqB,IAAI,EAAE,EAAE,CAAC;YAC7D,QAAQ,CAAC,IAAI,CAAC,GAAG,wBAAwB,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAC9F,CAAC;QACD,OAAO,QAAQ,CAAC;IAAA,CAChB;IAEO,eAAe,CAAC,UAAkB,EAAE,MAAqB,EAAE,KAAa,EAAgC;QAC/G,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;YACnC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;gBAAE,OAAO,SAAS,CAAC;YACtC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;YAC7D,MAAM,YAAY,GAAG,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACnD,OAAO;gBACN,OAAO,EAAE,uBAAuB,CAAC;oBAChC,KAAK,EAAE,MAAM;oBACb,MAAM;oBACN,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC;oBAC/B,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;oBACrC,YAAY;iBACZ,CAAC;gBACF,UAAU,EAAE,CAAC;gBACb,KAAK;aACL,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;YAC7D,OAAO,SAAS,CAAC;QAClB,CAAC;IAAA,CACD;CACD","sourcesContent":["import type { ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join, resolve } from \"path\";\nimport { isValidThinkingLevel } from \"../cli/args.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { mergeResourceProfileSettings } from \"./resource-profile-blocks.ts\";\nimport type {\n\tModelRouterSettings,\n\tResourceProfileKind,\n\tResourceProfileSettings,\n\tSettings,\n} from \"./settings-manager.ts\";\nimport { validateSkillName } from \"./skills.ts\";\n\nexport type ProfileSource =\n\t| \"global-settings\"\n\t| \"project-settings\"\n\t| \"external-settings\"\n\t| \"profile-file\"\n\t| \"directory-overlay\"\n\t| \"inline\"\n\t| \"embedded\"\n\t| \"bundle\";\n\nexport interface NormalizedProfile {\n\tname: string;\n\tdescription?: string;\n\tmodel?: string;\n\tthinking?: ThinkingLevel;\n\tmodelRouter?: ModelRouterSettings;\n\t/** Situational identity injected into the system prompt while this profile is active (R6). */\n\tsoul?: string;\n\tresources: ResourceProfileSettings;\n\tsource: ProfileSource;\n\tsourcePath?: string;\n\tbaseDir?: string;\n}\n\nexport interface ProfileRegistryDiagnostic {\n\tsource: ProfileSource;\n\tpath?: string;\n\tmessage: string;\n}\n\nexport interface ProfileRegistryOptions {\n\tglobalSettings: Settings;\n\tprojectSettings: Settings;\n\tdirectoryProfileSettings: Settings;\n\tinlineResourceProfileDefinitions: Record<string, ResourceProfileSettings>;\n\tdiscoveredResourceProfileDefinitions: Record<string, ResourceProfileSettings>;\n\tprofilesDir?: string;\n\texternalResourceRoots?: string[];\n}\n\nconst RESOURCE_PROFILE_KINDS: ResourceProfileKind[] = [\"extensions\", \"skills\", \"prompts\", \"themes\", \"agents\", \"tools\"];\n\ninterface ProfileCandidate {\n\tprofile: NormalizedProfile;\n\tprecedence: number;\n\torder: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction asNonEmptyString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" && value.trim().length > 0 ? value.trim() : undefined;\n}\n\nfunction asStringArray(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\tconst strings = value.filter((item): item is string => typeof item === \"string\" && item.trim().length > 0);\n\treturn strings.length > 0 ? strings : undefined;\n}\n\nfunction shouldResolveAgainstBaseDir(pattern: string): boolean {\n\treturn pattern.startsWith(\"./\") || pattern.startsWith(\"../\");\n}\n\nfunction normalizePattern(pattern: string, baseDir: string | undefined): string {\n\tconst trimmed = pattern.trim();\n\tif (!baseDir || !shouldResolveAgainstBaseDir(trimmed)) return trimmed;\n\treturn resolvePath(trimmed, baseDir, { trim: true });\n}\n\nfunction normalizeStringArray(value: unknown, baseDir: string | undefined): string[] | undefined {\n\tconst strings = asStringArray(value);\n\tif (!strings) return undefined;\n\treturn strings.map((pattern) => normalizePattern(pattern, baseDir));\n}\n\nfunction normalizeResourceProfileSettings(value: unknown, baseDir: string | undefined): ResourceProfileSettings {\n\tif (!isRecord(value)) {\n\t\tthrow new Error(\"resources must be an object\");\n\t}\n\tconst result: ResourceProfileSettings = {};\n\tfor (const kind of RESOURCE_PROFILE_KINDS) {\n\t\tconst filterValue = value[kind];\n\t\tif (filterValue === undefined) continue;\n\t\tif (!isRecord(filterValue)) {\n\t\t\tthrow new Error(`${kind} filter must be an object`);\n\t\t}\n\t\tconst allow = normalizeStringArray(filterValue.allow, baseDir);\n\t\tconst block = normalizeStringArray(filterValue.block, baseDir);\n\t\tresult[kind] = { allow, block };\n\t}\n\treturn result;\n}\n\nfunction validateProfileName(name: string): string[] {\n\treturn validateSkillName(name);\n}\n\nfunction normalizeThinking(value: unknown): ThinkingLevel | undefined {\n\tconst thinking = asNonEmptyString(value);\n\tif (!thinking) return undefined;\n\tif (!isValidThinkingLevel(thinking)) {\n\t\tthrow new Error(`thinking must be one of off, minimal, low, medium, high, xhigh`);\n\t}\n\treturn thinking;\n}\n\nfunction normalizeModelRouterSettings(value: unknown): ModelRouterSettings | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst settings: ModelRouterSettings = {};\n\tif (typeof value.enabled === \"boolean\") settings.enabled = value.enabled;\n\tfor (const key of [\"cheapModel\", \"mediumModel\", \"expensiveModel\", \"learningModel\"] as const) {\n\t\tconst candidate = asNonEmptyString(value[key]);\n\t\tif (candidate) settings[key] = candidate;\n\t}\n\treturn Object.keys(settings).length > 0 ? settings : undefined;\n}\n\nfunction normalizeWrapperProfile(options: {\n\tvalue: unknown;\n\tsource: ProfileSource;\n\tsourcePath?: string;\n\tbaseDir?: string;\n\tfallbackName?: string;\n}): NormalizedProfile {\n\tif (!isRecord(options.value)) {\n\t\tthrow new Error(\"profile JSON must be an object\");\n\t}\n\tconst name = asNonEmptyString(options.value.name) ?? options.fallbackName;\n\tif (!name) {\n\t\tthrow new Error(\"profile name is required\");\n\t}\n\tconst nameErrors = validateProfileName(name);\n\tif (nameErrors.length > 0) {\n\t\tthrow new Error(`invalid profile name \"${name}\": ${nameErrors.join(\", \")}`);\n\t}\n\tconst resources = normalizeResourceProfileSettings(options.value.resources ?? {}, options.baseDir);\n\tconst description = asNonEmptyString(options.value.description);\n\tconst model = asNonEmptyString(options.value.model);\n\tconst thinking = normalizeThinking(options.value.thinking);\n\tconst modelRouter = normalizeModelRouterSettings(options.value.modelRouter);\n\tconst soul = asNonEmptyString(options.value.soul);\n\treturn {\n\t\tname,\n\t\tdescription,\n\t\tmodel,\n\t\tthinking,\n\t\tmodelRouter,\n\t\tsoul,\n\t\tresources,\n\t\tsource: options.source,\n\t\tsourcePath: options.sourcePath,\n\t\tbaseDir: options.baseDir,\n\t};\n}\n\nfunction normalizeSettingsProfiles(\n\tsettings: Settings,\n\tsource: ProfileSource,\n\tbaseDir?: string,\n\tsourcePath?: string,\n): Array<Omit<NormalizedProfile, \"source\"> & { source?: ProfileSource }> {\n\tconst profiles: Array<Omit<NormalizedProfile, \"source\"> & { source?: ProfileSource }> = [];\n\tfor (const [name, resources] of Object.entries(settings.resourceProfiles ?? {})) {\n\t\tconst nameErrors = validateProfileName(name);\n\t\tif (nameErrors.length > 0) continue;\n\t\tprofiles.push({\n\t\t\tname,\n\t\t\tresources: mergeResourceProfileSettings(undefined, resources),\n\t\t\tsourcePath,\n\t\t\tbaseDir,\n\t\t});\n\t}\n\treturn profiles.map((profile) => ({ ...profile, source }));\n}\n\nfunction loadSettingsFileProfiles(sourcePath: string, source: ProfileSource): NormalizedProfile[] {\n\ttry {\n\t\tconst stats = statSync(sourcePath);\n\t\tif (!stats.isFile()) return [];\n\t\tconst parsed = JSON.parse(readFileSync(sourcePath, \"utf-8\")) as Settings;\n\t\treturn normalizeSettingsProfiles(parsed, source, dirname(resolve(sourcePath)), resolve(sourcePath)).map(\n\t\t\t(profile) => ({\n\t\t\t\t...profile,\n\t\t\t\tsource,\n\t\t\t}),\n\t\t);\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction normalizeDefinitions(\n\tdefinitions: Record<string, ResourceProfileSettings>,\n\tsource: ProfileSource,\n): NormalizedProfile[] {\n\tconst profiles: NormalizedProfile[] = [];\n\tfor (const [name, resources] of Object.entries(definitions)) {\n\t\tconst nameErrors = validateProfileName(name);\n\t\tif (nameErrors.length > 0) continue;\n\t\tprofiles.push({ name, resources: mergeResourceProfileSettings(undefined, resources), source });\n\t}\n\treturn profiles;\n}\n\nexport class ProfileRegistry {\n\tprivate options: ProfileRegistryOptions;\n\tprivate diagnostics: ProfileRegistryDiagnostic[] = [];\n\n\tconstructor(options: ProfileRegistryOptions) {\n\t\tthis.options = options;\n\t}\n\n\tlistDiagnostics(): ProfileRegistryDiagnostic[] {\n\t\tthis.collectCandidates();\n\t\treturn [...this.diagnostics];\n\t}\n\n\tlistProfiles(): NormalizedProfile[] {\n\t\tconst candidates = this.collectCandidates();\n\t\tconst winners = new Map<string, ProfileCandidate>();\n\t\tfor (const candidate of candidates) {\n\t\t\tconst existing = winners.get(candidate.profile.name);\n\t\t\tif (\n\t\t\t\t!existing ||\n\t\t\t\tcandidate.precedence < existing.precedence ||\n\t\t\t\t(candidate.precedence === existing.precedence && candidate.order < existing.order)\n\t\t\t) {\n\t\t\t\twinners.set(candidate.profile.name, candidate);\n\t\t\t}\n\t\t}\n\t\treturn Array.from(winners.values())\n\t\t\t.sort((a, b) => a.profile.name.localeCompare(b.profile.name))\n\t\t\t.map((candidate) => candidate.profile);\n\t}\n\n\tgetProfile(name: string): NormalizedProfile | undefined {\n\t\tconst trimmed = name.trim();\n\t\tif (!trimmed) return undefined;\n\t\treturn this.listProfiles().find((profile) => profile.name === trimmed);\n\t}\n\n\tresolveProfileRef(ref: string, fromDir: string): NormalizedProfile | undefined {\n\t\tconst trimmed = ref.trim();\n\t\tif (!trimmed) return undefined;\n\t\tif (trimmed.startsWith(\"./\") || trimmed.startsWith(\"../\")) {\n\t\t\tconst sourcePath = resolvePath(trimmed, fromDir, { trim: true });\n\t\t\treturn this.loadProfileFile(sourcePath, \"profile-file\", 0)?.profile;\n\t\t}\n\t\treturn this.getProfile(trimmed);\n\t}\n\n\tprivate collectCandidates(): ProfileCandidate[] {\n\t\tthis.diagnostics = [];\n\t\tconst candidates: ProfileCandidate[] = [];\n\t\tlet order = 0;\n\t\tconst add = (profile: NormalizedProfile, precedence: number): void => {\n\t\t\tcandidates.push({ profile, precedence, order: order++ });\n\t\t};\n\n\t\tfor (const profile of normalizeDefinitions(this.options.inlineResourceProfileDefinitions, \"inline\")) {\n\t\t\tadd(profile, 1);\n\t\t}\n\t\tfor (const profile of normalizeSettingsProfiles(this.options.directoryProfileSettings, \"directory-overlay\")) {\n\t\t\tadd({ ...profile, source: \"directory-overlay\" }, 2);\n\t\t}\n\t\tfor (const profile of normalizeSettingsProfiles(this.options.projectSettings, \"project-settings\")) {\n\t\t\tadd({ ...profile, source: \"project-settings\" }, 3);\n\t\t}\n\t\tfor (const profile of this.loadProfileFiles()) {\n\t\t\tadd(profile.profile, profile.precedence);\n\t\t}\n\t\tfor (const profile of this.loadExternalSettingsProfiles()) {\n\t\t\tadd(profile, 4.2);\n\t\t}\n\t\tfor (const profile of normalizeSettingsProfiles(this.options.globalSettings, \"global-settings\")) {\n\t\t\tadd({ ...profile, source: \"global-settings\" }, 5);\n\t\t}\n\t\tfor (const profile of normalizeDefinitions(this.options.discoveredResourceProfileDefinitions, \"embedded\")) {\n\t\t\tadd(profile, 6);\n\t\t}\n\n\t\treturn candidates;\n\t}\n\n\tprivate loadProfileFiles(): ProfileCandidate[] {\n\t\tconst candidates: ProfileCandidate[] = [];\n\t\tlet order = 0;\n\n\t\tconst profilesDir = this.options.profilesDir;\n\t\tif (profilesDir && existsSync(profilesDir)) {\n\t\t\ttry {\n\t\t\t\tconst entries = readdirSync(profilesDir)\n\t\t\t\t\t.filter((entry) => entry.endsWith(\".json\"))\n\t\t\t\t\t.sort();\n\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\tconst sourcePath = join(profilesDir, entry);\n\t\t\t\t\tconst loaded = this.loadProfileFile(sourcePath, \"profile-file\", order++);\n\t\t\t\t\tif (loaded) {\n\t\t\t\t\t\tcandidates.push({ ...loaded, precedence: 4 });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis.diagnostics.push({ source: \"profile-file\", path: profilesDir, message: String(error) });\n\t\t\t}\n\t\t}\n\n\t\tconst externalRoots = this.options.externalResourceRoots ?? [];\n\t\tfor (const root of externalRoots) {\n\t\t\tconst extProfilesDir = join(root, \"profiles\");\n\t\t\tif (existsSync(extProfilesDir)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst entries = readdirSync(extProfilesDir)\n\t\t\t\t\t\t.filter((entry) => entry.endsWith(\".json\"))\n\t\t\t\t\t\t.sort();\n\t\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\t\tconst sourcePath = join(extProfilesDir, entry);\n\t\t\t\t\t\tconst loaded = this.loadProfileFile(sourcePath, \"profile-file\", order++);\n\t\t\t\t\t\tif (loaded) {\n\t\t\t\t\t\t\tcandidates.push({ ...loaded, precedence: 4.1 });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.diagnostics.push({ source: \"profile-file\", path: extProfilesDir, message: String(error) });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn candidates;\n\t}\n\n\tprivate loadExternalSettingsProfiles(): NormalizedProfile[] {\n\t\tconst profiles: NormalizedProfile[] = [];\n\t\tfor (const root of this.options.externalResourceRoots ?? []) {\n\t\t\tprofiles.push(...loadSettingsFileProfiles(join(root, \"settings.json\"), \"external-settings\"));\n\t\t}\n\t\treturn profiles;\n\t}\n\n\tprivate loadProfileFile(sourcePath: string, source: ProfileSource, order: number): ProfileCandidate | undefined {\n\t\ttry {\n\t\t\tconst stats = statSync(sourcePath);\n\t\t\tif (!stats.isFile()) return undefined;\n\t\t\tconst parsed = JSON.parse(readFileSync(sourcePath, \"utf-8\"));\n\t\t\tconst fallbackName = basename(sourcePath, \".json\");\n\t\t\treturn {\n\t\t\t\tprofile: normalizeWrapperProfile({\n\t\t\t\t\tvalue: parsed,\n\t\t\t\t\tsource,\n\t\t\t\t\tsourcePath: resolve(sourcePath),\n\t\t\t\t\tbaseDir: dirname(resolve(sourcePath)),\n\t\t\t\t\tfallbackName,\n\t\t\t\t}),\n\t\t\t\tprecedence: 0,\n\t\t\t\torder,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthis.diagnostics.push({ source, path: sourcePath, message });\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"profile-registry.js","sourceRoot":"","sources":["../../src/core/profile-registry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join, resolve } from \"path\";\nimport { isValidThinkingLevel } from \"../cli/args.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { mergeResourceProfileSettings } from \"./resource-profile-blocks.ts\";\nimport type {\n\tModelRouterSettings,\n\tProfileDefinitionInput,\n\tResourceProfileKind,\n\tResourceProfileSettings,\n\tSettings,\n} from \"./settings-manager.ts\";\nimport { validateSkillName } from \"./skills.ts\";\n\nexport type ProfileSource =\n\t| \"global-settings\"\n\t| \"project-settings\"\n\t| \"external-settings\"\n\t| \"profile-file\"\n\t| \"directory-overlay\"\n\t| \"inline\"\n\t| \"embedded\"\n\t| \"bundle\";\n\nexport interface NormalizedProfile {\n\tname: string;\n\tdescription?: string;\n\tmodel?: string;\n\tthinking?: ThinkingLevel;\n\tmodelRouter?: ModelRouterSettings;\n\t/** Situational identity injected into the system prompt while this profile is active (R6). */\n\tsoul?: string;\n\tresources: ResourceProfileSettings;\n\tsource: ProfileSource;\n\tsourcePath?: string;\n\tbaseDir?: string;\n}\n\nexport interface ProfileRegistryDiagnostic {\n\tsource: ProfileSource;\n\tpath?: string;\n\tmessage: string;\n}\n\nexport interface ProfileRegistryOptions {\n\tglobalSettings: Settings;\n\tprojectSettings: Settings;\n\tdirectoryProfileSettings: Settings;\n\tinlineResourceProfileDefinitions: Record<string, ProfileDefinitionInput>;\n\tdiscoveredResourceProfileDefinitions: Record<string, ResourceProfileSettings>;\n\tprofilesDir?: string;\n\texternalResourceRoots?: string[];\n}\n\nconst RESOURCE_PROFILE_KINDS: ResourceProfileKind[] = [\"extensions\", \"skills\", \"prompts\", \"themes\", \"agents\", \"tools\"];\n\ninterface ProfileCandidate {\n\tprofile: NormalizedProfile;\n\tprecedence: number;\n\torder: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction asNonEmptyString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" && value.trim().length > 0 ? value.trim() : undefined;\n}\n\nfunction asStringArray(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\tconst strings = value.filter((item): item is string => typeof item === \"string\" && item.trim().length > 0);\n\treturn strings.length > 0 ? strings : undefined;\n}\n\nfunction shouldResolveAgainstBaseDir(pattern: string): boolean {\n\treturn pattern.startsWith(\"./\") || pattern.startsWith(\"../\");\n}\n\nfunction normalizePattern(pattern: string, baseDir: string | undefined): string {\n\tconst trimmed = pattern.trim();\n\tif (!baseDir || !shouldResolveAgainstBaseDir(trimmed)) return trimmed;\n\treturn resolvePath(trimmed, baseDir, { trim: true });\n}\n\nfunction normalizeStringArray(value: unknown, baseDir: string | undefined): string[] | undefined {\n\tconst strings = asStringArray(value);\n\tif (!strings) return undefined;\n\treturn strings.map((pattern) => normalizePattern(pattern, baseDir));\n}\n\nfunction normalizeResourceProfileSettings(value: unknown, baseDir: string | undefined): ResourceProfileSettings {\n\tif (!isRecord(value)) {\n\t\tthrow new Error(\"resources must be an object\");\n\t}\n\tconst result: ResourceProfileSettings = {};\n\tfor (const kind of RESOURCE_PROFILE_KINDS) {\n\t\tconst filterValue = value[kind];\n\t\tif (filterValue === undefined) continue;\n\t\tif (!isRecord(filterValue)) {\n\t\t\tthrow new Error(`${kind} filter must be an object`);\n\t\t}\n\t\tconst allow = normalizeStringArray(filterValue.allow, baseDir);\n\t\tconst block = normalizeStringArray(filterValue.block, baseDir);\n\t\tresult[kind] = { allow, block };\n\t}\n\treturn result;\n}\n\nfunction validateProfileName(name: string): string[] {\n\treturn validateSkillName(name);\n}\n\nfunction normalizeThinking(value: unknown): ThinkingLevel | undefined {\n\tconst thinking = asNonEmptyString(value);\n\tif (!thinking) return undefined;\n\tif (!isValidThinkingLevel(thinking)) {\n\t\tthrow new Error(`thinking must be one of off, minimal, low, medium, high, xhigh, max, ultra`);\n\t}\n\treturn thinking;\n}\n\nfunction normalizeModelRouterSettings(value: unknown): ModelRouterSettings | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst settings: ModelRouterSettings = {};\n\tfor (const key of [\"enabled\", \"judgeEnabled\", \"fitnessGate\"] as const) {\n\t\tconst candidate = value[key];\n\t\tif (typeof candidate === \"boolean\") settings[key] = candidate;\n\t}\n\tfor (const key of [\n\t\t\"cheapModel\",\n\t\t\"mediumModel\",\n\t\t\"expensiveModel\",\n\t\t\"learningModel\",\n\t\t\"judgeModel\",\n\t\t\"executorModel\",\n\t] as const) {\n\t\tconst candidate = asNonEmptyString(value[key]);\n\t\tif (candidate) settings[key] = candidate;\n\t}\n\tfor (const key of [\n\t\t\"cheapThinking\",\n\t\t\"mediumThinking\",\n\t\t\"expensiveThinking\",\n\t\t\"executorThinking\",\n\t\t\"judgeThinking\",\n\t] as const) {\n\t\tconst candidate = asNonEmptyString(value[key]);\n\t\tif (!candidate) continue;\n\t\tif (!isValidThinkingLevel(candidate)) {\n\t\t\tthrow new Error(`${key} must be one of off, minimal, low, medium, high, xhigh, max, ultra`);\n\t\t}\n\t\tsettings[key] = candidate;\n\t}\n\treturn Object.keys(settings).length > 0 ? settings : undefined;\n}\n\nfunction normalizeWrapperProfile(options: {\n\tvalue: unknown;\n\tsource: ProfileSource;\n\tsourcePath?: string;\n\tbaseDir?: string;\n\tfallbackName?: string;\n}): NormalizedProfile {\n\tif (!isRecord(options.value)) {\n\t\tthrow new Error(\"profile JSON must be an object\");\n\t}\n\tconst name = asNonEmptyString(options.value.name) ?? options.fallbackName;\n\tif (!name) {\n\t\tthrow new Error(\"profile name is required\");\n\t}\n\tconst nameErrors = validateProfileName(name);\n\tif (nameErrors.length > 0) {\n\t\tthrow new Error(`invalid profile name \"${name}\": ${nameErrors.join(\", \")}`);\n\t}\n\tconst resources = normalizeResourceProfileSettings(options.value.resources ?? {}, options.baseDir);\n\tconst description = asNonEmptyString(options.value.description);\n\tconst model = asNonEmptyString(options.value.model);\n\tconst thinking = normalizeThinking(options.value.thinking);\n\tconst modelRouter = normalizeModelRouterSettings(options.value.modelRouter);\n\tconst soul = asNonEmptyString(options.value.soul);\n\treturn {\n\t\tname,\n\t\tdescription,\n\t\tmodel,\n\t\tthinking,\n\t\tmodelRouter,\n\t\tsoul,\n\t\tresources,\n\t\tsource: options.source,\n\t\tsourcePath: options.sourcePath,\n\t\tbaseDir: options.baseDir,\n\t};\n}\n\nfunction normalizeSettingsProfiles(\n\tsettings: Settings,\n\tsource: ProfileSource,\n\tbaseDir?: string,\n\tsourcePath?: string,\n): Array<Omit<NormalizedProfile, \"source\"> & { source?: ProfileSource }> {\n\tconst profiles: Array<Omit<NormalizedProfile, \"source\"> & { source?: ProfileSource }> = [];\n\tfor (const [name, definition] of Object.entries(settings.resourceProfiles ?? {})) {\n\t\tconst nameErrors = validateProfileName(name);\n\t\tif (nameErrors.length > 0) continue;\n\t\tif (isRecord(definition) && Object.hasOwn(definition, \"resources\")) {\n\t\t\tprofiles.push(\n\t\t\t\tnormalizeWrapperProfile({\n\t\t\t\t\tvalue: { ...definition, name },\n\t\t\t\t\tsource,\n\t\t\t\t\tsourcePath,\n\t\t\t\t\tbaseDir,\n\t\t\t\t\tfallbackName: name,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\tprofiles.push({\n\t\t\tname,\n\t\t\tresources: mergeResourceProfileSettings(undefined, definition as ResourceProfileSettings),\n\t\t\tsourcePath,\n\t\t\tbaseDir,\n\t\t});\n\t}\n\treturn profiles.map((profile) => ({ ...profile, source }));\n}\n\nfunction normalizeInlineDefinitions(\n\tdefinitions: Record<string, ProfileDefinitionInput>,\n\tsource: ProfileSource,\n): NormalizedProfile[] {\n\tconst profiles: NormalizedProfile[] = [];\n\tfor (const [name, definition] of Object.entries(definitions)) {\n\t\ttry {\n\t\t\tprofiles.push(\n\t\t\t\tnormalizeWrapperProfile({\n\t\t\t\t\tvalue: { ...definition, name },\n\t\t\t\t\tsource,\n\t\t\t\t\tfallbackName: name,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch {\n\t\t\t// Inline names/definitions are validated at the SettingsManager API boundary.\n\t\t}\n\t}\n\treturn profiles;\n}\n\nfunction loadSettingsFileProfiles(sourcePath: string, source: ProfileSource): NormalizedProfile[] {\n\ttry {\n\t\tconst stats = statSync(sourcePath);\n\t\tif (!stats.isFile()) return [];\n\t\tconst parsed = JSON.parse(readFileSync(sourcePath, \"utf-8\")) as Settings;\n\t\treturn normalizeSettingsProfiles(parsed, source, dirname(resolve(sourcePath)), resolve(sourcePath)).map(\n\t\t\t(profile) => ({\n\t\t\t\t...profile,\n\t\t\t\tsource,\n\t\t\t}),\n\t\t);\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction normalizeDefinitions(\n\tdefinitions: Record<string, ResourceProfileSettings>,\n\tsource: ProfileSource,\n): NormalizedProfile[] {\n\tconst profiles: NormalizedProfile[] = [];\n\tfor (const [name, resources] of Object.entries(definitions)) {\n\t\tconst nameErrors = validateProfileName(name);\n\t\tif (nameErrors.length > 0) continue;\n\t\tprofiles.push({ name, resources: mergeResourceProfileSettings(undefined, resources), source });\n\t}\n\treturn profiles;\n}\n\nexport class ProfileRegistry {\n\tprivate options: ProfileRegistryOptions;\n\tprivate diagnostics: ProfileRegistryDiagnostic[] = [];\n\n\tconstructor(options: ProfileRegistryOptions) {\n\t\tthis.options = options;\n\t}\n\n\tlistDiagnostics(): ProfileRegistryDiagnostic[] {\n\t\tthis.collectCandidates();\n\t\treturn [...this.diagnostics];\n\t}\n\n\tlistProfiles(): NormalizedProfile[] {\n\t\tconst candidates = this.collectCandidates();\n\t\tconst winners = new Map<string, ProfileCandidate>();\n\t\tfor (const candidate of candidates) {\n\t\t\tconst existing = winners.get(candidate.profile.name);\n\t\t\tif (\n\t\t\t\t!existing ||\n\t\t\t\tcandidate.precedence < existing.precedence ||\n\t\t\t\t(candidate.precedence === existing.precedence && candidate.order < existing.order)\n\t\t\t) {\n\t\t\t\twinners.set(candidate.profile.name, candidate);\n\t\t\t}\n\t\t}\n\t\treturn Array.from(winners.values())\n\t\t\t.sort((a, b) => a.profile.name.localeCompare(b.profile.name))\n\t\t\t.map((candidate) => candidate.profile);\n\t}\n\n\tgetProfile(name: string): NormalizedProfile | undefined {\n\t\tconst trimmed = name.trim();\n\t\tif (!trimmed) return undefined;\n\t\treturn this.listProfiles().find((profile) => profile.name === trimmed);\n\t}\n\n\tresolveProfileRef(ref: string, fromDir: string): NormalizedProfile | undefined {\n\t\tconst trimmed = ref.trim();\n\t\tif (!trimmed) return undefined;\n\t\tif (trimmed.startsWith(\"./\") || trimmed.startsWith(\"../\")) {\n\t\t\tconst sourcePath = resolvePath(trimmed, fromDir, { trim: true });\n\t\t\treturn this.loadProfileFile(sourcePath, \"profile-file\", 0)?.profile;\n\t\t}\n\t\treturn this.getProfile(trimmed);\n\t}\n\n\tprivate collectCandidates(): ProfileCandidate[] {\n\t\tthis.diagnostics = [];\n\t\tconst candidates: ProfileCandidate[] = [];\n\t\tlet order = 0;\n\t\tconst add = (profile: NormalizedProfile, precedence: number): void => {\n\t\t\tcandidates.push({ profile, precedence, order: order++ });\n\t\t};\n\n\t\tfor (const profile of normalizeInlineDefinitions(this.options.inlineResourceProfileDefinitions, \"inline\")) {\n\t\t\tadd(profile, 1);\n\t\t}\n\t\tfor (const profile of normalizeSettingsProfiles(this.options.directoryProfileSettings, \"directory-overlay\")) {\n\t\t\tadd({ ...profile, source: \"directory-overlay\" }, 2);\n\t\t}\n\t\tfor (const profile of normalizeSettingsProfiles(this.options.projectSettings, \"project-settings\")) {\n\t\t\tadd({ ...profile, source: \"project-settings\" }, 3);\n\t\t}\n\t\tfor (const profile of this.loadProfileFiles()) {\n\t\t\tadd(profile.profile, profile.precedence);\n\t\t}\n\t\tfor (const profile of this.loadExternalSettingsProfiles()) {\n\t\t\tadd(profile, 4.2);\n\t\t}\n\t\tfor (const profile of normalizeSettingsProfiles(this.options.globalSettings, \"global-settings\")) {\n\t\t\tadd({ ...profile, source: \"global-settings\" }, 5);\n\t\t}\n\t\tfor (const profile of normalizeDefinitions(this.options.discoveredResourceProfileDefinitions, \"embedded\")) {\n\t\t\tadd(profile, 6);\n\t\t}\n\n\t\treturn candidates;\n\t}\n\n\tprivate loadProfileFiles(): ProfileCandidate[] {\n\t\tconst candidates: ProfileCandidate[] = [];\n\t\tlet order = 0;\n\n\t\tconst profilesDir = this.options.profilesDir;\n\t\tif (profilesDir && existsSync(profilesDir)) {\n\t\t\ttry {\n\t\t\t\tconst entries = readdirSync(profilesDir)\n\t\t\t\t\t.filter((entry) => entry.endsWith(\".json\"))\n\t\t\t\t\t.sort();\n\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\tconst sourcePath = join(profilesDir, entry);\n\t\t\t\t\tconst loaded = this.loadProfileFile(sourcePath, \"profile-file\", order++);\n\t\t\t\t\tif (loaded) {\n\t\t\t\t\t\tcandidates.push({ ...loaded, precedence: 4 });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis.diagnostics.push({ source: \"profile-file\", path: profilesDir, message: String(error) });\n\t\t\t}\n\t\t}\n\n\t\tconst externalRoots = this.options.externalResourceRoots ?? [];\n\t\tfor (const root of externalRoots) {\n\t\t\tconst extProfilesDir = join(root, \"profiles\");\n\t\t\tif (existsSync(extProfilesDir)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst entries = readdirSync(extProfilesDir)\n\t\t\t\t\t\t.filter((entry) => entry.endsWith(\".json\"))\n\t\t\t\t\t\t.sort();\n\t\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\t\tconst sourcePath = join(extProfilesDir, entry);\n\t\t\t\t\t\tconst loaded = this.loadProfileFile(sourcePath, \"profile-file\", order++);\n\t\t\t\t\t\tif (loaded) {\n\t\t\t\t\t\t\tcandidates.push({ ...loaded, precedence: 4.1 });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.diagnostics.push({ source: \"profile-file\", path: extProfilesDir, message: String(error) });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn candidates;\n\t}\n\n\tprivate loadExternalSettingsProfiles(): NormalizedProfile[] {\n\t\tconst profiles: NormalizedProfile[] = [];\n\t\tfor (const root of this.options.externalResourceRoots ?? []) {\n\t\t\tprofiles.push(...loadSettingsFileProfiles(join(root, \"settings.json\"), \"external-settings\"));\n\t\t}\n\t\treturn profiles;\n\t}\n\n\tprivate loadProfileFile(sourcePath: string, source: ProfileSource, order: number): ProfileCandidate | undefined {\n\t\ttry {\n\t\t\tconst stats = statSync(sourcePath);\n\t\t\tif (!stats.isFile()) return undefined;\n\t\t\tconst parsed = JSON.parse(readFileSync(sourcePath, \"utf-8\"));\n\t\t\tconst fallbackName = basename(sourcePath, \".json\");\n\t\t\treturn {\n\t\t\t\tprofile: normalizeWrapperProfile({\n\t\t\t\t\tvalue: parsed,\n\t\t\t\t\tsource,\n\t\t\t\t\tsourcePath: resolve(sourcePath),\n\t\t\t\t\tbaseDir: dirname(resolve(sourcePath)),\n\t\t\t\t\tfallbackName,\n\t\t\t\t}),\n\t\t\t\tprecedence: 0,\n\t\t\t\torder,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthis.diagnostics.push({ source, path: sourcePath, message });\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n"]}