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.
- package/CHANGELOG.md +43 -0
- package/CODEBASE.md +15 -9
- package/bin/lib/cmd-explain.js +2 -2
- package/bin/lib/cmd-run.js +2 -2
- package/bin/lib/shared.js +8 -2
- package/dist/cjs/cache.d.ts +2 -1
- package/dist/cjs/cache.d.ts.map +1 -1
- package/dist/cjs/cache.js +11 -6
- package/dist/cjs/cache.js.map +1 -1
- package/dist/cjs/engine.d.ts +30 -0
- package/dist/cjs/engine.d.ts.map +1 -1
- package/dist/cjs/engine.js +69 -25
- package/dist/cjs/engine.js.map +1 -1
- package/dist/cjs/generator.d.ts.map +1 -1
- package/dist/cjs/generator.js +16 -1
- package/dist/cjs/generator.js.map +1 -1
- package/dist/cjs/learning.d.ts +20 -10
- package/dist/cjs/learning.d.ts.map +1 -1
- package/dist/cjs/learning.js +146 -129
- package/dist/cjs/learning.js.map +1 -1
- package/dist/cjs/matcher.d.ts +5 -2
- package/dist/cjs/matcher.d.ts.map +1 -1
- package/dist/cjs/matcher.js +73 -10
- package/dist/cjs/matcher.js.map +1 -1
- package/dist/cjs/parser.js +8 -2
- package/dist/cjs/parser.js.map +1 -1
- package/dist/cjs/resolver.d.ts +7 -0
- package/dist/cjs/resolver.d.ts.map +1 -1
- package/dist/cjs/resolver.js +47 -23
- package/dist/cjs/resolver.js.map +1 -1
- package/dist/cjs/schema.d.ts +93 -1
- package/dist/cjs/schema.d.ts.map +1 -1
- package/dist/cjs/schema.js +5 -2
- package/dist/cjs/schema.js.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/cache.d.ts +2 -1
- package/dist/esm/cache.js +11 -6
- package/dist/esm/engine.d.ts +30 -0
- package/dist/esm/engine.js +69 -25
- package/dist/esm/generator.js +16 -1
- package/dist/esm/learning.d.ts +20 -10
- package/dist/esm/learning.js +146 -129
- package/dist/esm/matcher.d.ts +5 -2
- package/dist/esm/matcher.js +70 -10
- package/dist/esm/parser.js +8 -2
- package/dist/esm/resolver.d.ts +7 -0
- package/dist/esm/resolver.js +47 -23
- package/dist/esm/schema.d.ts +93 -1
- package/dist/esm/schema.js +5 -2
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- 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
|
|
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` —
|
|
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` —
|
|
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` —
|
|
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
|
package/bin/lib/cmd-explain.js
CHANGED
|
@@ -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) {
|
package/bin/lib/cmd-run.js
CHANGED
|
@@ -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
|
-
|
|
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 }
|
package/dist/cjs/cache.d.ts
CHANGED
|
@@ -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
|
|
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>;
|
package/dist/cjs/cache.d.ts.map
CHANGED
|
@@ -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,
|
|
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.
|
|
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
|
-
|
|
121
|
-
if (this.
|
|
122
|
-
|
|
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
|
-
|
|
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)}`);
|
package/dist/cjs/cache.js.map
CHANGED
|
@@ -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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"}
|
package/dist/cjs/engine.d.ts
CHANGED
|
@@ -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,
|
package/dist/cjs/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAa,aAAa,EAA4F,MAAM,SAAS,CAAA;AACvM,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,YAAY,CAAA;AAK7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAMxC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,QAAQ,EAAE,QAAQ,CAAA;IAClB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,kDAAkD;IAClD,GAAG,CAAC,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAA;IAC9B,0FAA0F;IAC1F,KAAK,CAAC,EAAE,UAAU,GAAG,KAAK,CAAA;IAC1B,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,aAAa,GAAG,KAAK,CAAA;IAChC,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,mDAAmD;IACnD,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAE7B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAA;IAEnC;;;OAGG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/engine.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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:
|
|
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 });
|