akm-cli 0.7.0-rc1 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. package/dist/{src/cli.js → cli.js} +100 -16
  2. package/dist/{src/commands → commands}/config-cli.js +42 -0
  3. package/dist/{src/commands → commands}/history.js +78 -7
  4. package/dist/{src/commands → commands}/registry-search.js +69 -6
  5. package/dist/{src/commands → commands}/search.js +30 -3
  6. package/dist/{src/commands → commands}/show.js +29 -0
  7. package/dist/{src/commands → commands}/source-add.js +5 -1
  8. package/dist/{src/commands → commands}/source-manage.js +7 -1
  9. package/dist/{src/core → core}/config.js +28 -0
  10. package/dist/{src/indexer → indexer}/db-search.js +1 -0
  11. package/dist/{src/indexer → indexer}/indexer.js +16 -2
  12. package/dist/{src/indexer → indexer}/matchers.js +1 -1
  13. package/dist/{src/indexer → indexer}/search-source.js +4 -2
  14. package/dist/{src/integrations → integrations}/agent/profiles.js +1 -1
  15. package/dist/{src/integrations → integrations}/agent/spawn.js +67 -16
  16. package/dist/{src/integrations → integrations}/github.js +9 -3
  17. package/dist/{src/llm → llm}/embedders/remote.js +37 -3
  18. package/dist/{src/output → output}/cli-hints.js +15 -2
  19. package/dist/{src/output → output}/renderers.js +3 -1
  20. package/dist/{src/output → output}/shapes.js +8 -1
  21. package/dist/{src/output → output}/text.js +156 -3
  22. package/dist/{src/registry → registry}/build-index.js +5 -4
  23. package/dist/{src/registry → registry}/providers/static-index.js +3 -1
  24. package/dist/{src/setup → setup}/setup.js +9 -0
  25. package/dist/{src/wiki → wiki}/wiki.js +54 -6
  26. package/dist/{src/workflows → workflows}/runs.js +37 -3
  27. package/package.json +8 -8
  28. package/dist/tests/add-website-source.test.js +0 -119
  29. package/dist/tests/agent/agent-config-loader.test.js +0 -70
  30. package/dist/tests/agent/agent-config.test.js +0 -221
  31. package/dist/tests/agent/agent-detect.test.js +0 -100
  32. package/dist/tests/agent/agent-spawn.test.js +0 -234
  33. package/dist/tests/agent-output.test.js +0 -186
  34. package/dist/tests/architecture/agent-no-llm-sdk-guard.test.js +0 -103
  35. package/dist/tests/architecture/agent-spawn-seam.test.js +0 -193
  36. package/dist/tests/architecture/llm-stateless-seam.test.js +0 -112
  37. package/dist/tests/asset-ref.test.js +0 -192
  38. package/dist/tests/asset-registry.test.js +0 -103
  39. package/dist/tests/asset-spec.test.js +0 -241
  40. package/dist/tests/bench/attribution.test.js +0 -995
  41. package/dist/tests/bench/cleanup-sigint.test.js +0 -83
  42. package/dist/tests/bench/cleanup.js +0 -203
  43. package/dist/tests/bench/cleanup.test.js +0 -166
  44. package/dist/tests/bench/cli.js +0 -683
  45. package/dist/tests/bench/cli.test.js +0 -177
  46. package/dist/tests/bench/compare.test.js +0 -556
  47. package/dist/tests/bench/corpus.js +0 -314
  48. package/dist/tests/bench/corpus.test.js +0 -258
  49. package/dist/tests/bench/driver.js +0 -346
  50. package/dist/tests/bench/driver.test.js +0 -443
  51. package/dist/tests/bench/evolve-metrics.js +0 -179
  52. package/dist/tests/bench/evolve-metrics.test.js +0 -187
  53. package/dist/tests/bench/evolve.js +0 -580
  54. package/dist/tests/bench/evolve.test.js +0 -616
  55. package/dist/tests/bench/failure-modes.test.js +0 -300
  56. package/dist/tests/bench/feedback-integrity.test.js +0 -456
  57. package/dist/tests/bench/leakage.test.js +0 -125
  58. package/dist/tests/bench/learning-curve.test.js +0 -133
  59. package/dist/tests/bench/metrics.js +0 -2319
  60. package/dist/tests/bench/metrics.test.js +0 -1144
  61. package/dist/tests/bench/no-os-tmpdir-invariant.test.js +0 -43
  62. package/dist/tests/bench/report.js +0 -1821
  63. package/dist/tests/bench/report.test.js +0 -989
  64. package/dist/tests/bench/runner.js +0 -536
  65. package/dist/tests/bench/runner.test.js +0 -958
  66. package/dist/tests/bench/search-bridge.test.js +0 -331
  67. package/dist/tests/bench/tmp.js +0 -41
  68. package/dist/tests/bench/trajectory.js +0 -116
  69. package/dist/tests/bench/trajectory.test.js +0 -127
  70. package/dist/tests/bench/verifier.js +0 -109
  71. package/dist/tests/bench/verifier.test.js +0 -118
  72. package/dist/tests/bench/workflow-evaluator.js +0 -557
  73. package/dist/tests/bench/workflow-evaluator.test.js +0 -421
  74. package/dist/tests/bench/workflow-spec.js +0 -358
  75. package/dist/tests/bench/workflow-spec.test.js +0 -363
  76. package/dist/tests/bench/workflow-trace.js +0 -438
  77. package/dist/tests/bench/workflow-trace.test.js +0 -254
  78. package/dist/tests/benchmark-search-quality.js +0 -536
  79. package/dist/tests/benchmark-suite.js +0 -1441
  80. package/dist/tests/capture-cli.test.js +0 -112
  81. package/dist/tests/cli-errors.test.js +0 -203
  82. package/dist/tests/commands/events.test.js +0 -370
  83. package/dist/tests/commands/history.test.js +0 -223
  84. package/dist/tests/commands/import.test.js +0 -103
  85. package/dist/tests/commands/proposal-cli.test.js +0 -209
  86. package/dist/tests/commands/reflect-propose-cli.test.js +0 -333
  87. package/dist/tests/commands/remember.test.js +0 -97
  88. package/dist/tests/commands/scope-flags.test.js +0 -300
  89. package/dist/tests/commands/search.test.js +0 -537
  90. package/dist/tests/commands/show-indexer-parity.test.js +0 -117
  91. package/dist/tests/commands/show.test.js +0 -294
  92. package/dist/tests/common.test.js +0 -266
  93. package/dist/tests/completions.test.js +0 -142
  94. package/dist/tests/config-cli.test.js +0 -193
  95. package/dist/tests/config-llm-features.test.js +0 -139
  96. package/dist/tests/config.test.js +0 -544
  97. package/dist/tests/contracts/migration-baseline.test.js +0 -43
  98. package/dist/tests/contracts/reflect-propose-envelope.test.js +0 -139
  99. package/dist/tests/contracts/spec-helpers.js +0 -46
  100. package/dist/tests/contracts/v1-spec-section-11-proposal-queue.test.js +0 -228
  101. package/dist/tests/contracts/v1-spec-section-12-agent-config.test.js +0 -56
  102. package/dist/tests/contracts/v1-spec-section-13-lesson-type.test.js +0 -34
  103. package/dist/tests/contracts/v1-spec-section-14-llm-features.test.js +0 -94
  104. package/dist/tests/contracts/v1-spec-section-4-1-asset-types.test.js +0 -39
  105. package/dist/tests/contracts/v1-spec-section-4-2-quality-rules.test.js +0 -44
  106. package/dist/tests/contracts/v1-spec-section-5-configuration.test.js +0 -47
  107. package/dist/tests/contracts/v1-spec-section-6-orchestration.test.js +0 -40
  108. package/dist/tests/contracts/v1-spec-section-7-module-layout.test.js +0 -58
  109. package/dist/tests/contracts/v1-spec-section-8-extension-points.test.js +0 -34
  110. package/dist/tests/contracts/v1-spec-section-9-4-cli-surface.test.js +0 -75
  111. package/dist/tests/contracts/v1-spec-section-9-7-llm-agent-boundary.test.js +0 -36
  112. package/dist/tests/core/write-source.test.js +0 -366
  113. package/dist/tests/curate-command.test.js +0 -87
  114. package/dist/tests/db-scoring.test.js +0 -201
  115. package/dist/tests/db.test.js +0 -654
  116. package/dist/tests/distill-cli-flag.test.js +0 -208
  117. package/dist/tests/distill.test.js +0 -515
  118. package/dist/tests/docker-install.test.js +0 -120
  119. package/dist/tests/e2e.test.js +0 -1398
  120. package/dist/tests/embedder.test.js +0 -340
  121. package/dist/tests/embedding-model-config.test.js +0 -379
  122. package/dist/tests/feedback-command.test.js +0 -172
  123. package/dist/tests/file-context.test.js +0 -552
  124. package/dist/tests/fixtures/scripts/git/summarize-diff.js +0 -9
  125. package/dist/tests/fixtures/scripts/lint/eslint-check.js +0 -7
  126. package/dist/tests/fixtures/stashes/load.js +0 -166
  127. package/dist/tests/fixtures/stashes/load.test.js +0 -88
  128. package/dist/tests/fixtures/stashes/ranking-baseline/scripts/mem0-search.js +0 -12
  129. package/dist/tests/frontmatter.test.js +0 -190
  130. package/dist/tests/fts-field-weighting.test.js +0 -254
  131. package/dist/tests/fuzzy-search.test.js +0 -230
  132. package/dist/tests/git-provider-clone.test.js +0 -45
  133. package/dist/tests/github.test.js +0 -161
  134. package/dist/tests/graph-boost-ranking.test.js +0 -305
  135. package/dist/tests/graph-extraction.test.js +0 -282
  136. package/dist/tests/helpers/usage-events.js +0 -8
  137. package/dist/tests/index-pass-llm.test.js +0 -161
  138. package/dist/tests/indexer.test.js +0 -559
  139. package/dist/tests/info-command.test.js +0 -166
  140. package/dist/tests/init.test.js +0 -69
  141. package/dist/tests/install-script.test.js +0 -246
  142. package/dist/tests/integration/agent-real-profile.test.js +0 -94
  143. package/dist/tests/issue-36-repro.test.js +0 -304
  144. package/dist/tests/issues-191-194.test.js +0 -160
  145. package/dist/tests/lesson-lint.test.js +0 -111
  146. package/dist/tests/llm-client.test.js +0 -115
  147. package/dist/tests/llm-feature-gate.test.js +0 -151
  148. package/dist/tests/llm.test.js +0 -139
  149. package/dist/tests/lockfile.test.js +0 -216
  150. package/dist/tests/manifest.test.js +0 -205
  151. package/dist/tests/markdown.test.js +0 -126
  152. package/dist/tests/matchers-unit.test.js +0 -189
  153. package/dist/tests/memory-inference.test.js +0 -299
  154. package/dist/tests/merge-scoring.test.js +0 -136
  155. package/dist/tests/metadata.test.js +0 -313
  156. package/dist/tests/migration-help.test.js +0 -89
  157. package/dist/tests/origin-resolve.test.js +0 -124
  158. package/dist/tests/output-baseline.test.js +0 -217
  159. package/dist/tests/output-shapes-unit.test.js +0 -476
  160. package/dist/tests/parallel-search.test.js +0 -272
  161. package/dist/tests/parameter-metadata.test.js +0 -365
  162. package/dist/tests/paths.test.js +0 -177
  163. package/dist/tests/progressive-disclosure.test.js +0 -280
  164. package/dist/tests/proposals.test.js +0 -279
  165. package/dist/tests/proposed-quality.test.js +0 -271
  166. package/dist/tests/provider-registry.test.js +0 -32
  167. package/dist/tests/ranking-regression.test.js +0 -548
  168. package/dist/tests/reflect-propose.test.js +0 -455
  169. package/dist/tests/registry-build-index.test.js +0 -378
  170. package/dist/tests/registry-cli.test.js +0 -290
  171. package/dist/tests/registry-index-v2.test.js +0 -430
  172. package/dist/tests/registry-install.test.js +0 -728
  173. package/dist/tests/registry-providers/parity.test.js +0 -189
  174. package/dist/tests/registry-providers/skills-sh.test.js +0 -309
  175. package/dist/tests/registry-providers/static-index.test.js +0 -204
  176. package/dist/tests/registry-resolve.test.js +0 -126
  177. package/dist/tests/registry-search.test.js +0 -723
  178. package/dist/tests/remember-frontmatter.test.js +0 -380
  179. package/dist/tests/remember-unit.test.js +0 -123
  180. package/dist/tests/ripgrep-install.test.js +0 -251
  181. package/dist/tests/ripgrep-resolve.test.js +0 -108
  182. package/dist/tests/ripgrep.test.js +0 -163
  183. package/dist/tests/save-command.test.js +0 -94
  184. package/dist/tests/save-trust-qa-fixes.test.js +0 -270
  185. package/dist/tests/scoring-pipeline.test.js +0 -648
  186. package/dist/tests/search-include-proposed-cli.test.js +0 -118
  187. package/dist/tests/self-update.test.js +0 -442
  188. package/dist/tests/semantic-search-e2e.test.js +0 -512
  189. package/dist/tests/semantic-status.test.js +0 -471
  190. package/dist/tests/setup-run.integration.js +0 -877
  191. package/dist/tests/setup-wizard.test.js +0 -198
  192. package/dist/tests/setup.test.js +0 -131
  193. package/dist/tests/source-add.test.js +0 -11
  194. package/dist/tests/source-clone.test.js +0 -254
  195. package/dist/tests/source-manage.test.js +0 -366
  196. package/dist/tests/source-providers/filesystem.test.js +0 -82
  197. package/dist/tests/source-providers/git.test.js +0 -252
  198. package/dist/tests/source-providers/website.test.js +0 -128
  199. package/dist/tests/source-qa-fixes.test.js +0 -268
  200. package/dist/tests/source-registry.test.js +0 -350
  201. package/dist/tests/source-resolve.test.js +0 -100
  202. package/dist/tests/source-source.test.js +0 -221
  203. package/dist/tests/source.test.js +0 -533
  204. package/dist/tests/tar-utils-scan.test.js +0 -73
  205. package/dist/tests/toggle-components.test.js +0 -73
  206. package/dist/tests/usage-telemetry.test.js +0 -265
  207. package/dist/tests/utility-scoring.test.js +0 -558
  208. package/dist/tests/vault-load-error.test.js +0 -78
  209. package/dist/tests/vault-qa-fixes.test.js +0 -194
  210. package/dist/tests/vault.test.js +0 -429
  211. package/dist/tests/vector-search.test.js +0 -608
  212. package/dist/tests/walker.test.js +0 -252
  213. package/dist/tests/wave2-cluster-bc.test.js +0 -228
  214. package/dist/tests/wave2-cluster-d.test.js +0 -180
  215. package/dist/tests/wave2-cluster-e.test.js +0 -179
  216. package/dist/tests/wiki-qa-fixes.test.js +0 -270
  217. package/dist/tests/wiki.test.js +0 -529
  218. package/dist/tests/workflow-cli.test.js +0 -271
  219. package/dist/tests/workflow-markdown.test.js +0 -171
  220. package/dist/tests/workflow-path-escape.test.js +0 -132
  221. package/dist/tests/workflow-qa-fixes.test.js +0 -377
  222. package/dist/tests/workflows/indexer-rejection.test.js +0 -213
  223. /package/dist/{src/commands → commands}/completions.js +0 -0
  224. /package/dist/{src/commands → commands}/curate.js +0 -0
  225. /package/dist/{src/commands → commands}/distill.js +0 -0
  226. /package/dist/{src/commands → commands}/events.js +0 -0
  227. /package/dist/{src/commands → commands}/info.js +0 -0
  228. /package/dist/{src/commands → commands}/init.js +0 -0
  229. /package/dist/{src/commands → commands}/install-audit.js +0 -0
  230. /package/dist/{src/commands → commands}/installed-stashes.js +0 -0
  231. /package/dist/{src/commands → commands}/migration-help.js +0 -0
  232. /package/dist/{src/commands → commands}/proposal.js +0 -0
  233. /package/dist/{src/commands → commands}/propose.js +0 -0
  234. /package/dist/{src/commands → commands}/reflect.js +0 -0
  235. /package/dist/{src/commands → commands}/remember.js +0 -0
  236. /package/dist/{src/commands → commands}/self-update.js +0 -0
  237. /package/dist/{src/commands → commands}/source-clone.js +0 -0
  238. /package/dist/{src/commands → commands}/vault.js +0 -0
  239. /package/dist/{src/core → core}/asset-ref.js +0 -0
  240. /package/dist/{src/core → core}/asset-registry.js +0 -0
  241. /package/dist/{src/core → core}/asset-spec.js +0 -0
  242. /package/dist/{src/core → core}/common.js +0 -0
  243. /package/dist/{src/core → core}/errors.js +0 -0
  244. /package/dist/{src/core → core}/events.js +0 -0
  245. /package/dist/{src/core → core}/frontmatter.js +0 -0
  246. /package/dist/{src/core → core}/lesson-lint.js +0 -0
  247. /package/dist/{src/core → core}/markdown.js +0 -0
  248. /package/dist/{src/core → core}/paths.js +0 -0
  249. /package/dist/{src/core → core}/proposals.js +0 -0
  250. /package/dist/{src/core → core}/warn.js +0 -0
  251. /package/dist/{src/core → core}/write-source.js +0 -0
  252. /package/dist/{src/indexer → indexer}/db.js +0 -0
  253. /package/dist/{src/indexer → indexer}/file-context.js +0 -0
  254. /package/dist/{src/indexer → indexer}/graph-boost.js +0 -0
  255. /package/dist/{src/indexer → indexer}/graph-extraction.js +0 -0
  256. /package/dist/{src/indexer → indexer}/manifest.js +0 -0
  257. /package/dist/{src/indexer → indexer}/memory-inference.js +0 -0
  258. /package/dist/{src/indexer → indexer}/metadata.js +0 -0
  259. /package/dist/{src/indexer → indexer}/search-fields.js +0 -0
  260. /package/dist/{src/indexer → indexer}/semantic-status.js +0 -0
  261. /package/dist/{src/indexer → indexer}/usage-events.js +0 -0
  262. /package/dist/{src/indexer → indexer}/walker.js +0 -0
  263. /package/dist/{src/integrations → integrations}/agent/config.js +0 -0
  264. /package/dist/{src/integrations → integrations}/agent/detect.js +0 -0
  265. /package/dist/{src/integrations → integrations}/agent/index.js +0 -0
  266. /package/dist/{src/integrations → integrations}/agent/prompts.js +0 -0
  267. /package/dist/{src/integrations → integrations}/lockfile.js +0 -0
  268. /package/dist/{src/llm → llm}/client.js +0 -0
  269. /package/dist/{src/llm → llm}/embedder.js +0 -0
  270. /package/dist/{src/llm → llm}/embedders/cache.js +0 -0
  271. /package/dist/{src/llm → llm}/embedders/local.js +0 -0
  272. /package/dist/{src/llm → llm}/embedders/types.js +0 -0
  273. /package/dist/{src/llm → llm}/feature-gate.js +0 -0
  274. /package/dist/{src/llm → llm}/graph-extract.js +0 -0
  275. /package/dist/{src/llm → llm}/index-passes.js +0 -0
  276. /package/dist/{src/llm → llm}/memory-infer.js +0 -0
  277. /package/dist/{src/llm → llm}/metadata-enhance.js +0 -0
  278. /package/dist/{src/output → output}/context.js +0 -0
  279. /package/dist/{src/registry → registry}/create-provider-registry.js +0 -0
  280. /package/dist/{src/registry → registry}/factory.js +0 -0
  281. /package/dist/{src/registry → registry}/origin-resolve.js +0 -0
  282. /package/dist/{src/registry → registry}/providers/index.js +0 -0
  283. /package/dist/{src/registry → registry}/providers/skills-sh.js +0 -0
  284. /package/dist/{src/registry → registry}/providers/types.js +0 -0
  285. /package/dist/{src/registry → registry}/resolve.js +0 -0
  286. /package/dist/{src/registry → registry}/types.js +0 -0
  287. /package/dist/{src/setup → setup}/detect.js +0 -0
  288. /package/dist/{src/setup → setup}/ripgrep-install.js +0 -0
  289. /package/dist/{src/setup → setup}/ripgrep-resolve.js +0 -0
  290. /package/dist/{src/setup → setup}/steps.js +0 -0
  291. /package/dist/{src/sources → sources}/include.js +0 -0
  292. /package/dist/{src/sources → sources}/provider-factory.js +0 -0
  293. /package/dist/{src/sources → sources}/provider.js +0 -0
  294. /package/dist/{src/sources → sources}/providers/filesystem.js +0 -0
  295. /package/dist/{src/sources → sources}/providers/git.js +0 -0
  296. /package/dist/{src/sources → sources}/providers/index.js +0 -0
  297. /package/dist/{src/sources → sources}/providers/install-types.js +0 -0
  298. /package/dist/{src/sources → sources}/providers/npm.js +0 -0
  299. /package/dist/{src/sources → sources}/providers/provider-utils.js +0 -0
  300. /package/dist/{src/sources → sources}/providers/sync-from-ref.js +0 -0
  301. /package/dist/{src/sources → sources}/providers/tar-utils.js +0 -0
  302. /package/dist/{src/sources → sources}/providers/website.js +0 -0
  303. /package/dist/{src/sources → sources}/resolve.js +0 -0
  304. /package/dist/{src/sources → sources}/types.js +0 -0
  305. /package/dist/{src/templates → templates}/wiki-templates.js +0 -0
  306. /package/dist/{src/version.js → version.js} +0 -0
  307. /package/dist/{src/workflows → workflows}/authoring.js +0 -0
  308. /package/dist/{src/workflows → workflows}/cli.js +0 -0
  309. /package/dist/{src/workflows → workflows}/db.js +0 -0
  310. /package/dist/{src/workflows → workflows}/document-cache.js +0 -0
  311. /package/dist/{src/workflows → workflows}/parser.js +0 -0
  312. /package/dist/{src/workflows → workflows}/renderer.js +0 -0
  313. /package/dist/{src/workflows → workflows}/schema.js +0 -0
  314. /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
- });