akm-cli 0.7.0 → 0.7.2

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 (332) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{src/cli.js → cli.js} +22 -8
  3. package/dist/{src/commands → commands}/installed-stashes.js +1 -1
  4. package/dist/{src/commands → commands}/source-add.js +1 -1
  5. package/dist/{src/core → core}/common.js +16 -1
  6. package/dist/{src/core → core}/config.js +5 -2
  7. package/dist/{src/indexer → indexer}/db-search.js +16 -1
  8. package/dist/{src/indexer → indexer}/graph-extraction.js +5 -3
  9. package/dist/{src/indexer → indexer}/indexer.js +27 -11
  10. package/dist/{src/indexer → indexer}/memory-inference.js +47 -58
  11. package/dist/{src/indexer → indexer}/search-source.js +1 -1
  12. package/dist/{src/llm → llm}/client.js +61 -1
  13. package/dist/{src/llm → llm}/embedder.js +8 -5
  14. package/dist/{src/llm → llm}/embedders/local.js +8 -2
  15. package/dist/{src/llm → llm}/embedders/remote.js +4 -2
  16. package/dist/{src/llm → llm}/graph-extract.js +4 -4
  17. package/dist/llm/memory-infer.js +114 -0
  18. package/dist/{src/llm → llm}/metadata-enhance.js +2 -2
  19. package/dist/{src/output → output}/cli-hints.js +2 -0
  20. package/dist/{src/setup → setup}/setup.js +30 -20
  21. package/dist/sources/providers/website.js +27 -0
  22. package/dist/{src/sources/providers/website.js → sources/website-ingest.js} +38 -51
  23. package/docs/README.md +7 -0
  24. package/docs/migration/release-notes/0.7.0.md +14 -0
  25. package/package.json +11 -8
  26. package/dist/src/llm/memory-infer.js +0 -86
  27. package/dist/tests/add-website-source.test.js +0 -119
  28. package/dist/tests/agent/agent-config-loader.test.js +0 -70
  29. package/dist/tests/agent/agent-config.test.js +0 -221
  30. package/dist/tests/agent/agent-detect.test.js +0 -100
  31. package/dist/tests/agent/agent-spawn.test.js +0 -234
  32. package/dist/tests/agent-output.test.js +0 -186
  33. package/dist/tests/architecture/agent-no-llm-sdk-guard.test.js +0 -103
  34. package/dist/tests/architecture/agent-spawn-seam.test.js +0 -193
  35. package/dist/tests/architecture/llm-stateless-seam.test.js +0 -112
  36. package/dist/tests/asset-ref.test.js +0 -192
  37. package/dist/tests/asset-registry.test.js +0 -103
  38. package/dist/tests/asset-spec.test.js +0 -241
  39. package/dist/tests/bench/attribution.test.js +0 -996
  40. package/dist/tests/bench/cleanup-sigint.test.js +0 -83
  41. package/dist/tests/bench/cleanup.js +0 -234
  42. package/dist/tests/bench/cleanup.test.js +0 -166
  43. package/dist/tests/bench/cli.js +0 -1018
  44. package/dist/tests/bench/cli.test.js +0 -445
  45. package/dist/tests/bench/compare.test.js +0 -556
  46. package/dist/tests/bench/corpus.js +0 -317
  47. package/dist/tests/bench/corpus.test.js +0 -258
  48. package/dist/tests/bench/doctor.js +0 -525
  49. package/dist/tests/bench/driver.js +0 -401
  50. package/dist/tests/bench/driver.test.js +0 -584
  51. package/dist/tests/bench/environment.js +0 -233
  52. package/dist/tests/bench/environment.test.js +0 -199
  53. package/dist/tests/bench/evolve-metrics.js +0 -179
  54. package/dist/tests/bench/evolve-metrics.test.js +0 -187
  55. package/dist/tests/bench/evolve.js +0 -647
  56. package/dist/tests/bench/evolve.test.js +0 -624
  57. package/dist/tests/bench/failure-modes.test.js +0 -349
  58. package/dist/tests/bench/feedback-integrity.test.js +0 -457
  59. package/dist/tests/bench/leakage.test.js +0 -228
  60. package/dist/tests/bench/learning-curve.test.js +0 -134
  61. package/dist/tests/bench/metrics.js +0 -2395
  62. package/dist/tests/bench/metrics.test.js +0 -1150
  63. package/dist/tests/bench/no-os-tmpdir-invariant.test.js +0 -43
  64. package/dist/tests/bench/opencode-config.js +0 -194
  65. package/dist/tests/bench/opencode-config.test.js +0 -370
  66. package/dist/tests/bench/report.js +0 -1885
  67. package/dist/tests/bench/report.test.js +0 -1038
  68. package/dist/tests/bench/run-config.js +0 -355
  69. package/dist/tests/bench/run-config.test.js +0 -298
  70. package/dist/tests/bench/run-curate-test.js +0 -32
  71. package/dist/tests/bench/run-failing-tasks.js +0 -56
  72. package/dist/tests/bench/run-full-bench.js +0 -51
  73. package/dist/tests/bench/run-items36-targeted.js +0 -69
  74. package/dist/tests/bench/run-nano-quick.js +0 -42
  75. package/dist/tests/bench/run-waveg-targeted.js +0 -62
  76. package/dist/tests/bench/runner.js +0 -699
  77. package/dist/tests/bench/runner.test.js +0 -958
  78. package/dist/tests/bench/search-bridge.test.js +0 -331
  79. package/dist/tests/bench/tmp.js +0 -131
  80. package/dist/tests/bench/trajectory.js +0 -116
  81. package/dist/tests/bench/trajectory.test.js +0 -127
  82. package/dist/tests/bench/verifier.js +0 -114
  83. package/dist/tests/bench/verifier.test.js +0 -118
  84. package/dist/tests/bench/workflow-evaluator.js +0 -557
  85. package/dist/tests/bench/workflow-evaluator.test.js +0 -421
  86. package/dist/tests/bench/workflow-spec.js +0 -345
  87. package/dist/tests/bench/workflow-spec.test.js +0 -363
  88. package/dist/tests/bench/workflow-trace.js +0 -472
  89. package/dist/tests/bench/workflow-trace.test.js +0 -254
  90. package/dist/tests/benchmark-search-quality.js +0 -536
  91. package/dist/tests/benchmark-suite.js +0 -1441
  92. package/dist/tests/capture-cli.test.js +0 -112
  93. package/dist/tests/cli-errors.test.js +0 -204
  94. package/dist/tests/commands/events.test.js +0 -370
  95. package/dist/tests/commands/history.test.js +0 -418
  96. package/dist/tests/commands/import.test.js +0 -103
  97. package/dist/tests/commands/proposal-cli.test.js +0 -209
  98. package/dist/tests/commands/reflect-propose-cli.test.js +0 -333
  99. package/dist/tests/commands/remember.test.js +0 -97
  100. package/dist/tests/commands/scope-flags.test.js +0 -300
  101. package/dist/tests/commands/search.test.js +0 -537
  102. package/dist/tests/commands/show-indexer-parity.test.js +0 -117
  103. package/dist/tests/commands/show.test.js +0 -294
  104. package/dist/tests/common.test.js +0 -266
  105. package/dist/tests/completions.test.js +0 -142
  106. package/dist/tests/config-cli.test.js +0 -193
  107. package/dist/tests/config-llm-features.test.js +0 -139
  108. package/dist/tests/config.test.js +0 -569
  109. package/dist/tests/contracts/migration-baseline.test.js +0 -43
  110. package/dist/tests/contracts/reflect-propose-envelope.test.js +0 -139
  111. package/dist/tests/contracts/spec-helpers.js +0 -46
  112. package/dist/tests/contracts/v1-spec-section-11-proposal-queue.test.js +0 -228
  113. package/dist/tests/contracts/v1-spec-section-12-agent-config.test.js +0 -56
  114. package/dist/tests/contracts/v1-spec-section-13-lesson-type.test.js +0 -34
  115. package/dist/tests/contracts/v1-spec-section-14-llm-features.test.js +0 -94
  116. package/dist/tests/contracts/v1-spec-section-4-1-asset-types.test.js +0 -39
  117. package/dist/tests/contracts/v1-spec-section-4-2-quality-rules.test.js +0 -44
  118. package/dist/tests/contracts/v1-spec-section-5-configuration.test.js +0 -47
  119. package/dist/tests/contracts/v1-spec-section-6-orchestration.test.js +0 -40
  120. package/dist/tests/contracts/v1-spec-section-7-module-layout.test.js +0 -58
  121. package/dist/tests/contracts/v1-spec-section-8-extension-points.test.js +0 -34
  122. package/dist/tests/contracts/v1-spec-section-9-4-cli-surface.test.js +0 -75
  123. package/dist/tests/contracts/v1-spec-section-9-7-llm-agent-boundary.test.js +0 -36
  124. package/dist/tests/core/write-source.test.js +0 -366
  125. package/dist/tests/curate-command.test.js +0 -87
  126. package/dist/tests/db-scoring.test.js +0 -201
  127. package/dist/tests/db.test.js +0 -654
  128. package/dist/tests/distill-cli-flag.test.js +0 -208
  129. package/dist/tests/distill.test.js +0 -515
  130. package/dist/tests/docker-install.test.js +0 -120
  131. package/dist/tests/e2e.test.js +0 -1419
  132. package/dist/tests/embedder.test.js +0 -340
  133. package/dist/tests/embedding-model-config.test.js +0 -379
  134. package/dist/tests/feedback-command.test.js +0 -172
  135. package/dist/tests/file-context.test.js +0 -552
  136. package/dist/tests/fixtures/scripts/git/summarize-diff.js +0 -9
  137. package/dist/tests/fixtures/scripts/lint/eslint-check.js +0 -7
  138. package/dist/tests/fixtures/stashes/load.js +0 -166
  139. package/dist/tests/fixtures/stashes/load.test.js +0 -97
  140. package/dist/tests/fixtures/stashes/ranking-baseline/scripts/mem0-search.js +0 -12
  141. package/dist/tests/frontmatter.test.js +0 -190
  142. package/dist/tests/fts-field-weighting.test.js +0 -254
  143. package/dist/tests/fuzzy-search.test.js +0 -230
  144. package/dist/tests/git-provider-clone.test.js +0 -45
  145. package/dist/tests/github.test.js +0 -161
  146. package/dist/tests/graph-boost-ranking.test.js +0 -305
  147. package/dist/tests/graph-extraction.test.js +0 -282
  148. package/dist/tests/helpers/usage-events.js +0 -8
  149. package/dist/tests/index-pass-llm.test.js +0 -161
  150. package/dist/tests/indexer.test.js +0 -570
  151. package/dist/tests/info-command.test.js +0 -166
  152. package/dist/tests/init.test.js +0 -69
  153. package/dist/tests/install-script.test.js +0 -246
  154. package/dist/tests/integration/agent-real-profile.test.js +0 -94
  155. package/dist/tests/issue-36-repro.test.js +0 -304
  156. package/dist/tests/issues-191-194.test.js +0 -160
  157. package/dist/tests/lesson-lint.test.js +0 -111
  158. package/dist/tests/llm-client.test.js +0 -115
  159. package/dist/tests/llm-feature-gate.test.js +0 -151
  160. package/dist/tests/llm.test.js +0 -139
  161. package/dist/tests/lockfile.test.js +0 -216
  162. package/dist/tests/manifest.test.js +0 -205
  163. package/dist/tests/markdown.test.js +0 -126
  164. package/dist/tests/matchers-unit.test.js +0 -189
  165. package/dist/tests/memory-inference.test.js +0 -299
  166. package/dist/tests/merge-scoring.test.js +0 -136
  167. package/dist/tests/metadata.test.js +0 -313
  168. package/dist/tests/migration-help.test.js +0 -89
  169. package/dist/tests/origin-resolve.test.js +0 -124
  170. package/dist/tests/output-baseline.test.js +0 -218
  171. package/dist/tests/output-shapes-unit.test.js +0 -478
  172. package/dist/tests/parallel-search.test.js +0 -272
  173. package/dist/tests/parameter-metadata.test.js +0 -365
  174. package/dist/tests/paths.test.js +0 -177
  175. package/dist/tests/progressive-disclosure.test.js +0 -280
  176. package/dist/tests/proposals.test.js +0 -279
  177. package/dist/tests/proposed-quality.test.js +0 -271
  178. package/dist/tests/provider-registry.test.js +0 -32
  179. package/dist/tests/ranking-regression.test.js +0 -548
  180. package/dist/tests/reflect-propose.test.js +0 -455
  181. package/dist/tests/registry-build-index.test.js +0 -394
  182. package/dist/tests/registry-cli.test.js +0 -290
  183. package/dist/tests/registry-index-v2.test.js +0 -430
  184. package/dist/tests/registry-install.test.js +0 -728
  185. package/dist/tests/registry-providers/parity.test.js +0 -189
  186. package/dist/tests/registry-providers/skills-sh.test.js +0 -309
  187. package/dist/tests/registry-providers/static-index.test.js +0 -238
  188. package/dist/tests/registry-resolve.test.js +0 -126
  189. package/dist/tests/registry-search.test.js +0 -923
  190. package/dist/tests/remember-frontmatter.test.js +0 -378
  191. package/dist/tests/remember-unit.test.js +0 -123
  192. package/dist/tests/ripgrep-install.test.js +0 -251
  193. package/dist/tests/ripgrep-resolve.test.js +0 -108
  194. package/dist/tests/ripgrep.test.js +0 -163
  195. package/dist/tests/save-command.test.js +0 -94
  196. package/dist/tests/save-trust-qa-fixes.test.js +0 -270
  197. package/dist/tests/scoring-pipeline.test.js +0 -648
  198. package/dist/tests/search-include-proposed-cli.test.js +0 -118
  199. package/dist/tests/self-update.test.js +0 -442
  200. package/dist/tests/semantic-search-e2e.test.js +0 -512
  201. package/dist/tests/semantic-status.test.js +0 -471
  202. package/dist/tests/setup-run.integration.js +0 -877
  203. package/dist/tests/setup-wizard.test.js +0 -198
  204. package/dist/tests/setup.test.js +0 -131
  205. package/dist/tests/source-add.test.js +0 -11
  206. package/dist/tests/source-clone.test.js +0 -254
  207. package/dist/tests/source-manage.test.js +0 -366
  208. package/dist/tests/source-providers/filesystem.test.js +0 -82
  209. package/dist/tests/source-providers/git.test.js +0 -252
  210. package/dist/tests/source-providers/website.test.js +0 -128
  211. package/dist/tests/source-qa-fixes.test.js +0 -286
  212. package/dist/tests/source-registry.test.js +0 -350
  213. package/dist/tests/source-resolve.test.js +0 -100
  214. package/dist/tests/source-source.test.js +0 -281
  215. package/dist/tests/source.test.js +0 -533
  216. package/dist/tests/tar-utils-scan.test.js +0 -73
  217. package/dist/tests/toggle-components.test.js +0 -73
  218. package/dist/tests/usage-telemetry.test.js +0 -265
  219. package/dist/tests/utility-scoring.test.js +0 -558
  220. package/dist/tests/vault-load-error.test.js +0 -78
  221. package/dist/tests/vault-qa-fixes.test.js +0 -194
  222. package/dist/tests/vault.test.js +0 -429
  223. package/dist/tests/vector-search.test.js +0 -608
  224. package/dist/tests/walker.test.js +0 -252
  225. package/dist/tests/wave2-cluster-bc.test.js +0 -228
  226. package/dist/tests/wave2-cluster-d.test.js +0 -180
  227. package/dist/tests/wave2-cluster-e.test.js +0 -179
  228. package/dist/tests/wiki-qa-fixes.test.js +0 -270
  229. package/dist/tests/wiki.test.js +0 -529
  230. package/dist/tests/workflow-cli.test.js +0 -271
  231. package/dist/tests/workflow-markdown.test.js +0 -171
  232. package/dist/tests/workflow-path-escape.test.js +0 -132
  233. package/dist/tests/workflow-qa-fixes.test.js +0 -395
  234. package/dist/tests/workflows/indexer-rejection.test.js +0 -213
  235. /package/dist/{src/commands → commands}/completions.js +0 -0
  236. /package/dist/{src/commands → commands}/config-cli.js +0 -0
  237. /package/dist/{src/commands → commands}/curate.js +0 -0
  238. /package/dist/{src/commands → commands}/distill.js +0 -0
  239. /package/dist/{src/commands → commands}/events.js +0 -0
  240. /package/dist/{src/commands → commands}/history.js +0 -0
  241. /package/dist/{src/commands → commands}/info.js +0 -0
  242. /package/dist/{src/commands → commands}/init.js +0 -0
  243. /package/dist/{src/commands → commands}/install-audit.js +0 -0
  244. /package/dist/{src/commands → commands}/migration-help.js +0 -0
  245. /package/dist/{src/commands → commands}/proposal.js +0 -0
  246. /package/dist/{src/commands → commands}/propose.js +0 -0
  247. /package/dist/{src/commands → commands}/reflect.js +0 -0
  248. /package/dist/{src/commands → commands}/registry-search.js +0 -0
  249. /package/dist/{src/commands → commands}/remember.js +0 -0
  250. /package/dist/{src/commands → commands}/search.js +0 -0
  251. /package/dist/{src/commands → commands}/self-update.js +0 -0
  252. /package/dist/{src/commands → commands}/show.js +0 -0
  253. /package/dist/{src/commands → commands}/source-clone.js +0 -0
  254. /package/dist/{src/commands → commands}/source-manage.js +0 -0
  255. /package/dist/{src/commands → commands}/vault.js +0 -0
  256. /package/dist/{src/core → core}/asset-ref.js +0 -0
  257. /package/dist/{src/core → core}/asset-registry.js +0 -0
  258. /package/dist/{src/core → core}/asset-spec.js +0 -0
  259. /package/dist/{src/core → core}/errors.js +0 -0
  260. /package/dist/{src/core → core}/events.js +0 -0
  261. /package/dist/{src/core → core}/frontmatter.js +0 -0
  262. /package/dist/{src/core → core}/lesson-lint.js +0 -0
  263. /package/dist/{src/core → core}/markdown.js +0 -0
  264. /package/dist/{src/core → core}/paths.js +0 -0
  265. /package/dist/{src/core → core}/proposals.js +0 -0
  266. /package/dist/{src/core → core}/warn.js +0 -0
  267. /package/dist/{src/core → core}/write-source.js +0 -0
  268. /package/dist/{src/indexer → indexer}/db.js +0 -0
  269. /package/dist/{src/indexer → indexer}/file-context.js +0 -0
  270. /package/dist/{src/indexer → indexer}/graph-boost.js +0 -0
  271. /package/dist/{src/indexer → indexer}/manifest.js +0 -0
  272. /package/dist/{src/indexer → indexer}/matchers.js +0 -0
  273. /package/dist/{src/indexer → indexer}/metadata.js +0 -0
  274. /package/dist/{src/indexer → indexer}/search-fields.js +0 -0
  275. /package/dist/{src/indexer → indexer}/semantic-status.js +0 -0
  276. /package/dist/{src/indexer → indexer}/usage-events.js +0 -0
  277. /package/dist/{src/indexer → indexer}/walker.js +0 -0
  278. /package/dist/{src/integrations → integrations}/agent/config.js +0 -0
  279. /package/dist/{src/integrations → integrations}/agent/detect.js +0 -0
  280. /package/dist/{src/integrations → integrations}/agent/index.js +0 -0
  281. /package/dist/{src/integrations → integrations}/agent/profiles.js +0 -0
  282. /package/dist/{src/integrations → integrations}/agent/prompts.js +0 -0
  283. /package/dist/{src/integrations → integrations}/agent/spawn.js +0 -0
  284. /package/dist/{src/integrations → integrations}/github.js +0 -0
  285. /package/dist/{src/integrations → integrations}/lockfile.js +0 -0
  286. /package/dist/{src/llm → llm}/embedders/cache.js +0 -0
  287. /package/dist/{src/llm → llm}/embedders/types.js +0 -0
  288. /package/dist/{src/llm → llm}/feature-gate.js +0 -0
  289. /package/dist/{src/llm → llm}/index-passes.js +0 -0
  290. /package/dist/{src/output → output}/context.js +0 -0
  291. /package/dist/{src/output → output}/renderers.js +0 -0
  292. /package/dist/{src/output → output}/shapes.js +0 -0
  293. /package/dist/{src/output → output}/text.js +0 -0
  294. /package/dist/{src/registry → registry}/build-index.js +0 -0
  295. /package/dist/{src/registry → registry}/create-provider-registry.js +0 -0
  296. /package/dist/{src/registry → registry}/factory.js +0 -0
  297. /package/dist/{src/registry → registry}/origin-resolve.js +0 -0
  298. /package/dist/{src/registry → registry}/providers/index.js +0 -0
  299. /package/dist/{src/registry → registry}/providers/skills-sh.js +0 -0
  300. /package/dist/{src/registry → registry}/providers/static-index.js +0 -0
  301. /package/dist/{src/registry → registry}/providers/types.js +0 -0
  302. /package/dist/{src/registry → registry}/resolve.js +0 -0
  303. /package/dist/{src/registry → registry}/types.js +0 -0
  304. /package/dist/{src/setup → setup}/detect.js +0 -0
  305. /package/dist/{src/setup → setup}/ripgrep-install.js +0 -0
  306. /package/dist/{src/setup → setup}/ripgrep-resolve.js +0 -0
  307. /package/dist/{src/setup → setup}/steps.js +0 -0
  308. /package/dist/{src/sources → sources}/include.js +0 -0
  309. /package/dist/{src/sources → sources}/provider-factory.js +0 -0
  310. /package/dist/{src/sources → sources}/provider.js +0 -0
  311. /package/dist/{src/sources → sources}/providers/filesystem.js +0 -0
  312. /package/dist/{src/sources → sources}/providers/git.js +0 -0
  313. /package/dist/{src/sources → sources}/providers/index.js +0 -0
  314. /package/dist/{src/sources → sources}/providers/install-types.js +0 -0
  315. /package/dist/{src/sources → sources}/providers/npm.js +0 -0
  316. /package/dist/{src/sources → sources}/providers/provider-utils.js +0 -0
  317. /package/dist/{src/sources → sources}/providers/sync-from-ref.js +0 -0
  318. /package/dist/{src/sources → sources}/providers/tar-utils.js +0 -0
  319. /package/dist/{src/sources → sources}/resolve.js +0 -0
  320. /package/dist/{src/sources → sources}/types.js +0 -0
  321. /package/dist/{src/templates → templates}/wiki-templates.js +0 -0
  322. /package/dist/{src/version.js → version.js} +0 -0
  323. /package/dist/{src/wiki → wiki}/wiki.js +0 -0
  324. /package/dist/{src/workflows → workflows}/authoring.js +0 -0
  325. /package/dist/{src/workflows → workflows}/cli.js +0 -0
  326. /package/dist/{src/workflows → workflows}/db.js +0 -0
  327. /package/dist/{src/workflows → workflows}/document-cache.js +0 -0
  328. /package/dist/{src/workflows → workflows}/parser.js +0 -0
  329. /package/dist/{src/workflows → workflows}/renderer.js +0 -0
  330. /package/dist/{src/workflows → workflows}/runs.js +0 -0
  331. /package/dist/{src/workflows → workflows}/schema.js +0 -0
  332. /package/dist/{src/workflows → workflows}/validator.js +0 -0
