akm-cli 0.9.0-beta.9 → 0.9.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/CHANGELOG.md +592 -0
  2. package/README.md +12 -4
  3. package/dist/akm +38 -0
  4. package/dist/akm-migrate-storage +38 -0
  5. package/dist/assets/help/help-improve.md +9 -6
  6. package/dist/assets/hints/cli-hints-full.md +6 -5
  7. package/dist/assets/profiles/default.json +9 -4
  8. package/dist/assets/profiles/frequent.json +1 -1
  9. package/dist/assets/profiles/memory-focus.json +1 -1
  10. package/dist/assets/profiles/proactive-maintenance.json +25 -0
  11. package/dist/assets/profiles/quick.json +1 -1
  12. package/dist/assets/profiles/recombine-only.json +21 -0
  13. package/dist/assets/profiles/reflect-distill.json +30 -0
  14. package/dist/assets/profiles/synthesize.json +15 -0
  15. package/dist/assets/profiles/thorough.json +1 -1
  16. package/dist/assets/prompts/consolidate-system.md +23 -0
  17. package/dist/assets/prompts/contradiction-judge.md +33 -0
  18. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  19. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  20. package/dist/assets/prompts/extract-session.md +11 -3
  21. package/dist/assets/prompts/graph-extract-system.md +1 -0
  22. package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
  23. package/dist/assets/prompts/memory-infer-system.md +1 -0
  24. package/dist/assets/prompts/memory-infer-user.md +5 -0
  25. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  26. package/dist/assets/prompts/procedural-system.md +44 -0
  27. package/dist/assets/prompts/recombine-system.md +40 -0
  28. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  29. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
  39. package/dist/assets/templates/html/health.html +281 -111
  40. package/dist/assets/wiki/ingest-workflow-template.md +45 -16
  41. package/dist/assets/wiki/schema-template.md +4 -4
  42. package/dist/cli/clack.js +56 -0
  43. package/dist/cli/config-migrate.js +7 -1
  44. package/dist/cli/confirm.js +1 -1
  45. package/dist/cli/parse-args.js +46 -1
  46. package/dist/cli/shared.js +28 -0
  47. package/dist/cli.js +25 -21
  48. package/dist/commands/agent/agent-dispatch.js +3 -2
  49. package/dist/commands/agent/agent-support.js +0 -7
  50. package/dist/commands/agent/contribute-cli.js +26 -7
  51. package/dist/commands/config-cli.js +26 -13
  52. package/dist/commands/env/child-env.js +47 -0
  53. package/dist/commands/env/env-cli.js +220 -227
  54. package/dist/commands/env/env.js +14 -67
  55. package/dist/commands/env/secret-cli.js +140 -138
  56. package/dist/commands/feedback-cli.js +156 -155
  57. package/dist/commands/graph/graph-cli.js +5 -13
  58. package/dist/commands/graph/graph.js +3 -3
  59. package/dist/commands/health/advisories.js +151 -0
  60. package/dist/commands/health/checks.js +103 -16
  61. package/dist/commands/health/html-report.js +447 -81
  62. package/dist/commands/health/improve-metrics.js +771 -0
  63. package/dist/commands/health/llm-usage.js +65 -0
  64. package/dist/commands/health/md-report.js +103 -0
  65. package/dist/commands/health/metrics.js +278 -0
  66. package/dist/commands/health/stash-exposure.js +46 -0
  67. package/dist/commands/health/surfaces.js +216 -0
  68. package/dist/commands/health/task-runs.js +135 -0
  69. package/dist/commands/health/types.js +26 -0
  70. package/dist/commands/health/windows.js +195 -0
  71. package/dist/commands/health.js +91 -1091
  72. package/dist/commands/improve/anti-collapse.js +170 -0
  73. package/dist/commands/improve/calibration.js +161 -0
  74. package/dist/commands/improve/collapse-detector.js +421 -0
  75. package/dist/commands/improve/consolidate/chunking.js +141 -0
  76. package/dist/commands/improve/consolidate/eligibility.js +64 -0
  77. package/dist/commands/improve/consolidate/merge.js +145 -0
  78. package/dist/commands/improve/consolidate/sanitize.js +231 -0
  79. package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
  80. package/dist/commands/improve/consolidate.js +1295 -1277
  81. package/dist/commands/improve/dedup.js +482 -0
  82. package/dist/commands/improve/distill/content-repair.js +202 -0
  83. package/dist/commands/improve/distill/promote-memory.js +229 -0
  84. package/dist/commands/improve/distill/quality-gate.js +236 -0
  85. package/dist/commands/improve/distill-guards.js +127 -0
  86. package/dist/commands/improve/distill-promotion-policy.js +826 -167
  87. package/dist/commands/improve/distill.js +228 -605
  88. package/dist/commands/improve/eligibility.js +434 -0
  89. package/dist/commands/improve/encoding-salience.js +205 -0
  90. package/dist/commands/improve/extract-cli.js +179 -59
  91. package/dist/commands/improve/extract-prompt.js +54 -3
  92. package/dist/commands/improve/extract-watch.js +140 -0
  93. package/dist/commands/improve/extract.js +409 -43
  94. package/dist/commands/improve/feedback-valence.js +54 -0
  95. package/dist/commands/improve/hot-probation.js +45 -0
  96. package/dist/commands/improve/improve-auto-accept.js +157 -10
  97. package/dist/commands/improve/improve-cli.js +115 -73
  98. package/dist/commands/improve/improve-profiles.js +28 -8
  99. package/dist/commands/improve/improve-result-file.js +15 -25
  100. package/dist/commands/improve/improve-session.js +58 -0
  101. package/dist/commands/improve/improve.js +485 -2764
  102. package/dist/commands/improve/locks.js +154 -0
  103. package/dist/commands/improve/loop-stages.js +1100 -0
  104. package/dist/commands/improve/memory/memory-belief.js +14 -15
  105. package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
  106. package/dist/commands/improve/memory/memory-improve.js +27 -27
  107. package/dist/commands/improve/outcome-loop.js +270 -0
  108. package/dist/commands/improve/preparation.js +2002 -0
  109. package/dist/commands/improve/proactive-maintenance.js +37 -35
  110. package/dist/commands/improve/procedural.js +398 -0
  111. package/dist/commands/improve/recombine.js +818 -0
  112. package/dist/commands/improve/reflect-noise.js +0 -0
  113. package/dist/commands/improve/reflect.js +206 -45
  114. package/dist/commands/improve/salience.js +455 -0
  115. package/dist/commands/improve/schema-similarity-gate.js +168 -0
  116. package/dist/commands/improve/shared.js +51 -0
  117. package/dist/commands/improve/triage.js +93 -0
  118. package/dist/commands/lint/agent-linter.js +19 -24
  119. package/dist/commands/lint/base-linter.js +173 -60
  120. package/dist/commands/lint/command-linter.js +19 -24
  121. package/dist/commands/lint/env-key-rules.js +38 -1
  122. package/dist/commands/lint/fact-linter.js +39 -0
  123. package/dist/commands/lint/index.js +31 -13
  124. package/dist/commands/lint/memory-linter.js +1 -1
  125. package/dist/commands/lint/registry.js +7 -2
  126. package/dist/commands/lint/task-linter.js +3 -3
  127. package/dist/commands/lint/workflow-linter.js +26 -1
  128. package/dist/commands/observability-cli.js +4 -4
  129. package/dist/commands/proposal/drain-policies.js +13 -4
  130. package/dist/commands/proposal/drain.js +45 -51
  131. package/dist/commands/proposal/legacy-import.js +115 -0
  132. package/dist/commands/proposal/proposal-cli.js +24 -34
  133. package/dist/commands/proposal/proposal.js +2 -1
  134. package/dist/commands/proposal/propose.js +8 -3
  135. package/dist/commands/proposal/repository.js +829 -0
  136. package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
  137. package/dist/commands/proposal/validators/proposals.js +93 -895
  138. package/dist/commands/read/curate.js +410 -111
  139. package/dist/commands/read/knowledge.js +10 -3
  140. package/dist/commands/read/remember-cli.js +133 -138
  141. package/dist/commands/read/search-cli.js +15 -8
  142. package/dist/commands/read/search.js +22 -11
  143. package/dist/commands/read/show.js +106 -14
  144. package/dist/commands/registry-cli.js +76 -87
  145. package/dist/commands/remember.js +11 -12
  146. package/dist/commands/sources/add-cli.js +91 -95
  147. package/dist/commands/sources/history.js +1 -1
  148. package/dist/commands/sources/init.js +66 -18
  149. package/dist/commands/sources/installed-stashes.js +11 -3
  150. package/dist/commands/sources/schema-repair.js +44 -46
  151. package/dist/commands/sources/self-update.js +2 -2
  152. package/dist/commands/sources/source-add.js +7 -3
  153. package/dist/commands/sources/sources-cli.js +3 -3
  154. package/dist/commands/sources/stash-cli.js +19 -39
  155. package/dist/commands/sources/stash-skeleton.js +57 -8
  156. package/dist/commands/tasks/default-tasks.js +15 -2
  157. package/dist/commands/tasks/tasks-cli.js +20 -29
  158. package/dist/commands/tasks/tasks.js +39 -11
  159. package/dist/commands/wiki-cli.js +23 -38
  160. package/dist/commands/workflow-cli.js +15 -1
  161. package/dist/core/asset/asset-registry.js +3 -1
  162. package/dist/core/asset/asset-spec.js +21 -4
  163. package/dist/core/asset/frontmatter.js +188 -167
  164. package/dist/core/asset/markdown.js +8 -0
  165. package/dist/core/authoring-rules.js +92 -0
  166. package/dist/core/common.js +4 -23
  167. package/dist/core/concurrent.js +10 -1
  168. package/dist/core/config/config-io.js +10 -1
  169. package/dist/core/config/config-migration.js +18 -40
  170. package/dist/core/config/config-schema.js +382 -62
  171. package/dist/core/config/config-types.js +3 -3
  172. package/dist/core/config/config.js +67 -22
  173. package/dist/core/deep-merge.js +38 -0
  174. package/dist/core/errors.js +1 -0
  175. package/dist/core/eval/rank-metrics.js +113 -0
  176. package/dist/core/events.js +4 -7
  177. package/dist/core/improve-types.js +47 -8
  178. package/dist/core/logs-db.js +14 -75
  179. package/dist/core/parse.js +36 -16
  180. package/dist/core/paths.js +18 -18
  181. package/dist/core/standards/resolve-standards-context.js +87 -0
  182. package/dist/core/standards/resolve-stash-standards.js +99 -0
  183. package/dist/core/standards/resolve-type-conventions.js +66 -0
  184. package/dist/core/state/migrations.js +770 -0
  185. package/dist/core/state-db.js +132 -1126
  186. package/dist/core/structured.js +69 -0
  187. package/dist/core/time.js +53 -0
  188. package/dist/core/warn.js +21 -0
  189. package/dist/core/write-source.js +37 -0
  190. package/dist/indexer/db/db.js +259 -769
  191. package/dist/indexer/db/entry-mapper.js +41 -0
  192. package/dist/indexer/db/graph-db.js +129 -86
  193. package/dist/indexer/db/llm-cache.js +2 -2
  194. package/dist/indexer/db/schema.js +516 -0
  195. package/dist/indexer/ensure-index.js +36 -92
  196. package/dist/indexer/feedback/utility-policy.js +75 -0
  197. package/dist/indexer/graph/graph-boost.js +51 -41
  198. package/dist/indexer/graph/graph-extraction.js +207 -4
  199. package/dist/indexer/index-writer-lock.js +18 -11
  200. package/dist/indexer/index-written-assets.js +105 -0
  201. package/dist/indexer/indexer.js +182 -204
  202. package/dist/indexer/passes/dir-staleness.js +114 -0
  203. package/dist/indexer/passes/memory-inference.js +13 -5
  204. package/dist/indexer/passes/metadata.js +20 -0
  205. package/dist/indexer/read-preflight.js +23 -0
  206. package/dist/indexer/search/db-search.js +89 -13
  207. package/dist/indexer/search/fts-query.js +51 -0
  208. package/dist/indexer/search/ranking-contributors.js +95 -9
  209. package/dist/indexer/search/ranking.js +79 -3
  210. package/dist/indexer/search/search-fields.js +6 -0
  211. package/dist/indexer/search/search-source.js +32 -21
  212. package/dist/indexer/search/semantic-status.js +4 -0
  213. package/dist/indexer/walk/matchers.js +9 -0
  214. package/dist/indexer/walk/walker.js +21 -13
  215. package/dist/integrations/agent/builders.js +39 -13
  216. package/dist/integrations/agent/config.js +20 -59
  217. package/dist/integrations/agent/detect.js +9 -0
  218. package/dist/integrations/agent/index.js +3 -19
  219. package/dist/integrations/agent/model-aliases.js +7 -2
  220. package/dist/integrations/agent/profiles.js +7 -1
  221. package/dist/integrations/agent/prompts.js +75 -9
  222. package/dist/integrations/agent/runner-dispatch.js +59 -0
  223. package/dist/integrations/agent/runner.js +13 -9
  224. package/dist/integrations/agent/spawn.js +69 -67
  225. package/dist/integrations/harnesses/claude/agent-builder.js +1 -1
  226. package/dist/integrations/harnesses/claude/index.js +2 -0
  227. package/dist/integrations/harnesses/claude/session-log.js +10 -0
  228. package/dist/integrations/harnesses/index.js +2 -3
  229. package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
  230. package/dist/integrations/harnesses/opencode/index.js +2 -0
  231. package/dist/integrations/harnesses/opencode/session-log.js +173 -3
  232. package/dist/integrations/harnesses/opencode-sdk/index.js +2 -2
  233. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +98 -17
  234. package/dist/integrations/harnesses/types.js +1 -0
  235. package/dist/integrations/session-logs/index.js +16 -0
  236. package/dist/llm/call-ai.js +2 -2
  237. package/dist/llm/client.js +34 -11
  238. package/dist/llm/embedder.js +67 -4
  239. package/dist/llm/embedders/cache.js +3 -1
  240. package/dist/llm/embedders/deterministic.js +66 -0
  241. package/dist/llm/embedders/local.js +73 -3
  242. package/dist/llm/feature-gate.js +16 -15
  243. package/dist/llm/graph-extract.js +67 -44
  244. package/dist/llm/memory-infer-impl.js +138 -0
  245. package/dist/llm/memory-infer.js +1 -127
  246. package/dist/llm/metadata-enhance.js +44 -31
  247. package/dist/llm/structured-call.js +49 -0
  248. package/dist/migrate-storage-node.mjs +8 -0
  249. package/dist/output/context.js +5 -5
  250. package/dist/output/renderers.js +85 -14
  251. package/dist/output/shapes/curate.js +14 -2
  252. package/dist/output/shapes/helpers.js +0 -3
  253. package/dist/output/shapes/passthrough.js +2 -1
  254. package/dist/output/text/helpers.js +29 -1
  255. package/dist/output/text/workflow.js +1 -0
  256. package/dist/registry/providers/skills-sh.js +21 -147
  257. package/dist/registry/providers/static-index.js +15 -157
  258. package/dist/registry/resolve.js +27 -9
  259. package/dist/runtime.js +25 -1
  260. package/dist/scripts/migrate-storage.js +2661 -2369
  261. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +883 -596
  262. package/dist/setup/detect.js +9 -0
  263. package/dist/setup/legacy-config.js +106 -0
  264. package/dist/setup/prompt.js +57 -0
  265. package/dist/setup/providers.js +14 -0
  266. package/dist/setup/registry-stash-loader.js +12 -0
  267. package/dist/setup/semantic-assets.js +124 -0
  268. package/dist/setup/setup.js +52 -1614
  269. package/dist/setup/steps/connection.js +734 -0
  270. package/dist/setup/steps/output.js +31 -0
  271. package/dist/setup/steps/platforms.js +124 -0
  272. package/dist/setup/steps/semantic.js +27 -0
  273. package/dist/setup/steps/sources.js +222 -0
  274. package/dist/setup/steps/stashdir.js +42 -0
  275. package/dist/setup/steps/tasks.js +152 -0
  276. package/dist/sources/include.js +6 -2
  277. package/dist/sources/providers/filesystem.js +0 -1
  278. package/dist/sources/providers/git-install.js +210 -0
  279. package/dist/sources/providers/git-provider.js +234 -0
  280. package/dist/sources/providers/git-stash.js +248 -0
  281. package/dist/sources/providers/git.js +10 -661
  282. package/dist/sources/providers/npm.js +2 -6
  283. package/dist/sources/providers/provider-utils.js +13 -7
  284. package/dist/sources/providers/sync-from-ref.js +9 -1
  285. package/dist/sources/providers/website.js +9 -5
  286. package/dist/sources/website-ingest.js +187 -29
  287. package/dist/sources/wiki-fetchers/registry.js +53 -0
  288. package/dist/sources/wiki-fetchers/youtube.js +239 -0
  289. package/dist/storage/database.js +45 -10
  290. package/dist/storage/managed-db.js +82 -0
  291. package/dist/storage/repositories/canaries-repository.js +107 -0
  292. package/dist/storage/repositories/consolidation-repository.js +38 -0
  293. package/dist/storage/repositories/embeddings-repository.js +72 -0
  294. package/dist/storage/repositories/events-repository.js +187 -0
  295. package/dist/storage/repositories/extract-sessions-repository.js +96 -0
  296. package/dist/storage/repositories/improve-runs-repository.js +146 -0
  297. package/dist/storage/repositories/index-db.js +14 -8
  298. package/dist/storage/repositories/proposals-repository.js +220 -0
  299. package/dist/storage/repositories/recombine-repository.js +213 -0
  300. package/dist/storage/repositories/registry-cache.js +93 -0
  301. package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
  302. package/dist/storage/repositories/task-history-repository.js +93 -0
  303. package/dist/storage/sqlite-pragmas.js +146 -0
  304. package/dist/tasks/backends/cron.js +1 -1
  305. package/dist/tasks/backends/index.js +9 -0
  306. package/dist/tasks/backends/launchd.js +1 -1
  307. package/dist/tasks/backends/schtasks.js +1 -1
  308. package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
  309. package/dist/tasks/runner.js +15 -13
  310. package/dist/text-import-hook.mjs +0 -0
  311. package/dist/wiki/wiki.js +52 -11
  312. package/dist/workflows/cli.js +1 -0
  313. package/dist/workflows/db.js +3 -4
  314. package/dist/workflows/runtime/runs.js +43 -118
  315. package/dist/workflows/runtime/workflow-asset-loader.js +125 -0
  316. package/dist/workflows/validate-summary.js +2 -7
  317. package/docs/README.md +69 -18
  318. package/docs/data-and-telemetry.md +5 -4
  319. package/docs/migration/release-notes/0.7.0.md +1 -1
  320. package/docs/migration/release-notes/0.9.0.md +39 -0
  321. package/package.json +10 -10
  322. package/dist/assets/tasks/core/update-stashes.yml +0 -4
  323. package/dist/commands/db-cli.js +0 -23
  324. package/dist/indexer/db/db-backup.js +0 -376
  325. package/dist/indexer/passes/staleness-detect.js +0 -488
