capman 0.5.2 → 0.5.4

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 (53) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/CODEBASE.md +15 -9
  3. package/bin/lib/cmd-explain.js +2 -2
  4. package/bin/lib/cmd-run.js +2 -2
  5. package/bin/lib/shared.js +8 -2
  6. package/dist/cjs/cache.d.ts +2 -1
  7. package/dist/cjs/cache.d.ts.map +1 -1
  8. package/dist/cjs/cache.js +11 -6
  9. package/dist/cjs/cache.js.map +1 -1
  10. package/dist/cjs/engine.d.ts +30 -0
  11. package/dist/cjs/engine.d.ts.map +1 -1
  12. package/dist/cjs/engine.js +69 -25
  13. package/dist/cjs/engine.js.map +1 -1
  14. package/dist/cjs/generator.d.ts.map +1 -1
  15. package/dist/cjs/generator.js +16 -1
  16. package/dist/cjs/generator.js.map +1 -1
  17. package/dist/cjs/learning.d.ts +20 -10
  18. package/dist/cjs/learning.d.ts.map +1 -1
  19. package/dist/cjs/learning.js +146 -129
  20. package/dist/cjs/learning.js.map +1 -1
  21. package/dist/cjs/matcher.d.ts +5 -2
  22. package/dist/cjs/matcher.d.ts.map +1 -1
  23. package/dist/cjs/matcher.js +73 -10
  24. package/dist/cjs/matcher.js.map +1 -1
  25. package/dist/cjs/parser.js +8 -2
  26. package/dist/cjs/parser.js.map +1 -1
  27. package/dist/cjs/resolver.d.ts +7 -0
  28. package/dist/cjs/resolver.d.ts.map +1 -1
  29. package/dist/cjs/resolver.js +47 -23
  30. package/dist/cjs/resolver.js.map +1 -1
  31. package/dist/cjs/schema.d.ts +93 -1
  32. package/dist/cjs/schema.d.ts.map +1 -1
  33. package/dist/cjs/schema.js +5 -2
  34. package/dist/cjs/schema.js.map +1 -1
  35. package/dist/cjs/version.d.ts +1 -1
  36. package/dist/cjs/version.js +1 -1
  37. package/dist/esm/cache.d.ts +2 -1
  38. package/dist/esm/cache.js +11 -6
  39. package/dist/esm/engine.d.ts +30 -0
  40. package/dist/esm/engine.js +69 -25
  41. package/dist/esm/generator.js +16 -1
  42. package/dist/esm/learning.d.ts +20 -10
  43. package/dist/esm/learning.js +146 -129
  44. package/dist/esm/matcher.d.ts +5 -2
  45. package/dist/esm/matcher.js +70 -10
  46. package/dist/esm/parser.js +8 -2
  47. package/dist/esm/resolver.d.ts +7 -0
  48. package/dist/esm/resolver.js +47 -23
  49. package/dist/esm/schema.d.ts +93 -1
  50. package/dist/esm/schema.js +5 -2
  51. package/dist/esm/version.d.ts +1 -1
  52. package/dist/esm/version.js +1 -1
  53. package/package.json +11 -10
package/CHANGELOG.md CHANGED
@@ -4,6 +4,49 @@ All notable changes to capman are documented here.
4
4
 
5
5
  ---
6
6
 