@@ -1,186 +0,0 @@
1
- import { afterEach, describe, expect, test } from "bun:test";
2
- import { spawnSync } from "node:child_process";
3
- import fs from "node:fs";
4
- import os from "node:os";
5
- import path from "node:path";
6
- const CLI = path.join(__dirname, "..", "src", "cli.ts");
7
- const tempDirs = [];
8
- function makeTempDir(prefix) {
9
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
10
- tempDirs.push(dir);
11
- return dir;
12
- }
13
- function writeFile(filePath, content) {
14
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
15
- fs.writeFileSync(filePath, content);
16
- }
17
- function writeConfig(configDir, config) {
18
- const configPath = path.join(configDir, "akm", "config.json");
19
- fs.mkdirSync(path.dirname(configPath), { recursive: true });
20
- fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
21
- }
22
- function runCli(stashDir, args, config) {
23
- const xdgCache = makeTempDir("akm-agent-cache-");
24
- const xdgConfig = makeTempDir("akm-agent-config-");
25
- if (config)
26
- writeConfig(xdgConfig, config);
27
- const result = spawnSync("bun", [CLI, ...args], {
28
- encoding: "utf8",
29
- timeout: 30_000,
30
- env: {
31
- ...process.env,
32
- AKM_STASH_DIR: stashDir,
33
- XDG_CACHE_HOME: xdgCache,
34
- XDG_CONFIG_HOME: xdgConfig,
35
- },
36
- });
37
- if (result.status !== 0) {
38
- throw new Error(`CLI exited ${result.status}:\n${result.stderr}`);
39
- }
40
- return result.stdout.trim();
41
- }
42
- afterEach(() => {
43
- for (const dir of tempDirs.splice(0)) {
44
- fs.rmSync(dir, { recursive: true, force: true });
45
- }
46
- });
47
- describe("--for-agent output mode", () => {
48
- function makeStash() {
49
- const stashDir = makeTempDir("akm-agent-stash-");
50
- writeFile(path.join(stashDir, "agents", "architect.md"), "---\ndescription: System architecture agent\ntags: [arch, design]\n---\nYou are an architect.\n");
51
- writeFile(path.join(stashDir, "scripts", "deploy.sh"), "#!/usr/bin/env bash\necho deploy\n");
52
- writeFile(path.join(stashDir, "commands", "release.md"), "---\ndescription: Release process\n---\nRun release {{version}}\n");
53
- return stashDir;
54
- }
55
- test("--for-agent search output has only: name, ref, type, description, action, score", () => {
56
- const stashDir = makeStash();
57
- const output = runCli(stashDir, ["search", "architect", "--format=json", "--for-agent"]);
58
- const json = JSON.parse(output);
59
- expect(json.hits.length).toBeGreaterThan(0);
60
- const hit = json.hits[0];
61
- const keys = Object.keys(hit);
62
- // Must have these agent-essential fields (when present)
63
- expect(keys).toContain("name");
64
- expect(keys).toContain("type");
65
- expect(keys).toContain("action");
66
- // Only allowed keys (estimatedTokens is optional — present when fileSize is known)
67
- const allowedKeys = new Set(["name", "ref", "type", "description", "action", "score", "estimatedTokens"]);
68
- for (const key of keys) {
69
- expect(allowedKeys.has(key)).toBe(true);
70
- }
71
- });
72
- test("--for-agent search output does NOT have: schemaVersion, stashDir, path, whyMatched, origin, editable", () => {
73
- const stashDir = makeStash();
74
- const output = runCli(stashDir, ["search", "architect", "--format=json", "--for-agent"]);
75
- const json = JSON.parse(output);
76
- // Top-level envelope must not have these
77
- expect(json).not.toHaveProperty("schemaVersion");
78
- expect(json).not.toHaveProperty("stashDir");
79
- expect(json).not.toHaveProperty("timing");
80
- // Hits must not have these
81
- const hits = json.hits;
82
- for (const hit of hits) {
83
- expect(hit).not.toHaveProperty("path");
84
- expect(hit).not.toHaveProperty("whyMatched");
85
- expect(hit).not.toHaveProperty("origin");
86
- expect(hit).not.toHaveProperty("editable");
87
- expect(hit).not.toHaveProperty("editHint");
88
- expect(hit).not.toHaveProperty("tags");
89
- expect(hit).not.toHaveProperty("size");
90
- }
91
- });
92
- test("--for-agent show output strips non-essential fields", () => {
93
- const stashDir = makeStash();
94
- const output = runCli(stashDir, ["show", "command:release.md", "--format=json", "--for-agent"]);
95
- const json = JSON.parse(output);
96
- // Must have essential fields
97
- expect(json).toHaveProperty("name");
98
- expect(json).toHaveProperty("type");
99
- // Must NOT have non-essential fields
100
- expect(json).not.toHaveProperty("schemaVersion");
101
- expect(json).not.toHaveProperty("path");
102
- expect(json).not.toHaveProperty("origin");
103
- expect(json).not.toHaveProperty("editable");
104
- expect(json).not.toHaveProperty("editHint");
105
- });
106
- test("--for-agent show output keeps content/run/action", () => {
107
- const stashDir = makeStash();
108
- // Command has template content
109
- const cmdOutput = runCli(stashDir, ["show", "command:release.md", "--format=json", "--for-agent"]);
110
- const cmdJson = JSON.parse(cmdOutput);
111
- expect(cmdJson).toHaveProperty("template");
112
- expect(cmdJson).toHaveProperty("action");
113
- // Script has run field
114
- const scriptOutput = runCli(stashDir, ["show", "script:deploy.sh", "--format=json", "--for-agent"]);
115
- const scriptJson = JSON.parse(scriptOutput);
116
- expect(scriptJson).toHaveProperty("run");
117
- expect(scriptJson).toHaveProperty("action");
118
- });
119
- test("standard output (without --for-agent) is unchanged", () => {
120
- const stashDir = makeStash();
121
- // Default brief search still has same shape
122
- const searchOutput = runCli(stashDir, ["search", "architect", "--format=json"]);
123
- const searchJson = JSON.parse(searchOutput);
124
- // hits is always present; warnings may appear when semantic search is pending
125
- expect(Object.keys(searchJson)).toContain("hits");
126
- // Standard brief output includes at least name, type, action (may also include estimatedTokens etc.)
127
- const hit = searchJson.hits[0] ?? {};
128
- expect(hit).toHaveProperty("name");
129
- expect(hit).toHaveProperty("type");
130
- expect(hit).toHaveProperty("action");
131
- // Default show still has origin
132
- const showOutput = runCli(stashDir, ["show", "command:release.md", "--format=json"]);
133
- const showJson = JSON.parse(showOutput);
134
- expect(showJson).toHaveProperty("origin");
135
- });
136
- });
137
- describe("--format jsonl", () => {
138
- function makeStash() {
139
- const stashDir = makeTempDir("akm-jsonl-stash-");
140
- writeFile(path.join(stashDir, "agents", "architect.md"), "---\ndescription: System architecture agent\n---\nYou are an architect.\n");
141
- writeFile(path.join(stashDir, "scripts", "deploy.sh"), "#!/usr/bin/env bash\necho deploy\n");
142
- return stashDir;
143
- }
144
- test("JSONL format outputs one JSON object per line for search hits", () => {
145
- const stashDir = makeStash();
146
- // QA #14: empty query now rejects; use a real keyword that matches stash assets.
147
- // Use "architect" since architect.md has that word in both name and content.
148
- const output = runCli(stashDir, ["search", "architect", "--format=jsonl"]);
149
- const lines = output.split("\n").filter((line) => line.trim().length > 0);
150
- // Should have at least 1 hit
151
- expect(lines.length).toBeGreaterThanOrEqual(1);
152
- // Each line must be its own object, not wrapped in an envelope
153
- for (const line of lines) {
154
- const parsed = JSON.parse(line);
155
- expect(typeof parsed).toBe("object");
156
- expect(parsed).toHaveProperty("name");
157
- }
158
- });
159
- test("each JSONL line is valid parseable JSON", () => {
160
- const stashDir = makeStash();
161
- const output = runCli(stashDir, ["search", "deploy", "--format=jsonl"]);
162
- const lines = output.split("\n").filter((line) => line.trim().length > 0);
163
- for (const line of lines) {
164
- expect(() => JSON.parse(line)).not.toThrow();
165
- const parsed = JSON.parse(line);
166
- expect(typeof parsed).toBe("object");
167
- expect(Array.isArray(parsed)).toBe(false);
168
- }
169
- });
170
- test("JSONL combined with --for-agent uses agent shaping", () => {
171
- const stashDir = makeStash();
172
- const output = runCli(stashDir, ["search", "deploy", "--format=jsonl", "--for-agent"]);
173
- const lines = output.split("\n").filter((line) => line.trim().length > 0);
174
- for (const line of lines) {
175
- const parsed = JSON.parse(line);
176
- const allowedKeys = new Set(["name", "ref", "type", "description", "action", "score", "estimatedTokens"]);
177
- for (const key of Object.keys(parsed)) {
178
- expect(allowedKeys.has(key)).toBe(true);
179
- }
180
- // Must not have stripped fields
181
- expect(parsed).not.toHaveProperty("path");
182
- expect(parsed).not.toHaveProperty("origin");
183
- expect(parsed).not.toHaveProperty("whyMatched");
184
- }
185
- });
186
- });
@@ -1,103 +0,0 @@
1
- /**
2
- * Regression guard — `src/integrations/agent/**` does not import LLM SDKs.
3
- *
4
- * Locks v1 spec §9.7 (LLM/agent boundary) and §12 (CLI shell-out only).
5
- * Issue #222.
6
- *
7
- * **This test is defence-in-depth, not the primary enforcement.**
8
- *
9
- * The primary enforcement of the agent shell-out invariant is:
10
- * 1. The seam test in `agent-spawn-seam.test.ts`, which locks the
11
- * `runAgent` interface.
12
- * 2. The TypeScript module graph — vendor SDKs are not in
13
- * `package.json`, so an accidental import would fail to resolve at
14
- * build time.
15
- * 3. Code review and the architectural boundary documented in
16
- * `docs/technical/architecture.md`.
17
- *
18
- * The guard below scans file contents under `src/integrations/agent/`
19
- * for known LLM SDK package names. It exists to surface accidental
20
- * regressions in PRs (e.g. someone copies an example that pulls in a
21
- * vendor SDK before the type-check would catch it). The list is
22
- * intentionally narrow — it names specific vendor packages, not broad
23
- * patterns — so it does not flag legitimate code.
24
- *
25
- * Adding a new SDK package to the list (when a new vendor ships) is a
26
- * one-line change. Removing the test entirely is a contract violation:
27
- * agents are reachable only via the spawn wrapper.
28
- */
29
- import { describe, expect, test } from "bun:test";
30
- import fs from "node:fs";
31
- import path from "node:path";
32
- const REPO_ROOT = path.resolve(import.meta.dir, "..", "..");
33
- const AGENT_DIR = path.join(REPO_ROOT, "src", "integrations", "agent");
34
- /**
35
- * Specific vendor SDK package names whose presence in the agent
36
- * integration tree would indicate the shell-out invariant has been
37
- * crossed. The names are matched as quoted-import strings, not as
38
- * arbitrary substrings, so unrelated mentions in comments do not
39
- * trip the guard.
40
- */
41
- const FORBIDDEN_LLM_SDK_PACKAGES = [
42
- "@anthropic-ai/sdk",
43
- "@anthropic-ai/bedrock-sdk",
44
- "@anthropic-ai/vertex-sdk",
45
- "openai",
46
- "@google/generative-ai",
47
- "@google/genai",
48
- "@google-ai/generativelanguage",
49
- "cohere-ai",
50
- "@mistralai/mistralai",
51
- "@huggingface/inference",
52
- "groq-sdk",
53
- "ollama",
54
- "langchain",
55
- "@langchain/core",
56
- "@langchain/openai",
57
- "@langchain/anthropic",
58
- "ai",
59
- "replicate",
60
- ];
61
- function listAgentSourceFiles() {
62
- const out = [];
63
- function walk(dir) {
64
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
65
- const abs = path.join(dir, entry.name);
66
- if (entry.isDirectory())
67
- walk(abs);
68
- else if (entry.isFile() && entry.name.endsWith(".ts"))
69
- out.push(abs);
70
- }
71
- }
72
- walk(AGENT_DIR);
73
- return out.sort();
74
- }
75
- /**
76
- * Match `from "<pkg>"` and `from '<pkg>'` and the equivalent
77
- * `import("<pkg>")` / `require("<pkg>")` forms. This is deliberately
78
- * a quoted-import match, not a free-text substring match, so writing
79
- * about a vendor SDK in a comment ("never imports `openai`") does not
80
- * trip the guard.
81
- */
82
- function buildImportRegex(pkg) {
83
- const escaped = pkg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
84
- return new RegExp(`${String.raw `(?:from|import\(|require\()\s*['"]` + escaped}(?:/[^'"]*)?['"]`);
85
- }
86
- describe("regression guard: src/integrations/agent/** never imports LLM SDKs", () => {
87
- test("the agent integration tree exists", () => {
88
- expect(fs.existsSync(AGENT_DIR)).toBe(true);
89
- const files = listAgentSourceFiles();
90
- expect(files.length).toBeGreaterThan(0);
91
- });
92
- test.each([...FORBIDDEN_LLM_SDK_PACKAGES])("no file imports %s", (pkg) => {
93
- const re = buildImportRegex(pkg);
94
- const offenders = [];
95
- for (const file of listAgentSourceFiles()) {
96
- const text = fs.readFileSync(file, "utf8");
97
- if (re.test(text)) {
98
- offenders.push(path.relative(REPO_ROOT, file));
99
- }
100
- }
101
- expect(offenders).toEqual([]);
102
- });
103
- });
@@ -1,193 +0,0 @@
1
- /**
2
- * Architecture seam test — `runAgent` is the single agent CLI entry point.
3
- *
4
- * Locks v1 spec §9.7 (LLM/agent boundary) and §12 (agent CLI integration).
5
- * Issue #222.
6
- *
7
- * The test exercises the documented `runAgent` interface without
8
- * spawning a real binary. Every agent-CLI integration in akm passes
9
- * through this seam; if the shape changes, callers break.
10
- *
11
- * Specifically locked:
12
- * • `runAgent` is exported from `src/integrations/agent/spawn.ts`
13
- * and re-exported from `src/integrations/agent/index.ts`.
14
- * • `AgentRunResult` carries the documented envelope.
15
- * • `AgentFailureReason` is the discriminated union
16
- * `"timeout" | "spawn_failed" | "non_zero_exit" | "parse_error"`.
17
- * • Captured stdio captures stdout/stderr; interactive stdio inherits
18
- * the parent's streams (no captured strings).
19
- * • A per-call `timeoutMs` override forces a `timeout` reason.
20
- */
21
- import { describe, expect, test } from "bun:test";
22
- import * as agentBarrel from "../../src/integrations/agent";
23
- import { runAgent } from "../../src/integrations/agent/spawn";
24
- const KNOWN_FAILURE_REASONS = new Set([
25
- "timeout",
26
- "spawn_failed",
27
- "non_zero_exit",
28
- "parse_error",
29
- ]);
30
- function makeProfile(overrides = {}) {
31
- return {
32
- name: "seam-test-agent",
33
- bin: "seam-test-agent",
34
- args: [],
35
- stdio: "captured",
36
- envPassthrough: ["PATH"],
37
- parseOutput: "text",
38
- ...overrides,
39
- };
40
- }
41
- function asReadableStream(text) {
42
- const bytes = new TextEncoder().encode(text);
43
- return new ReadableStream({
44
- start(controller) {
45
- controller.enqueue(bytes);
46
- controller.close();
47
- },
48
- });
49
- }
50
- function fakeSpawn(stdout, stderr, exitCode) {
51
- let calls = 0;
52
- const spawn = () => {
53
- calls++;
54
- const proc = {
55
- exitCode,
56
- exited: Promise.resolve(exitCode),
57
- stdout: asReadableStream(stdout),
58
- stderr: asReadableStream(stderr),
59
- stdin: null,
60
- kill: () => undefined,
61
- };
62
- return proc;
63
- };
64
- return {
65
- spawn,
66
- get calls() {
67
- return calls;
68
- },
69
- };
70
- }
71
- describe("`runAgent` seam (v1 spec §9.7, §12.2)", () => {
72
- test("`runAgent` is exported from both the spawn module and the agent barrel", () => {
73
- expect(typeof runAgent).toBe("function");
74
- expect(agentBarrel.runAgent).toBe(runAgent);
75
- });
76
- test("captured-stdio success returns `ok: true` with stdout/stderr strings", async () => {
77
- const fake = fakeSpawn("agent-output", "agent-stderr", 0);
78
- const result = await runAgent(makeProfile(), "hello", { spawn: fake.spawn });
79
- expect(result.ok).toBe(true);
80
- expect(result.exitCode).toBe(0);
81
- expect(result.stdout).toBe("agent-output");
82
- expect(result.stderr).toBe("agent-stderr");
83
- expect(result.reason).toBeUndefined();
84
- expect(result.error).toBeUndefined();
85
- expect(typeof result.durationMs).toBe("number");
86
- expect(fake.calls).toBe(1);
87
- });
88
- test("interactive-stdio mode does not capture stdout/stderr into the result", async () => {
89
- // Build a spawn that records the stdio options it was given. The
90
- // contract: when stdio is "interactive", stdout/stderr default to
91
- // "inherit", which the wrapper must not try to read from.
92
- let observed;
93
- const spawn = (_cmd, options) => {
94
- observed = {
95
- stdin: options.stdin,
96
- stdout: options.stdout,
97
- stderr: options.stderr,
98
- };
99
- return {
100
- exitCode: 0,
101
- exited: Promise.resolve(0),
102
- stdout: null,
103
- stderr: null,
104
- stdin: null,
105
- kill: () => undefined,
106
- };
107
- };
108
- const result = await runAgent(makeProfile({ stdio: "interactive" }), "hi", { spawn });
109
- expect(observed?.stdout).toBe("inherit");
110
- expect(observed?.stderr).toBe("inherit");
111
- expect(observed?.stdin).toBe("inherit");
112
- expect(result.ok).toBe(true);
113
- expect(result.stdout).toBe("");
114
- expect(result.stderr).toBe("");
115
- });
116
- test("failure-reason discriminated union covers exactly the documented vocabulary", async () => {
117
- // Synchronous spawn failure → `spawn_failed`.
118
- const spawnFailedSpawn = () => {
119
- throw new Error("boom");
120
- };
121
- const spawnFailed = await runAgent(makeProfile(), undefined, { spawn: spawnFailedSpawn });
122
- expect(spawnFailed.ok).toBe(false);
123
- expect(spawnFailed.reason).toBe("spawn_failed");
124
- expect(KNOWN_FAILURE_REASONS.has(spawnFailed.reason)).toBe(true);
125
- // Non-zero exit → `non_zero_exit`.
126
- const nonZero = fakeSpawn("", "oops", 7);
127
- const nonZeroResult = await runAgent(makeProfile(), undefined, { spawn: nonZero.spawn });
128
- expect(nonZeroResult.ok).toBe(false);
129
- expect(nonZeroResult.reason).toBe("non_zero_exit");
130
- expect(nonZeroResult.exitCode).toBe(7);
131
- // Malformed JSON when parseOutput is "json" → `parse_error`.
132
- const badJson = fakeSpawn("not json", "", 0);
133
- const parseResult = await runAgent(makeProfile({ parseOutput: "json" }), undefined, { spawn: badJson.spawn });
134
- expect(parseResult.ok).toBe(false);
135
- expect(parseResult.reason).toBe("parse_error");
136
- });
137
- test("timeout override produces `reason: 'timeout'` deterministically", async () => {
138
- // A spawn that hangs until kill() is called. We drive the timer
139
- // synchronously so the timeout fires immediately.
140
- const hangingSpawn = () => {
141
- const spawn = () => {
142
- let resolve;
143
- const exited = new Promise((r) => {
144
- resolve = r;
145
- });
146
- const proc = {
147
- exitCode: null,
148
- exited,
149
- stdout: asReadableStream(""),
150
- stderr: asReadableStream(""),
151
- stdin: null,
152
- kill: () => resolve?.(143),
153
- };
154
- return proc;
155
- };
156
- return { spawn };
157
- };
158
- const fakeTimers = [];
159
- let nextId = 1;
160
- const setTimeoutFn = ((cb) => {
161
- const id = nextId++;
162
- fakeTimers.push({ id, cb });
163
- return id;
164
- });
165
- const clearTimeoutFn = ((id) => {
166
- const idx = fakeTimers.findIndex((t) => t.id === id);
167
- if (idx >= 0)
168
- fakeTimers.splice(idx, 1);
169
- });
170
- const { spawn } = hangingSpawn();
171
- const promise = runAgent(makeProfile(), undefined, {
172
- spawn,
173
- timeoutMs: 10,
174
- setTimeoutFn,
175
- clearTimeoutFn,
176
- });
177
- // Drive the timer synchronously.
178
- expect(fakeTimers.length).toBe(1);
179
- fakeTimers[0]?.cb();
180
- const result = await promise;
181
- expect(result.ok).toBe(false);
182
- expect(result.reason).toBe("timeout");
183
- expect(typeof result.error).toBe("string");
184
- });
185
- test("`AgentFailureReason` union from the barrel matches the documented vocabulary", () => {
186
- // Compile-time + runtime: assigning each known reason string to the
187
- // exported type pins the union shape. If the union narrows or
188
- // widens, this block fails to compile (the runtime arm just
189
- // mirrors the same set so the test reads end-to-end).
190
- const reasons = ["timeout", "spawn_failed", "non_zero_exit", "parse_error"];
191
- expect(new Set(reasons)).toEqual(KNOWN_FAILURE_REASONS);
192
- });
193
- });
@@ -1,112 +0,0 @@
1
- /**
2
- * Architecture seam test — `src/llm/*` is bounded and stateless.
3
- *
4
- * Locks v1 spec §9.7 (LLM/agent boundary) and §14.4 (statelessness
5
- * invariant). Issue #222.
6
- *
7
- * The test inspects the **module shape** of each public LLM helper,
8
- * not the source text. The contract under test is:
9
- *
10
- * 1. Each module's runtime exports are functions (or values that
11
- * describe pure data, e.g. a model-name constant). They are not
12
- * instances of stateful clients.
13
- * 2. The only module-level singleton across `src/llm/*` is the local
14
- * embedder pipeline in `src/llm/embedder.ts`, which is documented
15
- * as a stateless model handle and exposes `resetLocalEmbedder()`
16
- * so tests can construct a fresh pipeline.
17
- * 3. The transport-level helpers (`chatCompletion`, `enhanceMetadata`,
18
- * `splitMemoryIntoAtomicFacts`, `resolveIndexPassLLM`) take the
19
- * connection config as a parameter — they do not read it from
20
- * module state.
21
- *
22
- * Together these properties keep every in-tree LLM call to a single
23
- * bounded request/response cycle. Crossing this seam (introducing a
24
- * conversation cache, a streaming session, or a hidden module-level
25
- * config) is a contract violation and should fail this test.
26
- */
27
- import { describe, expect, test } from "bun:test";
28
- import * as client from "../../src/llm/client";
29
- import * as embedder from "../../src/llm/embedder";
30
- import * as indexPasses from "../../src/llm/index-passes";
31
- import * as memoryInfer from "../../src/llm/memory-infer";
32
- import * as metadataEnhance from "../../src/llm/metadata-enhance";
33
- describe("src/llm/* is bounded and stateless (v1 spec §9.7, §14.4)", () => {
34
- test("`client` exports are pure functions", () => {
35
- expect(typeof client.chatCompletion).toBe("function");
36
- expect(typeof client.stripJsonFences).toBe("function");
37
- expect(typeof client.parseJsonResponse).toBe("function");
38
- expect(typeof client.isLlmAvailable).toBe("function");
39
- expect(typeof client.probeLlmCapabilities).toBe("function");
40
- });
41
- test("`client.chatCompletion` accepts the connection config as its first arg", () => {
42
- // Length-on-function reflects declared (non-rest) parameter count.
43
- // The contract is: the connection config is a parameter, not module
44
- // state. Two declared params: (config, messages); options is
45
- // optional and trailing.
46
- expect(client.chatCompletion.length).toBeGreaterThanOrEqual(2);
47
- });
48
- test("`client` does not export any non-function runtime value (no module-level client instance)", () => {
49
- for (const [name, value] of Object.entries(client)) {
50
- if (value === undefined)
51
- continue;
52
- expect(typeof value).toBe("function");
53
- // Eslint-style sanity: anything callable that exposes mutable
54
- // state at module scope would surface here as a non-function
55
- // export (an instance, a Map, etc).
56
- void name;
57
- }
58
- });
59
- test("`metadata-enhance` exports a single pure helper", () => {
60
- expect(typeof metadataEnhance.enhanceMetadata).toBe("function");
61
- expect(metadataEnhance.enhanceMetadata.length).toBeGreaterThanOrEqual(2);
62
- const runtimeExports = Object.entries(metadataEnhance).filter(([, v]) => v !== undefined);
63
- expect(runtimeExports.length).toBe(1);
64
- });
65
- test("`memory-infer` exports a single pure helper", () => {
66
- expect(typeof memoryInfer.splitMemoryIntoAtomicFacts).toBe("function");
67
- expect(memoryInfer.splitMemoryIntoAtomicFacts.length).toBeGreaterThanOrEqual(2);
68
- const runtimeExports = Object.entries(memoryInfer).filter(([, v]) => v !== undefined);
69
- expect(runtimeExports.length).toBe(1);
70
- });
71
- test("`index-passes` exports a single pure resolver", () => {
72
- expect(typeof indexPasses.resolveIndexPassLLM).toBe("function");
73
- expect(indexPasses.resolveIndexPassLLM.length).toBeGreaterThanOrEqual(2);
74
- const runtimeExports = Object.entries(indexPasses).filter(([, v]) => v !== undefined);
75
- expect(runtimeExports.length).toBe(1);
76
- });
77
- test("`embedder` only exports functions and pure data constants", () => {
78
- // The embedder facade has more surface than the chat helpers because
79
- // it owns the local-pipeline cache. Every export must still be a
80
- // function or a pure data value (e.g. the default model name).
81
- const allowedNonFunctionNames = new Set(["DEFAULT_LOCAL_MODEL"]);
82
- for (const [name, value] of Object.entries(embedder)) {
83
- if (value === undefined)
84
- continue;
85
- if (allowedNonFunctionNames.has(name)) {
86
- // Pure-data constant. Must be a primitive (string/number/boolean).
87
- expect(["string", "number", "boolean"]).toContain(typeof value);
88
- continue;
89
- }
90
- expect(typeof value).toBe("function");
91
- }
92
- });
93
- test("`embedder.resetLocalEmbedder` exists so tests can rebuild the cached pipeline", () => {
94
- // The local embedder is a documented module-level singleton — it is
95
- // a stateless model handle, not a session. The reset hook is part
96
- // of the seam: it lets tests assert pipeline-construction logic
97
- // without relying on module-load order.
98
- expect(typeof embedder.resetLocalEmbedder).toBe("function");
99
- // resetLocalEmbedder is a no-arg function.
100
- expect(embedder.resetLocalEmbedder.length).toBe(0);
101
- });
102
- test("`stripJsonFences` and `parseJsonResponse` are referentially transparent", () => {
103
- // Two calls with the same input produce the same output. These are
104
- // pure, so this is not a deep test of statelessness — but it does
105
- // pin the seam: response parsing is not allowed to learn from
106
- // prior responses.
107
- const fenced = '```json\n{"a":1}\n```';
108
- expect(client.stripJsonFences(fenced)).toBe(client.stripJsonFences(fenced));
109
- expect(client.parseJsonResponse(fenced)).toEqual({ a: 1 });
110
- expect(client.parseJsonResponse(fenced)).toEqual({ a: 1 });
111
- });
112
- });