@@ -1,8 +1,68 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import fs from "node:fs";
5
+ import { makeAssetRef } from "../../core/asset/asset-ref.js";
6
+ import { getStateDbPath } from "../../core/state-db.js";
7
+ import { openDatabase } from "../../storage/database.js";
4
8
  import { getUtilityScoresByIds } from "../db/db.js";
5
- import { applyScoreContributors, applyUtilityContributors } from "./ranking-contributors.js";
9
+ import { applyContributorAblation, applyScoreContributors, applyUtilityContributors, defaultRankingContributors, defaultUtilityRankingContributors, } from "./ranking-contributors.js";
10
+ /**
11
+ * R2 — best-effort load of `asset_salience.rank_score` from state.db for the
12
+ * ranked items. Fail-open: any error (state.db locked by a concurrent improve
13
+ * run, missing table, unreadable path) returns an empty map, which makes the
14
+ * salience contributor a no-op — byte-identical to pre-R2 ranking.
15
+ *
16
+ * Deliberately NOT `openStateDatabase()`: that helper runs migrations and sets
17
+ * a 30 s busy timeout — too heavy for a search hot path. This opens read-only,
18
+ * never creates or migrates state.db (missing file / missing table = empty
19
+ * map), and caps lock waits at 250 ms so a concurrent improve run can only
20
+ * ever cost the search a quarter second, not a stall.
21
+ */
22
+ export function loadSalienceRankScores(items) {
23
+ const result = new Map();
24
+ if (items.length === 0)
25
+ return result;
26
+ try {
27
+ const dbPath = getStateDbPath();
28
+ if (!fs.existsSync(dbPath))
29
+ return result; // improve loop has never run here
30
+ const idByRef = new Map();
31
+ for (const item of items) {
32
+ idByRef.set(makeAssetRef(item.entry.type, item.entry.name), item.id);
33
+ }
34
+ const stateDb = openDatabase(dbPath, { readonly: true });
35
+ try {
36
+ try {
37
+ stateDb.exec("PRAGMA busy_timeout = 250");
38
+ }
39
+ catch {
40
+ // pragma failure on a readonly handle is fine — default timeout applies
41
+ }
42
+ const refs = [...idByRef.keys()];
43
+ const CHUNK = 500;
44
+ for (let i = 0; i < refs.length; i += CHUNK) {
45
+ const chunk = refs.slice(i, i + CHUNK);
46
+ const placeholders = chunk.map(() => "?").join(",");
47
+ const rows = stateDb
48
+ .prepare(`SELECT asset_ref, rank_score FROM asset_salience WHERE asset_ref IN (${placeholders})`)
49
+ .all(...chunk);
50
+ for (const row of rows) {
51
+ const id = idByRef.get(row.asset_ref);
52
+ if (id !== undefined)
53
+ result.set(id, row.rank_score);
54
+ }
55
+ }
56
+ }
57
+ finally {
58
+ stateDb.close();
59
+ }
60
+ }
61
+ catch {
62
+ // Fail open — search must never break because state.db is unavailable.
63
+ }
64
+ return result;
65
+ }
6
66
  export function normalizeFtsScores(results) {
7
67
  const ftsScoreMap = new Map();
8
68
  if (results.length === 0)
@@ -20,6 +80,7 @@ export function normalizeFtsScores(results) {
20
80
  export function combineSearchScores(options) {
21
81
  const FTS_WEIGHT = 0.7;
22
82
  const VEC_WEIGHT = 0.3;
83
+ const excludeTypeSet = options.excludeTypes && options.excludeTypes.length > 0 ? new Set(options.excludeTypes) : null;
23
84
  const scored = [];
24
85
  const seenIds = new Set();
25
86
  for (const [id, { score: ftsScore, result }] of options.ftsScoreMap) {
@@ -42,6 +103,9 @@ export function combineSearchScores(options) {
42
103
  continue;
43
104
  if (options.typeFilter && found.entry.type !== options.typeFilter)
44
105
  continue;
106
+ // #627 — drop vector-only neighbors whose type is excluded on the default path.
107
+ if (excludeTypeSet?.has(found.entry.type))
108
+ continue;
45
109
  scored.push({
46
110
  id,
47
111
  entry: found.entry,
@@ -63,19 +127,31 @@ export function applyRankingRules(options) {
63
127
  graphContext: options.graphContext,
64
128
  projectContext: options.projectContext,
65
129
  };
130
+ // Eval/debug only: AKM_ABLATE_CONTRIBUTORS lets the ablation harness drop
131
+ // named contributors to measure their effect. Resolved once per query;
132
+ // a no-op (full lists) when the env var is unset — see applyContributorAblation.
133
+ const ablateEnv = process.env.AKM_ABLATE_CONTRIBUTORS;
134
+ const activeScoreContributors = applyContributorAblation(defaultRankingContributors, ablateEnv);
135
+ const activeUtilityContributors = applyContributorAblation(defaultUtilityRankingContributors, ablateEnv);
66
136
  for (const item of options.items) {
67
- applyScoreContributors(item, rankingContext);
137
+ applyScoreContributors(item, rankingContext, activeScoreContributors);
68
138
  }
69
139
  const { global: utilScoresMap, scoped: scopedUtilScoresMap } = getUtilityScoresByIds(options.db, options.items.map((item) => item.id), options.scopeKey);
140
+ // R2 — compose the improve loop's salience into user-facing ranking.
141
+ // undefined = load from state.db (default); null = explicitly disabled.
142
+ const salienceRankScores = options.salienceRankScores === null
143
+ ? new Map()
144
+ : (options.salienceRankScores ?? loadSalienceRankScores(options.items));
70
145
  const utilityContext = {
71
146
  ...rankingContext,
72
147
  utilityScores: utilScoresMap,
73
148
  scopedUtilityScores: scopedUtilScoresMap,
74
149
  utilityDecayConfig: options.utilityDecayConfig,
75
150
  positiveFeedbackCounts: options.positiveFeedbackCounts,
151
+ salienceRankScores,
76
152
  };
77
153
  for (const item of options.items) {
78
- applyUtilityContributors(item, utilityContext);
154
+ applyUtilityContributors(item, utilityContext, activeUtilityContributors);
79
155
  }
80
156
  return options.items;
81
157
  }
@@ -11,6 +11,12 @@
11
11
  * - hints: searchHints + examples + usage + intent fields
12
12
  * - content: TOC headings (lowest-weight catch-all)
13
13
  */
14
+ // NOTE (R5): the collapse detector's frozen canary queries are built from the
15
+ // same surface this function indexes (name tokens / tags / description) and
16
+ // scored via FTS against it. Changing what buildSearchFields includes shifts
17
+ // the detector's recall baseline for ALL existing canary sets — coordinate
18
+ // with src/commands/improve/collapse-detector.ts (buildCanaryQuery) and expect
19
+ // operators to re-mint via `akm improve canary --refresh` after such a change.
14
20
  export function buildSearchFields(entry) {
15
21
  const name = entry.name.replace(/[-_]/g, " ").toLowerCase();
16
22
  const description = (entry.description ?? "").toLowerCase();
@@ -10,8 +10,6 @@ import { resolveSourceProviderFactory } from "../../sources/provider-factory.js"
10
10
  // before resolveEntryContentDir() runs.
11
11
  import "../../sources/providers/index.js";
12
12
  import { warn } from "../../core/warn.js";
13
- import { ensureGitMirror, getCachePaths, parseGitRepoUrl } from "../../sources/providers/git.js";
14
- import { ensureWebsiteMirror } from "../../sources/website-ingest.js";
15
13
  // Legacy "context-hub" / "github" type aliases are normalized to "git" at
16
14
  // config-load time (see src/config.ts), so this set only contains the canonical
17
15
  // type.
@@ -169,14 +167,26 @@ export function getWritableStashDirs(overrideStashDir, existingConfig) {
169
167
  }
170
168
  /**
171
169
  * Find which source a file path belongs to.
170
+ *
171
+ * Longest-matching-prefix wins: a source nested inside another (e.g. `akm add
172
+ * ./sub` where `./sub` lives under the primary stash — which is always
173
+ * `sources[0]`) is the more specific owner and must win over the enclosing
174
+ * source regardless of array order. A first-match-in-order scan would
175
+ * misattribute every asset under the nested source to the primary stash,
176
+ * corrupting origin / editability / provenance decisions for the affected files.
172
177
  */
173
178
  export function findSourceForPath(filePath, sources) {
174
179
  const resolved = path.resolve(filePath);
180
+ let best;
181
+ let bestLen = -1;
175
182
  for (const source of sources) {
176
- if (resolved.startsWith(path.resolve(source.path) + path.sep))
177
- return source;
183
+ const base = path.resolve(source.path);
184
+ if (resolved.startsWith(base + path.sep) && base.length > bestLen) {
185
+ best = source;
186
+ bestLen = base.length;
187
+ }
178
188
  }
179
- return undefined;
189
+ return best;
180
190
  }
181
191
  /**
182
192
  * Return the primary stash source (first entry in the list).
@@ -261,31 +271,32 @@ function isValidDirectory(dir) {
261
271
  export async function ensureSourceCaches(config, options) {
262
272
  const cfg = config ?? loadConfig();
263
273
  const force = options?.force === true;
264
- const entries = getSources(cfg);
265
- for (const entry of entries) {
266
- if (!GIT_STASH_TYPES.has(entry.type) || !entry.url || entry.enabled === false)
274
+ // Polymorphic refresh: walk every enabled source through its registered
275
+ // provider and call `sync()`. Every cache-backed kind (git, website, npm)
276
+ // refreshes the same way a bad source warns and is skipped without
277
+ // aborting the others. The git content/-subdir layout convention stays in
278
+ // resolveEntryContentDir.
279
+ for (const entry of getSources(cfg)) {
280
+ if (entry.enabled === false)
281
+ continue;
282
+ const factory = resolveSourceProviderFactory(entry.type);
283
+ if (!factory)
267
284
  continue;
285
+ let provider;
268
286
  try {
269
- const repo = parseGitRepoUrl(entry.url);
270
- const cachePaths = getCachePaths(repo.canonicalUrl);
271
- await ensureGitMirror(repo, cachePaths, {
272
- requireRepoDir: true,
273
- writable: entry.writable === true,
274
- force,
275
- });
287
+ provider = factory(entry);
276
288
  }
277
289
  catch (err) {
278
- warn(`Warning: failed to refresh git mirror for "${entry.url}": ${err instanceof Error ? err.message : String(err)}`);
290
+ warn(`Warning: failed to construct ${entry.type} source provider for "${entry.name ?? entry.url ?? entry.path}": ${err instanceof Error ? err.message : String(err)}`);
291
+ continue;
279
292
  }
280
- }
281
- for (const entry of entries) {
282
- if (entry.type !== "website" || !entry.url || entry.enabled === false)
293
+ if (!provider.sync)
283
294
  continue;
284
295
  try {
285
- await ensureWebsiteMirror(entry, { requireStashDir: true, force });
296
+ await provider.sync({ force });
286
297
  }
287
298
  catch (err) {
288
- warn(`Warning: failed to refresh website stash for "${entry.url}": ${err instanceof Error ? err.message : String(err)}`);
299
+ warn(`Warning: failed to refresh ${provider.kind} source "${provider.name}": ${err instanceof Error ? err.message : String(err)}`);
289
300
  }
290
301
  }
291
302
  }
@@ -4,8 +4,12 @@
4
4
  import fs from "node:fs";
5
5
  import { writeFileAtomic } from "../../core/common.js";
6
6
  import { getCacheDir, getSemanticStatusPath } from "../../core/paths.js";
7
+ import { DETERMINISTIC_EMBED_MODEL_ID, isDeterministicEmbedEnabled } from "../../llm/embedders/deterministic.js";
7
8
  import { DEFAULT_LOCAL_MODEL } from "../../llm/embedders/local.js";
8
9
  export function deriveSemanticProviderFingerprint(embedding) {
10
+ if (isDeterministicEmbedEnabled()) {
11
+ return `deterministic:${DETERMINISTIC_EMBED_MODEL_ID}`;
12
+ }
9
13
  if (embedding?.endpoint) {
10
14
  return `remote:${embedding.endpoint}|${embedding.model}|${embedding.dimension ?? "default"}`;
11
15
  }
@@ -78,6 +78,15 @@ const DIR_TYPE_MAP = [
78
78
  type: "session",
79
79
  test: (ext) => ext === ".md",
80
80
  },
81
+ {
82
+ // Durable stash-level facts live under `facts/<category>/<name>.md`.
83
+ // classifyByDirectory walks every ancestor dir, so nested category
84
+ // subdirs still match. Without this entry a fact file would fall through
85
+ // to classifyBySmartMd and be mistyped as `knowledge`.
86
+ dir: "facts",
87
+ type: "fact",
88
+ test: (ext) => ext === ".md",
89
+ },
81
90
  ];
82
91
  const COMMAND_PLACEHOLDER_RE = /\$ARGUMENTS|\$[123]\b/;
83
92
  // Files that should never be treated as the typed asset for the surrounding
@@ -148,20 +148,28 @@ function isInsideGitRepo(dir) {
148
148
  * read (e.g. permission errors).
149
149
  */
150
150
  export function* walkMarkdownFiles(root) {
151
- let entries;
152
- try {
153
- entries = fs.readdirSync(root, { withFileTypes: true });
154
- }
155
- catch {
156
- return;
157
- }
158
- for (const entry of entries) {
159
- const full = path.join(root, entry.name);
160
- if (entry.isDirectory()) {
161
- yield* walkMarkdownFiles(full);
151
+ const stack = [root];
152
+ while (stack.length > 0) {
153
+ const current = stack.pop();
154
+ if (!current)
155
+ continue;
156
+ let entries;
157
+ try {
158
+ entries = fs.readdirSync(current, { withFileTypes: true });
162
159
  }
163
- else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
164
- yield full;
160
+ catch {
161
+ continue;
162
+ }
163
+ for (const entry of entries) {
164
+ const full = path.join(current, entry.name);
165
+ if (entry.isSymbolicLink())
166
+ continue;
167
+ if (entry.isDirectory()) {
168
+ stack.push(full);
169
+ }
170
+ else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
171
+ yield full;
172
+ }
165
173
  }
166
174
  }
167
175
  }
@@ -12,8 +12,8 @@
12
12
  * Adding a new platform: implement `AgentCommandBuilder`, add to
13
13
  * `BUILTIN_BUILDERS`. Nothing else changes.
14
14
  */
15
- import { claudeBuilder } from "../harnesses/claude/agent-builder.js";
16
- import { opencodeBuilder } from "../harnesses/opencode/agent-builder.js";
15
+ import { ConfigError } from "../../core/errors.js";
16
+ import { HARNESS_REGISTRY } from "../harnesses/index.js";
17
17
  // Types + shared validation helpers live in the leaf module `builder-shared.ts`
18
18
  // so per-harness builders (harnesses/claude/agent-builder.ts) can depend on them
19
19
  // without importing this file back — avoiding an init-order cycle through
@@ -21,6 +21,7 @@ import { opencodeBuilder } from "../harnesses/opencode/agent-builder.js";
21
21
  // sites keep working.
22
22
  import { assertNotFlag } from "./builder-shared.js";
23
23
  import { resolveModel } from "./model-aliases.js";
24
+ import { getBuiltinAgentProfile } from "./profiles.js";
24
25
  export { assertNotFlag, normalizeTools } from "./builder-shared.js";
25
26
  // ── Platform builders ─────────────────────────────────────────────────────────
26
27
  // The OpenCode builder was migrated to its harness directory in #564
@@ -43,7 +44,7 @@ const defaultBuilder = {
43
44
  args.push("--system-prompt", req.systemPrompt);
44
45
  }
45
46
  if (req.model) {
46
- const resolved = resolveModel(req.model, profile.name, profile.modelAliases);
47
+ const resolved = resolveModel(req.model, profile.name, profile.modelAliases, profile.globalModelAliases);
47
48
  args.push("--model", resolved);
48
49
  }
49
50
  args.push("--");
@@ -52,17 +53,42 @@ const defaultBuilder = {
52
53
  },
53
54
  };
54
55
  // ── Registry ──────────────────────────────────────────────────────────────────
55
- const BUILTIN_BUILDERS = {
56
- opencode: opencodeBuilder,
57
- "opencode-headless": opencodeBuilder,
58
- claude: claudeBuilder,
59
- "claude-headless": claudeBuilder,
60
- };
61
56
  /**
62
- * Return the builder for the given platform name, falling back to the default
63
- * builder for unknown platforms. Custom builders injected via tests can be
64
- * passed as `registry`.
57
+ * DERIVED from `HARNESS_REGISTRY` (P0.5 registry-drift fix): each harness that
58
+ * owns an `agentBuilder` is registered under its canonical id, its
59
+ * `<id>-headless` profile variant, and every alias. Previously this was a
60
+ * hand-maintained map that could (and did) drift from the harness registry.
61
+ */
62
+ const BUILTIN_BUILDERS = (() => {
63
+ const registry = {};
64
+ for (const harness of HARNESS_REGISTRY) {
65
+ if (!harness.agentBuilder)
66
+ continue;
67
+ registry[harness.id] = harness.agentBuilder;
68
+ registry[`${harness.id}-headless`] = harness.agentBuilder;
69
+ for (const alias of harness.aliases)
70
+ registry[alias] = harness.agentBuilder;
71
+ }
72
+ return Object.freeze(registry);
73
+ })();
74
+ /**
75
+ * Return the builder for the given platform name.
76
+ *
77
+ * A *custom* profile (unknown platform) falls back to the default builder —
78
+ * that generic `--system-prompt`/`--model`/`--` shape is the documented
79
+ * contract for user-defined wrappers. A *known built-in agent CLI* with no
80
+ * dedicated builder (codex, gemini, aider + their `-headless` variants) is a
81
+ * loud `ConfigError` instead: the default flag shape is wrong for those CLIs
82
+ * (aider, for one, treats positionals as file names), so the old silent
83
+ * fallback produced a broken command that "ran" and failed downstream.
84
+ * Custom builders injected via tests can be passed as `registry`.
65
85
  */
66
86
  export function getCommandBuilder(platform, registry = BUILTIN_BUILDERS) {
67
- return registry[platform] ?? defaultBuilder;
87
+ const found = registry[platform];
88
+ if (found)
89
+ return found;
90
+ if (getBuiltinAgentProfile(platform)) {
91
+ throw new ConfigError(`agent dispatch for "${platform}" is not supported yet: no command builder exists for this CLI, and the generic flag shape would produce a broken command.`, "INVALID_CONFIG_FILE", 'Use an "opencode" or "claude" profile, or — if your CLI is flag-compatible with one of those — set `profiles.agent.<name>.commandBuilder` to "opencode" or "claude".');
92
+ }
93
+ return defaultBuilder;
68
94
  }
@@ -1,9 +1,7 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- import { VALID_HARNESS_IDS } from "../../core/config/config.js";
5
4
  import { ConfigError } from "../../core/errors.js";
6
- import { warn } from "../../core/warn.js";
7
5
  import { BUILTIN_AGENT_PROFILE_NAMES, getBuiltinAgentProfile, listBuiltinAgentProfiles, } from "./profiles.js";
8
6
  /**
9
7
  * Default hard timeout for an agent CLI (60s — matches the value used in
@@ -15,7 +13,7 @@ export const DEFAULT_AGENT_TIMEOUT_MS = 60_000;
15
13
  * user override (`profiles.agent[name]`) on top of the built-in profile (if
16
14
  * any). Returns `undefined` when neither yields a usable profile.
17
15
  */
18
- export function resolveAgentProfile(name, overrides) {
16
+ export function resolveAgentProfile(name, overrides, globalModelAliases) {
19
17
  const builtin = getBuiltinAgentProfile(name);
20
18
  const platform = overrides?.platform;
21
19
  // For opencode-sdk profiles, allow synthesizing without a built-in.
@@ -32,8 +30,14 @@ export function resolveAgentProfile(name, overrides) {
32
30
  parseOutput: "text",
33
31
  ...(sdkMode ? { sdkMode: true } : {}),
34
32
  };
35
- if (!overrides)
36
- return base;
33
+ if (!overrides) {
34
+ return globalModelAliases ? { ...base, globalModelAliases } : base;
35
+ }
36
+ // Per-profile aliases from config extend (and shadow) the built-in
37
+ // profile's own entries. (Previously always used base.modelAliases, so the
38
+ // documented `profiles.agent.<name>.modelAliases` config was silently
39
+ // dropped — same bug class as the timeoutMs override below.)
40
+ const modelAliases = overrides.modelAliases || base.modelAliases ? { ...base.modelAliases, ...overrides.modelAliases } : undefined;
37
41
  return {
38
42
  name,
39
43
  bin: overrides.bin ?? base.bin,
@@ -41,22 +45,29 @@ export function resolveAgentProfile(name, overrides) {
41
45
  stdio: base.stdio,
42
46
  env: base.env,
43
47
  envPassthrough: base.envPassthrough,
44
- timeoutMs: base.timeoutMs,
48
+ // Honor a user-configured `profiles.agent.<name>.timeoutMs` override; fall
49
+ // back to the built-in profile's value. (Previously always used base, so
50
+ // the documented config override was silently ignored — callers had to pass
51
+ // a CLI flag like `--timeout-ms`.) runAgent (spawn.ts) reads profile.timeoutMs
52
+ // when no per-call timeout is supplied, so this makes the config knob work.
53
+ timeoutMs: overrides.timeoutMs ?? base.timeoutMs,
45
54
  parseOutput: base.parseOutput,
46
55
  ...(sdkMode ? { sdkMode: true } : {}),
47
56
  model: overrides.model ?? base.model,
48
57
  endpoint: base.endpoint,
49
58
  apiKey: base.apiKey,
50
59
  commandBuilder: base.commandBuilder,
51
- modelAliases: base.modelAliases,
60
+ modelAliases,
61
+ ...(globalModelAliases ? { globalModelAliases } : {}),
52
62
  };
53
63
  }
54
64
  /**
55
65
  * Resolve the runnable profile for `name`, or `undefined` if none is
56
- * available (no built-in and no user override).
66
+ * available (no built-in and no user override). Threads the config-root
67
+ * `modelAliases` tier table onto the resolved profile.
57
68
  */
58
69
  export function resolveProfileFromConfig(name, config) {
59
- return resolveAgentProfile(name, config?.profiles?.agent?.[name]);
70
+ return resolveAgentProfile(name, config?.profiles?.agent?.[name], config?.modelAliases);
60
71
  }
61
72
  /**
62
73
  * Return the names of every agent profile available in `config` — built-ins
@@ -134,53 +145,3 @@ export function listResolvedAgentProfiles(config) {
134
145
  }
135
146
  return resolved;
136
147
  }
137
- /**
138
- * Parse the v2 `profiles.agent` map (AgentProfileConfigV2 shape with required
139
- * `platform` field). Returns a map of profile name → AgentProfileConfigV2.
140
- */
141
- export function parseAgentProfilesMapV2(value) {
142
- if (typeof value !== "object" || value === null || Array.isArray(value))
143
- return undefined;
144
- const out = {};
145
- // Derives from the canonical harness-id source of truth (#565).
146
- const VALID_PLATFORMS = VALID_HARNESS_IDS;
147
- for (const [name, raw] of Object.entries(value)) {
148
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
149
- warn(`[akm] Ignoring profiles.agent["${name}"]: expected an object.`);
150
- continue;
151
- }
152
- const obj = raw;
153
- if (!VALID_PLATFORMS.includes(obj.platform)) {
154
- warn(`[akm] Ignoring profiles.agent["${name}"]: missing or invalid "platform" (must be one of: ${VALID_PLATFORMS.join(", ")}).`);
155
- continue;
156
- }
157
- const profile = {
158
- platform: obj.platform,
159
- };
160
- if (typeof obj.bin === "string" && obj.bin.trim())
161
- profile.bin = obj.bin.trim();
162
- if (Array.isArray(obj.args) && obj.args.every((a) => typeof a === "string")) {
163
- profile.args = obj.args;
164
- }
165
- if (typeof obj.workspace === "string" && obj.workspace.trim())
166
- profile.workspace = obj.workspace.trim();
167
- if (typeof obj.model === "string" && obj.model.trim())
168
- profile.model = obj.model.trim();
169
- out[name] = profile;
170
- }
171
- return Object.keys(out).length > 0 ? out : undefined;
172
- }
173
- /**
174
- * Stub kept for source-compat with callers that previously used the v1 agent
175
- * config parser. After 0.8.0 there is no separate `agent` block to parse — the
176
- * loaded `AkmConfig` already carries the agent data on `profiles.agent` and
177
- * `defaults.agent`. This function is a no-op alias for those callers.
178
- *
179
- * @deprecated v0.8.0 — the unified `AkmConfig` IS the agent config. Use the
180
- * profile/defaults accessors above instead.
181
- */
182
- export function parseAgentConfig(_value) {
183
- // No-op: there is no separate agent block in 0.8.0. Callers should pass
184
- // their loaded `AkmConfig` directly to `requireAgentProfile` etc.
185
- return undefined;
186
- }
@@ -55,6 +55,11 @@ export function defaultWhich(bin, envSource = process.env) {
55
55
  }
56
56
  return undefined;
57
57
  }
58
+ let detectOverrides;
59
+ /** TEST-ONLY. Swap the detection implementations; pass undefined to restore. */
60
+ export function _setAgentDetectForTests(fakes) {
61
+ detectOverrides = fakes;
62
+ }
58
63
  /**
59
64
  * Probe every resolvable agent profile (built-ins plus user overrides)
60
65
  * for an installed CLI.
@@ -64,6 +69,8 @@ export function defaultWhich(bin, envSource = process.env) {
64
69
  * @param whichFn Binary lookup. Tests should inject a stub.
65
70
  */
66
71
  export function detectAgentCliProfiles(agent, whichFn = defaultWhich) {
72
+ if (detectOverrides?.detectAgentCliProfiles)
73
+ return detectOverrides.detectAgentCliProfiles(agent, whichFn);
67
74
  const profiles = listResolvedAgentProfiles(agent);
68
75
  return profiles.map((profile) => probeProfile(profile, whichFn));
69
76
  }
@@ -87,6 +94,8 @@ function probeProfile(profile, whichFn) {
87
94
  * writing `agent.default`.
88
95
  */
89
96
  export function pickDefaultAgentProfile(results, existingDefault) {
97
+ if (detectOverrides?.pickDefaultAgentProfile)
98
+ return detectOverrides.pickDefaultAgentProfile(results, existingDefault);
90
99
  if (existingDefault) {
91
100
  const match = results.find((r) => r.name === existingDefault && r.available);
92
101
  if (match)
@@ -1,26 +1,10 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /**
5
- * Internal entry point for the `agent` integration. CLI-only project — no
6
- * public exports map. Other akm modules import from this barrel for the
7
- * sake of grouping imports.
8
- *
9
- * Surface:
10
- * • Types: AgentProfile, AgentConfig, AgentRunResult, AgentFailureReason.
11
- * • Profiles: getBuiltinAgentProfile, listBuiltinAgentProfiles, BUILTIN_AGENT_PROFILE_NAMES.
12
- * • Config: parseAgentConfig, resolveProfileFromConfig, requireAgentProfile, listResolvedAgentProfiles, listAgentProfileNames.
13
- * • Spawn: runAgent. Builders: getCommandBuilder, AgentCommandBuilder, AgentDispatchRequest — platform-specific argv construction.
14
- * • Detection: detectAgentCliProfiles, pickDefaultAgentProfile, defaultWhich.
15
- */
16
- // The OpenCode SDK runner moved to its harness directory in #564
17
- // (`harnesses/opencode-sdk/`). Re-exported here so existing `agent/index`
18
- // import sites keep working.
19
- export { runAgentSdk } from "../harnesses/opencode-sdk/index.js";
20
4
  export { getCommandBuilder } from "./builders.js";
21
- export { DEFAULT_AGENT_TIMEOUT_MS, listAgentProfileNames, listResolvedAgentProfiles, parseAgentConfig, requireAgentProfile, resolveAgentProfile, resolveDefaultProfileName, resolveProfileFromConfig, } from "./config.js";
22
- export { defaultWhich, detectAgentCliProfiles, pickDefaultAgentProfile } from "./detect.js";
5
+ export { DEFAULT_AGENT_TIMEOUT_MS, listAgentProfileNames, listResolvedAgentProfiles, requireAgentProfile, resolveAgentProfile, resolveDefaultProfileName, resolveProfileFromConfig, } from "./config.js";
6
+ export { _setAgentDetectForTests, defaultWhich, detectAgentCliProfiles, pickDefaultAgentProfile } from "./detect.js";
23
7
  export { listBuiltinModelAliases, resolveModel } from "./model-aliases.js";
24
8
  export { BUILTIN_AGENT_PROFILE_NAMES, getBuiltinAgentProfile, listBuiltinAgentProfiles, } from "./profiles.js";
25
- export { buildProposePrompt, buildReflectPrompt, buildSchemaRepairPrompt, extractDraftConfidence, parseAgentProposalPayload, stripJsonFences, } from "./prompts.js";
9
+ export { buildProposePrompt, buildReflectPrompt, buildSchemaRepairPrompt, extractDraftConfidence, parseAgentProposalPayload, } from "./prompts.js";
26
10
  export { runAgent } from "./spawn.js";
@@ -39,13 +39,18 @@ const BUILTIN_ALIASES = [
39
39
  *
40
40
  * @param model Raw alias ("opus") or exact model ID ("claude-opus-4-7").
41
41
  * @param platform Builder platform name ("claude", "opencode", ...).
42
- * @param custom Profile-level aliases from config.json — take priority over builtins.
42
+ * @param custom Profile-level aliases from config.json — take priority over globals and builtins.
43
+ * @param global Config-root `modelAliases` tier table (alias → platform → model, `"*"` fallback).
43
44
  * @returns Resolved model string, or `model` verbatim when no alias matches.
44
45
  */
45
- export function resolveModel(model, platform, custom) {
46
+ export function resolveModel(model, platform, custom, global) {
46
47
  const key = model.toLowerCase();
47
48
  if (custom?.[key])
48
49
  return custom[key];
50
+ const tier = global?.[key];
51
+ const fromGlobal = tier?.[platform] ?? tier?.["*"];
52
+ if (fromGlobal)
53
+ return fromGlobal;
49
54
  const entry = BUILTIN_ALIASES.find((a) => a.alias === key);
50
55
  return entry?.platforms[platform] ?? model;
51
56
  }
@@ -1,7 +1,13 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- const COMMON_PASSTHROUGH = ["HOME", "PATH", "USER", "LANG", "LC_ALL", "TERM", "TMPDIR"];
4
+ // AKM_EVENT_SOURCE carries usage-event provenance (improve/task) so that akm
5
+ // invocations a spawned agent makes are recorded as machine traffic, not user
6
+ // demand (DRIFT-6). Without it in the passthrough whitelist, buildChildEnv drops
7
+ // the stamp at the agent boundary — e.g. `akm wiki ingest` spawns an agent whose
8
+ // `akm curate/show/search` tool-calls then log source='user', silently inflating
9
+ // every lane's read-back (GRR). It is a provenance tag, never a secret.
10
+ const COMMON_PASSTHROUGH = ["HOME", "PATH", "USER", "LANG", "LC_ALL", "TERM", "TMPDIR", "AKM_EVENT_SOURCE"];
5
11
  /**
6
12
  * Built-in profiles for the five agent CLIs the v1 spec calls out
7
13
  * explicitly. The fields here are conservative defaults — every value is