7
+ ## [0.5.4] — 2026-04-29
8
+ ### Added
9
+ - `engine.loadManifest(manifest)` — hot-reloads the manifest without creating a new engine instance. Preserves cache, learning history, and rate limiter state. Clears cache automatically since cached results from the old manifest are no longer valid
10
+ - `fuzzyMatch` and `fuzzyThreshold` options in `EngineOptions` — opt-in Fuse.js fuzzy matching catches typos, slight paraphrases, and morphological variants that exact keyword matching misses. Disabled by default, never runs in `cheap` mode
11
+ - POSIX `--` sentinel support in CLI — `capman run -- "query"` and `capman explain -- "query"` now correctly handle queries that start with `--` or contain flag-like strings
12
+
13
+ ### Fixed
14
+ - Example scoring now uses `Math.max` across examples instead of accumulating — a capability with 10 weak examples no longer beats one with a single precise example
15
+ - Fuse.js index built once per `match()` call using a flat corpus — each example/description/name is its own searchable entry, grouped by capability after search. Avoids the dead-weight property bug and multi-key aggregation issues from the previous implementation
16
+
17
+ ### Tests
18
+ - 97 tests passing (up from 91)
19
+
20
+ ---
21
+
22
+ ## [0.5.3] — 2026-04-25
23
+ ### Fixed
24
+
25
+ **Critical:**
26
+ - `.capman/` added to `.gitignore` — cache and learning files were being committed to git, exposing internal API path structures and cached match data
27
+ - Session `userId` no longer leaks into query string on multi-endpoint capabilities — path template check was joining all endpoint paths before checking for `{param}`, causing false positives. Now checks per-endpoint inside `resolveApi()`
28
+ - File writes are now atomic — `FileCache` and `FileLearningStore` write to a `.tmp` file then rename, preventing corrupt JSON on process crash mid-write
29
+ - `ask()` now caches only after successful resolution — previously cached match result before Step 5 (resolve), permanently poisoning the cache on transient network failures
30
+
31
+ **High:**
32
+ - API path params validated against allowlist in `buildUrl()` — `encodeURIComponent` does not encode `/`, allowing path traversal via params like `../../admin`. Now mirrors the validation already applied in `resolveNav()`
33
+ - Raw query text removed from `info`-level logs — queries like `"orders for jane@corp.com"` were emitted at info level to stdout. Query text now only appears at `debug` level
34
+ - Retry logic now only retries safe/idempotent methods (GET, HEAD, OPTIONS) — previously retried POST/PUT/PATCH/DELETE which could cause duplicate orders, double charges etc. Add `retryAllMethods: true` to `ResolveOptions` to opt in to retrying writes
35
+ - `FileCache` concurrent load guard added — two simultaneous `get()` calls before `loaded = true` both read the file. Now serialized through a shared `loadPromise`
36
+
37
+ **Medium:**
38
+ - Version comparison now validates semver format before parsing — `Number("v0")` → `NaN`, `NaN !== NaN` always true, causing spurious version warnings on every startup for non-semver manifest versions
39
+ - `LearningIndex` class extracted — `updateIndex`, `subtractFromIndex`, `rebuildIndex` were copy-pasted verbatim (~80 lines) between `FileLearningStore` and `MemoryLearningStore`. Both now compose `LearningIndex`
40
+ - `computeStats()` dead code removed — was only used by old `getStats()` before incremental index was introduced in v0.5.0
41
+ - `FileLearningStore` now debounces saves — previously wrote full JSON on every `record()` call (every `ask()`). Now batches with a 5s debounce timer and flushes synchronously on `process.exit`, `SIGTERM`, `SIGINT`
42
+ - YAML catch block now distinguishes `MODULE_NOT_FOUND` from actual parse errors using `err.code` — previously swallowed real YAML syntax errors with a generic message
43
+ - `baseUrl` now required by Zod schema when any capability uses `api` or `hybrid` resolver — previously optional, producing silent relative URLs that fail with opaque connection errors
44
+ - `explain()` now asserts `resolvedVia !== 'cache'` at runtime — makes the invariant that `explain()` never reads from cache explicit and catches future regressions immediately
45
+ - ESM config files now produce an actionable error — `ERR_REQUIRE_ESM` previously surfaced as a generic "Failed to load config" message with no guidance. Now explains the issue and lists three solutions
46
+
47
+ ### Tests
48
+ - 90 tests passing
49
+
7
50
  ## [0.5.2] — 2026-04-20
8
51
  ### Fixed
9
52
 
package/CODEBASE.md CHANGED
@@ -84,9 +84,10 @@ Key exports:
84
84
  - `STOPWORDS` — set of words filtered from scoring and learning index
85
85
 
86
86
  Scoring algorithm (weights):
87
- - Examples: best single-example overlap score — up to 60 points (not accumulated across examples)
87
+ - Examples: `Math.max` across all examples (best single match, up to 60 points),
88
88
  - Description match: up to 30 points
89
89
  - Name match: up to 10 points
90
+ - When `fuzzyMatch` enabled, Fuse.js flat corpus scores merged via `Math.max` with keyword scores.
90
91
 
91
92
  Param extraction:
92
93
  - `isIdParam` — single token (e.g. `order_id=1234`)
@@ -147,8 +148,9 @@ Usage analytics and keyword index — now incremental.
147
148
 
148
149
  Key exports:
149
150
  - `LearningStore` interface — `record(entry)`, `getStats()`, `getTopCapabilities(limit)`, `getIndex()`
150
- - `FileLearningStore` — persists to `.capman/learning.json`, caps at 10,000 entries
151
+ - `FileLearningStore` — persists to `.capman/learning.json`, caps at 10,000 entries. Saves are debounced (5s) with synchronous flush on process exit
151
152
  - `MemoryLearningStore` — in-memory only, used in tests
153
+ - `LearningIndex` — internal class shared by both stores. Maintains keyword index and stats counters incrementally. Eliminates ~80 lines of duplication
152
154
 
153
155
  `LearningEntry`:
154
156
  - `query`, `capabilityId`, `confidence`, `intent`, `extractedParams`
@@ -174,6 +176,8 @@ The recommended API — orchestrates matching, caching, learning, and tracing.
174
176
  Key exports:
175
177
  - `CapmanEngine` class
176
178
  - `EngineOptions` — all constructor options
179
+ - `fuzzyMatch` — enable Fuse.js fuzzy matching (default: false)
180
+ - `fuzzyThreshold` — Fuse.js threshold 0.0–1.0 (default: 0.4)
177
181
  - `EngineResult` — `{ match, resolution, resolvedVia, durationMs, trace }`
178
182
 
179
183
  ⚠️ **Concurrency:** `CapmanEngine` is not safe for sharing across concurrent async request handlers. The LLM rate limiter, circuit breaker, and learning index cache are instance-level mutable state. Create one engine per request in server deployments, or use `cheap` mode for shared instances.
@@ -184,6 +188,7 @@ Key exports:
184
188
  - `getStats()` → `KeywordStats | null`
185
189
  - `getTopCapabilities(limit?)` → `Array<{ id, hits }>`
186
190
  - `clearCache()`
191
+ - `loadManifest(manifest)` — hot-reloads manifest, clears cache, preserves learning and rate limiter state
187
192
 
188
193
  Matching pipeline in `ask()`:
189
194
  1. Cache check — return immediately on hit (public capabilities only)
@@ -250,9 +255,10 @@ Notable:
250
255
  Entry point only (~20 lines). Routes `command` to the correct module.
251
256
 
252
257
  ### `bin/lib/shared.js`
253
- Exports: `args`, `command`, `flags`, `getFlag`, `c`, `log`, `header`, `requireSrc`
258
+ Exports: `args`, `command`, `flags`, `getFlag`, `c`, `log`, `header`, `posArgs`, `requireSrc`
254
259
 
255
260
  `getFlag(name)` — exits with error if flag is present but has no value (e.g. `--from` with no path).
261
+ `posArgs` — positional arguments after POSIX `--` sentinel. Allows queries starting with `--` to be passed without flag interpretation
256
262
 
257
263
  ### `bin/lib/cmd-generate.js`
258
264
  Three generation paths: `--from` (OpenAPI), `--ai` (LLM-assisted), manual.
@@ -271,14 +277,14 @@ Contains `buildAIPrompt()` and `callLLM()`.
271
277
 
272
278
  ## tests/
273
279
 
274
- ### `tests/matcher.test.ts` — 16 tests
275
- Keyword scoring, OOS detection, param extraction, LLM edge cases (hallucinated ID, undefined reasoning)
280
+ ### `tests/matcher.test.ts` — 17 tests
281
+ Keyword scoring, OOS detection, param extraction, LLM edge cases (hallucinated ID, undefined reasoning), example scoring quality-over-quantity (Math.max)
276
282
 
277
- ### `tests/resolver.test.ts` — 22 tests
278
- API/nav/hybrid resolvers, privacy enforcement, session injection, null params, nav open redirect
283
+ ### `tests/resolver.test.ts` — 27 tests
284
+ API/nav/hybrid resolvers, privacy enforcement, session injection, null params, nav open redirect, API path param traversal rejection, multi-endpoint session param isolation, LRU cache eviction
279
285
 
280
- ### `tests/engine.test.ts` — 33 tests
281
- `ask()`, `explain()`, caching, learning, matching modes, trace, rate limiting, manifest version check, learning boost
286
+ ### `tests/engine.test.ts` — 44 tests
287
+ `ask()`, `explain()`, caching, learning, matching modes, trace, rate limiting, manifest version check, learning boost, query validation (TypeError/RangeError guards), LRU eviction, fuzzy matching (typos, cheap mode bypass, default disabled, strict threshold), `loadManifest()` hot-reload (cache cleared, learning preserved)
282
288
 
283
289
  ### `tests/parser.test.ts` — 9 tests
284
290
  OpenAPI capability extraction, privacy inference, param extraction, base URL, error handling
@@ -1,10 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const { header, log, c, args, getFlag, requireSrc } = require('./shared')
3
+ const { header, log, c, args, posArgs, getFlag, requireSrc } = require('./shared')
4
4
 
5
5
  module.exports = async function cmdExplain() {
6
6
  header()
7
- const query = args[1]
7
+ const query = posArgs[0] ?? args[1]
8
8
  const manifestPath = getFlag('--manifest') ?? 'manifest.json'
9
9
 
10
10
  if (!query) {
@@ -1,10 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const { header, log, c, args, flags, getFlag, requireSrc } = require('./shared')
3
+ const { header, log, c, args, posArgs, flags, getFlag, requireSrc } = require('./shared')
4
4
 
5
5
  module.exports = function cmdRun() {
6
6
  header()
7
- const query = args[1]
7
+ const query = posArgs[0] ?? args[1]
8
8
  const debug = flags.includes('--debug')
9
9
  const manifestPath = getFlag('--manifest') ?? 'manifest.json'
10
10
 
package/bin/lib/shared.js CHANGED
@@ -7,7 +7,13 @@ const fs = require('fs')
7
7
 
8
8
  const args = process.argv.slice(2)
9
9
  const command = args[0]
10
- const flags = args.slice(1)
10
+
11
+ // POSIX -- sentinel support — everything after -- is a positional argument,
12
+ // not a flag. Allows queries that start with -- or contain flag-like strings.
13
+ // e.g. capman run -- "--help me find orders"
14
+ const sentinelIdx = args.indexOf('--')
15
+ const flags = sentinelIdx !== -1 ? args.slice(1, sentinelIdx) : args.slice(1)
16
+ const posArgs = sentinelIdx !== -1 ? args.slice(sentinelIdx + 1) : []
11
17
 
12
18
  const getFlag = (name) => {
13
19
  const i = flags.indexOf(name)
@@ -74,4 +80,4 @@ function requireSrc() {
74
80
  process.exit(1)
75
81
  }
76
82
 
77
- module.exports = { args, command, flags, getFlag, c, log, header, requireSrc }
83
+ module.exports = { args, command, flags, getFlag, c, log, header, posArgs, requireSrc }
@@ -29,10 +29,11 @@ export declare class MemoryCache implements CacheStore {
29
29
  export declare class FileCache implements CacheStore {
30
30
  private filePath;
31
31
  private store;
32
- private loaded;
32
+ private loadPromise;
33
33
  private saveQueue;
34
34
  constructor(filePath?: string);
35
35
  private load;
36
+ private _doLoad;
36
37
  private save;
37
38
  private _doSave;
38
39
  get(key: string, ttlMs?: number): Promise<CacheEntry | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAK1C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,WAAW,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb;AAID,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IAC5D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CACxB;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;;GAKG;AAEH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GAC7C,MAAM,CAQR;AAOD,qBAAa,WAAY,YAAW,UAAU;IAC5C,OAAO,CAAC,KAAK,CAAgC;IAEvC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAiB5D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAepD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IACtB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAC9B;AAMD,qBAAa,SAAU,YAAW,UAAU;IAC1C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,KAAK,CAAyC;IACtD,OAAO,CAAC,MAAM,CAAoC;IAClD,OAAO,CAAC,SAAS,CAA6C;gBAElD,QAAQ,SAAuB;YAc7B,IAAI;IAwBlB,OAAO,CAAC,IAAI;YAKE,OAAO;IAaf,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAqB5D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAI9B;AAID,qBAAa,UAAW,YAAW,UAAU;IAC3C,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,IAAI,CAAW;gBAEX,QAAQ,SAAuB;IAKrC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAY5D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;0EACsE;IAChE,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAG9B"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAK1C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,WAAW,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb;AAID,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IAC5D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CACxB;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;;GAKG;AAEH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GAC7C,MAAM,CAQR;AAOD,qBAAa,WAAY,YAAW,UAAU;IAC5C,OAAO,CAAC,KAAK,CAAgC;IAEvC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAiB5D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAepD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IACtB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAC9B;AAMD,qBAAa,SAAU,YAAW,UAAU;IAC1C,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,KAAK,CAA2C;IACxD,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,SAAS,CAA+C;gBAEpD,QAAQ,SAAuB;IAcvC,OAAO,CAAC,IAAI;YAOE,OAAO;IAsBzB,OAAO,CAAC,IAAI;YAKE,OAAO;IAYf,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAqB5D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAI9B;AAID,qBAAa,UAAW,YAAW,UAAU;IAC3C,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,IAAI,CAAW;gBAEX,QAAQ,SAAuB;IAKrC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAY5D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;0EACsE;IAChE,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAG9B"}
package/dist/cjs/cache.js CHANGED
@@ -105,7 +105,7 @@ const FILE_CACHE_MAX = 2048;
105
105
  class FileCache {
106
106
  constructor(filePath = '.capman/cache.json') {
107
107
  this.store = new Map();
108
- this.loaded = false;
108
+ this.loadPromise = null;
109
109
  this.saveQueue = Promise.resolve();
110
110
  const cwd = process.cwd();
111
111
  const resolved = path.resolve(cwd, filePath);
@@ -117,9 +117,13 @@ class FileCache {
117
117
  this.filePath = resolved;
118
118
  logger_1.logger.info(`FileCache initialized — writing to: ${this.filePath}`);
119
119
  }
120
- async load() {
121
- if (this.loaded)
122
- return;
120
+ load() {
121
+ if (!this.loadPromise) {
122
+ this.loadPromise = this._doLoad();
123
+ }
124
+ return this.loadPromise;
125
+ }
126
+ async _doLoad() {
123
127
  try {
124
128
  const raw = await fs.promises.readFile(this.filePath, 'utf-8');
125
129
  const parsed = JSON.parse(raw);
@@ -141,7 +145,6 @@ class FileCache {
141
145
  catch {
142
146
  // File doesn't exist yet — start fresh
143
147
  }
144
- this.loaded = true;
145
148
  }
146
149
  save() {
147
150
  this.saveQueue = this.saveQueue.then(() => this._doSave());
@@ -151,7 +154,9 @@ class FileCache {
151
154
  try {
152
155
  const dir = path.dirname(this.filePath);
153
156
  await fs.promises.mkdir(dir, { recursive: true });
154
- await fs.promises.writeFile(this.filePath, JSON.stringify(Object.fromEntries(this.store), null, 2));
157
+ const tmp = `${this.filePath}.tmp`;
158
+ await fs.promises.writeFile(tmp, JSON.stringify(Object.fromEntries(this.store), null, 2));
159
+ await fs.promises.rename(tmp, this.filePath);
155
160
  }
156
161
  catch (err) {
157
162
  logger_1.logger.warn(`Failed to save file cache to ${this.filePath}: ${err instanceof Error ? err.message : String(err)}`);
@@ -1 +1 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"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"}
@@ -83,6 +83,20 @@ export interface EngineOptions {
83
83
  * @default 60000
84
84
  */
85
85
  llmCircuitBreakerResetMs?: number;
86
+ /**
87
+ * Enable fuzzy matching using Fuse.js — catches paraphrases, typos,
88
+ * and morphological variants that exact keyword matching misses.
89
+ * Example: "cancel my booking" matches a capability with "abort reservation" examples.
90
+ * Only applies in balanced and accurate modes — never in cheap mode.
91
+ * @default false
92
+ */
93
+ fuzzyMatch?: boolean;
94
+ /**
95
+ * Fuse.js threshold for fuzzy matching. 0.0 = exact match only, 1.0 = match anything.
96
+ * Lower values are stricter. Only used when fuzzyMatch is true.
97
+ * @default 0.4
98
+ */
99
+ fuzzyThreshold?: number;
86
100
  }
87
101
  export interface EngineResult {
88
102
  match: MatchResult;
@@ -105,6 +119,8 @@ export declare class CapmanEngine {
105
119
  private headers?;
106
120
  private threshold;
107
121
  private cacheTtlMs;
122
+ private fuzzyMatch;
123
+ private fuzzyThreshold;
108
124
  private maxLLMCallsPerMinute;
109
125
  private llmCooldownMs;
110
126
  private llmCircuitBreakerThreshold;
@@ -143,6 +159,20 @@ export declare class CapmanEngine {
143
159
  * Clear the cache.
144
160
  */
145
161
  clearCache(): Promise<void>;
162
+ private checkManifestVersion;
163
+ /**
164
+ * Replaces the active manifest without creating a new engine instance.
165
+ * Useful for hot-reloading manifests in long-running servers without
166
+ * losing cache, learning history, or rate limiter state.
167
+ *
168
+ * Note: clears the cache automatically — cached results from the old
169
+ * manifest are no longer valid after the manifest changes.
170
+ *
171
+ * @example
172
+ * const newManifest = generate(updatedConfig)
173
+ * await engine.loadManifest(newManifest)
174
+ */
175
+ loadManifest(manifest: Manifest): Promise<void>;
146
176
  /**
147
177
  * Explain what would happen for a query — without executing it.
148
178
  * Shows matched capability, all candidate scores with reasoning,
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/engine.ts"],"names":[],"mappings":"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"}
@@ -28,6 +28,8 @@ class CapmanEngine {
28
28
  this.llmCooldownMs = options.llmCooldownMs ?? 0;
29
29
  this.llmCircuitBreakerThreshold = options.llmCircuitBreakerThreshold ?? 3;
30
30
  this.llmCircuitBreakerResetMs = options.llmCircuitBreakerResetMs ?? 60_000;
31
+ this.fuzzyMatch = options.fuzzyMatch ?? false;
32
+ this.fuzzyThreshold = options.fuzzyThreshold ?? 0.4;
31
33
  // Cache — default MemoryCache (no filesystem writes), or disabled with false
32
34
  // Use FileCache or ComboCache explicitly for persistence across restarts
33
35
  this.cache = options.cache === false
@@ -40,15 +42,7 @@ class CapmanEngine {
40
42
  : (options.learning ?? new learning_1.MemoryLearningStore());
41
43
  logger_1.logger.info(`CapmanEngine initialized — mode: ${this.mode}, cache: ${this.cache ? 'enabled' : 'disabled'}, learning: ${this.learning ? 'enabled' : 'disabled'}`);
42
44
  // ── Manifest version compatibility check ─────────────────────────────────
43
- if (options.manifest.version) {
44
- const [mMaj, mMin] = options.manifest.version.split('.').map(Number);
45
- const [eMaj, eMin] = version_1.VERSION.split('.').map(Number);
46
- if (mMaj !== eMaj || mMin !== eMin) {
47
- console.warn(`[capman] Manifest version "${options.manifest.version}" was generated with a ` +
48
- `different engine version than "${version_1.VERSION}". This is usually fine across patch versions. ` +
49
- `If you experience unexpected matching issues, regenerate with: npx capman generate`);
50
- }
51
- }
45
+ this.checkManifestVersion(options.manifest);
52
46
  }
53
47
  /**
54
48
  * Ask the engine a natural language query.
@@ -77,7 +71,8 @@ class CapmanEngine {
77
71
  const cached = await this.cache.get(queryKey, this.cacheTtlMs ?? undefined);
78
72
  if (cached) {
79
73
  steps.push({ type: 'cache_check', status: 'hit', durationMs: Date.now() - cacheStart, detail: 'Served from cache' });
80
- logger_1.logger.info(`Cache hit for: "${query}"`);
74
+ logger_1.logger.info(`Cache hit — capability: "${cached.result.capability?.id ?? 'none'}"`);
75
+ logger_1.logger.debug(`Cache hit for query: "${query}"`);
81
76
  // Re-extract params from the current query — never re-use cached params.
82
77
  // Cached params belong to the original query (potentially from a different user).
83
78
  // e.g. User A: "show orders for john" → cached with { customer: 'john' }
@@ -128,15 +123,7 @@ class CapmanEngine {
128
123
  detail: `level: ${privacyLevel}`,
129
124
  });
130
125
  }
131
- // ── Step 4: Cache the match result (public capabilities only) ─────────────
132
- // Non-public capabilities are never cached — prevents auth bypass where
133
- // User A's cached match is served to User B without privacy enforcement.
134
- if (this.cache && matchResult.capability
135
- && matchResult.capability.privacy.level === 'public') {
136
- const queryKey = (0, cache_1.normalizeQuery)(query);
137
- await this.cache.set(queryKey, matchResult);
138
- }
139
- // ── Step 5: Resolve ──────────────────────────────────────────────────────
126
+ // ── Step 4: Resolve ──────────────────────────────────────────────────────
140
127
  const resolveStart = Date.now();
141
128
  const resolution = await (0, resolver_1.resolve)(matchResult, matchResult.extractedParams, this.resolveOptions(overrides));
142
129
  steps.push({
@@ -145,6 +132,16 @@ class CapmanEngine {
145
132
  durationMs: Date.now() - resolveStart,
146
133
  detail: resolution.error ?? `via ${resolution.resolverType}`,
147
134
  });
135
+ // ── Step 5: Cache after successful resolution ────────────────────────────
136
+ // Only cache when resolution succeeded — a failed resolution (network error,
137
+ // auth failure, bad params) must not poison the cache. A cached failed match
138
+ // would cause every subsequent cache hit to attempt the same failing resolution
139
+ // until TTL expires.
140
+ if (this.cache && resolution.success && matchResult.capability
141
+ && matchResult.capability.privacy.level === 'public') {
142
+ const queryKey = (0, cache_1.normalizeQuery)(query);
143
+ await this.cache.set(queryKey, matchResult);
144
+ }
148
145
  // ── Step 6: Build reasoning array ────────────────────────────────────────
149
146
  const reasoning = [];
150
147
  if (matchResult.candidates.length) {
@@ -215,6 +212,41 @@ class CapmanEngine {
215
212
  if (this.cache)
216
213
  await this.cache.clear();
217
214
  }
215
+ checkManifestVersion(manifest) {
216
+ if (!manifest.version)
217
+ return;
218
+ const SEMVER_RE = /^\d+\.\d+\.\d+$/;
219
+ if (SEMVER_RE.test(manifest.version) && SEMVER_RE.test(version_1.VERSION)) {
220
+ const [mMaj, mMin] = manifest.version.split('.').map(Number);
221
+ const [eMaj, eMin] = version_1.VERSION.split('.').map(Number);
222
+ if (mMaj !== eMaj || mMin !== eMin) {
223
+ console.warn(`[capman] Manifest version "${manifest.version}" was generated with a ` +
224
+ `different engine version than "${version_1.VERSION}". This is usually fine across patch versions. ` +
225
+ `If you experience unexpected matching issues, regenerate with: npx capman generate`);
226
+ }
227
+ }
228
+ else if (manifest.version !== version_1.VERSION) {
229
+ console.warn(`[capman] Manifest version "${manifest.version}" could not be compared ` +
230
+ `to engine version "${version_1.VERSION}" — version strings are not valid semver.`);
231
+ }
232
+ }
233
+ /**
234
+ * Replaces the active manifest without creating a new engine instance.
235
+ * Useful for hot-reloading manifests in long-running servers without
236
+ * losing cache, learning history, or rate limiter state.
237
+ *
238
+ * Note: clears the cache automatically — cached results from the old
239
+ * manifest are no longer valid after the manifest changes.
240
+ *
241
+ * @example
242
+ * const newManifest = generate(updatedConfig)
243
+ * await engine.loadManifest(newManifest)
244
+ */
245
+ async loadManifest(manifest) {
246
+ this.checkManifestVersion(manifest);
247
+ this.manifest = manifest;
248
+ await this.clearCache();
249
+ }
218
250
  /**
219
251
  * Explain what would happen for a query — without executing it.
220
252
  * Shows matched capability, all candidate scores with reasoning,
@@ -242,6 +274,12 @@ class CapmanEngine {
242
274
  const start = Date.now();
243
275
  // ── Match — shared with ask() via _runMatch() ─────────────────────────────
244
276
  let { matchResult, resolvedVia: _resolvedVia } = await this._runMatch(query);
277
+ // explain() never reads from cache — it always runs a fresh match.
278
+ // This assertion catches any future refactor that accidentally adds
279
+ // cache reads to _runMatch() when called from explain().
280
+ if (_resolvedVia === 'cache') {
281
+ throw new Error('Invariant violation: explain() must never resolve via cache');
282
+ }
245
283
  let resolvedVia = _resolvedVia;
246
284
  // ── Apply learning boost (same as ask()) ─────────────────────────────────
247
285
  matchResult = await this.applyBoostToMatchResult(query, matchResult);
@@ -441,6 +479,11 @@ class CapmanEngine {
441
479
  async _runMatch(query, steps) {
442
480
  let matchResult;
443
481
  let resolvedVia = 'keyword';
482
+ // Fuzzy options — never applied in cheap mode
483
+ const fuzzyOpts = {
484
+ fuzzyMatch: this.fuzzyMatch,
485
+ fuzzyThreshold: this.fuzzyThreshold,
486
+ };
444
487
  switch (this.mode) {
445
488
  case 'cheap': {
446
489
  const t = Date.now();
@@ -454,7 +497,7 @@ class CapmanEngine {
454
497
  if (skipReason) {
455
498
  logger_1.logger.warn(`LLM skipped — ${skipReason} — falling back to keyword`);
456
499
  const t = Date.now();
457
- matchResult = (0, matcher_1.match)(query, this.manifest);
500
+ matchResult = (0, matcher_1.match)(query, this.manifest, fuzzyOpts);
458
501
  steps?.push({ type: 'keyword_match', status: 'pass', durationMs: Date.now() - t, detail: `llm skipped: ${skipReason}` });
459
502
  }
460
503
  else {
@@ -464,7 +507,7 @@ class CapmanEngine {
464
507
  this.recordLLMSuccess();
465
508
  resolvedVia = 'llm';
466
509
  // Merge keyword scores into LLM candidates so boost has real signal for alternatives
467
- const kwResult = (0, matcher_1.match)(query, this.manifest);
510
+ const kwResult = (0, matcher_1.match)(query, this.manifest, fuzzyOpts);
468
511
  matchResult = {
469
512
  ...matchResult,
470
513
  candidates: matchResult.candidates.map(c => ({
@@ -482,7 +525,7 @@ class CapmanEngine {
482
525
  this.recordLLMFailure();
483
526
  logger_1.logger.warn(`LLM call failed — falling back to keyword: ${err instanceof Error ? err.message : String(err)}`);
484
527
  const t2 = Date.now();
485
- matchResult = (0, matcher_1.match)(query, this.manifest);
528
+ matchResult = (0, matcher_1.match)(query, this.manifest, fuzzyOpts);
486
529
  steps?.push({ type: 'llm_match', status: 'fail', durationMs: Date.now() - t, detail: String(err) });
487
530
  steps?.push({ type: 'keyword_match', status: 'pass', durationMs: Date.now() - t2, detail: 'fallback after llm failure' });
488
531
  }
@@ -491,7 +534,7 @@ class CapmanEngine {
491
534
  else {
492
535
  logger_1.logger.warn('accurate mode requires llm — falling back to keyword');
493
536
  const t = Date.now();
494
- matchResult = (0, matcher_1.match)(query, this.manifest);
537
+ matchResult = (0, matcher_1.match)(query, this.manifest, fuzzyOpts);
495
538
  steps?.push({ type: 'keyword_match', status: 'pass', durationMs: Date.now() - t, detail: 'llm not provided, used keyword' });
496
539
  }
497
540
  break;
@@ -499,7 +542,7 @@ class CapmanEngine {
499
542
  case 'balanced':
500
543
  default: {
501
544
  const t1 = Date.now();
502
- const keywordResult = (0, matcher_1.match)(query, this.manifest);
545
+ const keywordResult = (0, matcher_1.match)(query, this.manifest, fuzzyOpts);
503
546
  steps?.push({ type: 'keyword_match', status: 'pass', durationMs: Date.now() - t1, detail: `confidence: ${keywordResult.confidence}%` });
504
547
  if (keywordResult.confidence >= this.threshold || !this.llm) {
505
548
  matchResult = keywordResult;
@@ -512,7 +555,8 @@ class CapmanEngine {
512
555
  matchResult = keywordResult;
513
556
  }
514
557
  else {
515
- logger_1.logger.info(`Low confidence (${keywordResult.confidence}%) — escalating to LLM`);
558
+ logger_1.logger.info(`Low keyword confidence (${keywordResult.confidence}%) — escalating to LLM`);
559
+ logger_1.logger.debug(`Query escalated to LLM: "${query}"`);
516
560
  const t2 = Date.now();
517
561
  try {
518
562
  matchResult = await (0, matcher_1.matchWithLLM)(query, this.manifest, { llm: this.llm });