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/dist/cjs/schema.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
export declare const CapmanConfigSchema: z.ZodObject<{
|
|
2
|
+
export declare const CapmanConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
3
3
|
app: z.ZodString;
|
|
4
4
|
baseUrl: z.ZodOptional<z.ZodString>;
|
|
5
5
|
capabilities: z.ZodEffects<z.ZodArray<z.ZodObject<{
|
|
@@ -405,6 +405,98 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
405
405
|
examples?: string[] | undefined;
|
|
406
406
|
}[];
|
|
407
407
|
baseUrl?: string | undefined;
|
|
408
|
+
}>, {
|
|
409
|
+
app: string;
|
|
410
|
+
capabilities: {
|
|
411
|
+
name: string;
|
|
412
|
+
id: string;
|
|
413
|
+
params: {
|
|
414
|
+
name: string;
|
|
415
|
+
required: boolean;
|
|
416
|
+
description: string;
|
|
417
|
+
source: "user_query" | "session" | "context" | "static";
|
|
418
|
+
default?: string | number | boolean | undefined;
|
|
419
|
+
}[];
|
|
420
|
+
description: string;
|
|
421
|
+
returns: string[];
|
|
422
|
+
resolver: {
|
|
423
|
+
type: "api";
|
|
424
|
+
endpoints: {
|
|
425
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
426
|
+
path: string;
|
|
427
|
+
params?: string[] | undefined;
|
|
428
|
+
}[];
|
|
429
|
+
} | {
|
|
430
|
+
type: "nav";
|
|
431
|
+
destination: string;
|
|
432
|
+
hint?: string | undefined;
|
|
433
|
+
} | {
|
|
434
|
+
type: "hybrid";
|
|
435
|
+
api: {
|
|
436
|
+
endpoints: {
|
|
437
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
438
|
+
path: string;
|
|
439
|
+
params?: string[] | undefined;
|
|
440
|
+
}[];
|
|
441
|
+
};
|
|
442
|
+
nav: {
|
|
443
|
+
destination: string;
|
|
444
|
+
hint?: string | undefined;
|
|
445
|
+
};
|
|
446
|
+
};
|
|
447
|
+
privacy: {
|
|
448
|
+
level: "public" | "user_owned" | "admin";
|
|
449
|
+
note?: string | undefined;
|
|
450
|
+
};
|
|
451
|
+
examples?: string[] | undefined;
|
|
452
|
+
}[];
|
|
453
|
+
baseUrl?: string | undefined;
|
|
454
|
+
}, {
|
|
455
|
+
app: string;
|
|
456
|
+
capabilities: {
|
|
457
|
+
name: string;
|
|
458
|
+
id: string;
|
|
459
|
+
params: {
|
|
460
|
+
name: string;
|
|
461
|
+
required: boolean;
|
|
462
|
+
description: string;
|
|
463
|
+
source: "user_query" | "session" | "context" | "static";
|
|
464
|
+
default?: string | number | boolean | undefined;
|
|
465
|
+
}[];
|
|
466
|
+
description: string;
|
|
467
|
+
returns: string[];
|
|
468
|
+
resolver: {
|
|
469
|
+
type: "api";
|
|
470
|
+
endpoints: {
|
|
471
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
472
|
+
path: string;
|
|
473
|
+
params?: string[] | undefined;
|
|
474
|
+
}[];
|
|
475
|
+
} | {
|
|
476
|
+
type: "nav";
|
|
477
|
+
destination: string;
|
|
478
|
+
hint?: string | undefined;
|
|
479
|
+
} | {
|
|
480
|
+
type: "hybrid";
|
|
481
|
+
api: {
|
|
482
|
+
endpoints: {
|
|
483
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
484
|
+
path: string;
|
|
485
|
+
params?: string[] | undefined;
|
|
486
|
+
}[];
|
|
487
|
+
};
|
|
488
|
+
nav: {
|
|
489
|
+
destination: string;
|
|
490
|
+
hint?: string | undefined;
|
|
491
|
+
};
|
|
492
|
+
};
|
|
493
|
+
privacy: {
|
|
494
|
+
level: "public" | "user_owned" | "admin";
|
|
495
|
+
note?: string | undefined;
|
|
496
|
+
};
|
|
497
|
+
examples?: string[] | undefined;
|
|
498
|
+
}[];
|
|
499
|
+
baseUrl?: string | undefined;
|
|
408
500
|
}>;
|
|
409
501
|
export declare const ManifestSchema: z.ZodObject<{
|
|
410
502
|
version: z.ZodString;
|
package/dist/cjs/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA2EvB,eAAO,MAAM,kBAAkB
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA2EvB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiB9B,CAAA;AAID,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKzB,CAAA;AAIF,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,mBAAmB,CAQnE;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,mBAAmB,CAQvE"}
|
package/dist/cjs/schema.js
CHANGED
|
@@ -67,11 +67,14 @@ const CapabilitySchema = zod_1.z.object({
|
|
|
67
67
|
// ─── Config Schema ────────────────────────────────────────────────────────────
|
|
68
68
|
exports.CapmanConfigSchema = zod_1.z.object({
|
|
69
69
|
app: zod_1.z.string().min(1, 'app name is required'),
|
|
70
|
-
baseUrl: zod_1.z.string().url(
|
|
70
|
+
baseUrl: zod_1.z.string().url().optional(),
|
|
71
71
|
capabilities: zod_1.z.array(CapabilitySchema)
|
|
72
72
|
.min(1, 'at least one capability is required')
|
|
73
73
|
.refine(caps => new Set(caps.map(c => c.id)).size === caps.length, 'capability ids must be unique'),
|
|
74
|
-
})
|
|
74
|
+
}).refine(cfg => {
|
|
75
|
+
const needsBaseUrl = cfg.capabilities.some(c => c.resolver.type === 'api' || c.resolver.type === 'hybrid');
|
|
76
|
+
return !needsBaseUrl || !!cfg.baseUrl;
|
|
77
|
+
}, { message: 'baseUrl is required when any capability uses an api or hybrid resolver' });
|
|
75
78
|
// ─── Manifest Schema ──────────────────────────────────────────────────────────
|
|
76
79
|
exports.ManifestSchema = zod_1.z.object({
|
|
77
80
|
version: zod_1.z.string(),
|
package/dist/cjs/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/schema.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.5.
|
|
1
|
+
export declare const VERSION = "0.5.3";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cjs/version.js
CHANGED
package/dist/esm/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/esm/cache.js
CHANGED
|
@@ -66,7 +66,7 @@ const FILE_CACHE_MAX = 2048;
|
|
|
66
66
|
export class FileCache {
|
|
67
67
|
constructor(filePath = '.capman/cache.json') {
|
|
68
68
|
this.store = new Map();
|
|
69
|
-
this.
|
|
69
|
+
this.loadPromise = null;
|
|
70
70
|
this.saveQueue = Promise.resolve();
|
|
71
71
|
const cwd = process.cwd();
|
|
72
72
|
const resolved = path.resolve(cwd, filePath);
|
|
@@ -78,9 +78,13 @@ export class FileCache {
|
|
|
78
78
|
this.filePath = resolved;
|
|
79
79
|
logger.info(`FileCache initialized — writing to: ${this.filePath}`);
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
if (this.
|
|
83
|
-
|
|
81
|
+
load() {
|
|
82
|
+
if (!this.loadPromise) {
|
|
83
|
+
this.loadPromise = this._doLoad();
|
|
84
|
+
}
|
|
85
|
+
return this.loadPromise;
|
|
86
|
+
}
|
|
87
|
+
async _doLoad() {
|
|
84
88
|
try {
|
|
85
89
|
const raw = await fs.promises.readFile(this.filePath, 'utf-8');
|
|
86
90
|
const parsed = JSON.parse(raw);
|
|
@@ -102,7 +106,6 @@ export class FileCache {
|
|
|
102
106
|
catch {
|
|
103
107
|
// File doesn't exist yet — start fresh
|
|
104
108
|
}
|
|
105
|
-
this.loaded = true;
|
|
106
109
|
}
|
|
107
110
|
save() {
|
|
108
111
|
this.saveQueue = this.saveQueue.then(() => this._doSave());
|
|
@@ -112,7 +115,9 @@ export class FileCache {
|
|
|
112
115
|
try {
|
|
113
116
|
const dir = path.dirname(this.filePath);
|
|
114
117
|
await fs.promises.mkdir(dir, { recursive: true });
|
|
115
|
-
|
|
118
|
+
const tmp = `${this.filePath}.tmp`;
|
|
119
|
+
await fs.promises.writeFile(tmp, JSON.stringify(Object.fromEntries(this.store), null, 2));
|
|
120
|
+
await fs.promises.rename(tmp, this.filePath);
|
|
116
121
|
}
|
|
117
122
|
catch (err) {
|
|
118
123
|
logger.warn(`Failed to save file cache to ${this.filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
package/dist/esm/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/esm/engine.js
CHANGED
|
@@ -25,6 +25,8 @@ export class CapmanEngine {
|
|
|
25
25
|
this.llmCooldownMs = options.llmCooldownMs ?? 0;
|
|
26
26
|
this.llmCircuitBreakerThreshold = options.llmCircuitBreakerThreshold ?? 3;
|
|
27
27
|
this.llmCircuitBreakerResetMs = options.llmCircuitBreakerResetMs ?? 60_000;
|
|
28
|
+
this.fuzzyMatch = options.fuzzyMatch ?? false;
|
|
29
|
+
this.fuzzyThreshold = options.fuzzyThreshold ?? 0.4;
|
|
28
30
|
// Cache — default MemoryCache (no filesystem writes), or disabled with false
|
|
29
31
|
// Use FileCache or ComboCache explicitly for persistence across restarts
|
|
30
32
|
this.cache = options.cache === false
|
|
@@ -37,15 +39,7 @@ export class CapmanEngine {
|
|
|
37
39
|
: (options.learning ?? new MemoryLearningStore());
|
|
38
40
|
logger.info(`CapmanEngine initialized — mode: ${this.mode}, cache: ${this.cache ? 'enabled' : 'disabled'}, learning: ${this.learning ? 'enabled' : 'disabled'}`);
|
|
39
41
|
// ── Manifest version compatibility check ─────────────────────────────────
|
|
40
|
-
|
|
41
|
-
const [mMaj, mMin] = options.manifest.version.split('.').map(Number);
|
|
42
|
-
const [eMaj, eMin] = VERSION.split('.').map(Number);
|
|
43
|
-
if (mMaj !== eMaj || mMin !== eMin) {
|
|
44
|
-
console.warn(`[capman] Manifest version "${options.manifest.version}" was generated with a ` +
|
|
45
|
-
`different engine version than "${VERSION}". This is usually fine across patch versions. ` +
|
|
46
|
-
`If you experience unexpected matching issues, regenerate with: npx capman generate`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
42
|
+
this.checkManifestVersion(options.manifest);
|
|
49
43
|
}
|
|
50
44
|
/**
|
|
51
45
|
* Ask the engine a natural language query.
|
|
@@ -74,7 +68,8 @@ export class CapmanEngine {
|
|
|
74
68
|
const cached = await this.cache.get(queryKey, this.cacheTtlMs ?? undefined);
|
|
75
69
|
if (cached) {
|
|
76
70
|
steps.push({ type: 'cache_check', status: 'hit', durationMs: Date.now() - cacheStart, detail: 'Served from cache' });
|
|
77
|
-
logger.info(`Cache hit
|
|
71
|
+
logger.info(`Cache hit — capability: "${cached.result.capability?.id ?? 'none'}"`);
|
|
72
|
+
logger.debug(`Cache hit for query: "${query}"`);
|
|
78
73
|
// Re-extract params from the current query — never re-use cached params.
|
|
79
74
|
// Cached params belong to the original query (potentially from a different user).
|
|
80
75
|
// e.g. User A: "show orders for john" → cached with { customer: 'john' }
|
|
@@ -125,15 +120,7 @@ export class CapmanEngine {
|
|
|
125
120
|
detail: `level: ${privacyLevel}`,
|
|
126
121
|
});
|
|
127
122
|
}
|
|
128
|
-
// ── Step 4:
|
|
129
|
-
// Non-public capabilities are never cached — prevents auth bypass where
|
|
130
|
-
// User A's cached match is served to User B without privacy enforcement.
|
|
131
|
-
if (this.cache && matchResult.capability
|
|
132
|
-
&& matchResult.capability.privacy.level === 'public') {
|
|
133
|
-
const queryKey = normalizeQuery(query);
|
|
134
|
-
await this.cache.set(queryKey, matchResult);
|
|
135
|
-
}
|
|
136
|
-
// ── Step 5: Resolve ──────────────────────────────────────────────────────
|
|
123
|
+
// ── Step 4: Resolve ──────────────────────────────────────────────────────
|
|
137
124
|
const resolveStart = Date.now();
|
|
138
125
|
const resolution = await _resolve(matchResult, matchResult.extractedParams, this.resolveOptions(overrides));
|
|
139
126
|
steps.push({
|
|
@@ -142,6 +129,16 @@ export class CapmanEngine {
|
|
|
142
129
|
durationMs: Date.now() - resolveStart,
|
|
143
130
|
detail: resolution.error ?? `via ${resolution.resolverType}`,
|
|
144
131
|
});
|
|
132
|
+
// ── Step 5: Cache after successful resolution ────────────────────────────
|
|
133
|
+
// Only cache when resolution succeeded — a failed resolution (network error,
|
|
134
|
+
// auth failure, bad params) must not poison the cache. A cached failed match
|
|
135
|
+
// would cause every subsequent cache hit to attempt the same failing resolution
|
|
136
|
+
// until TTL expires.
|
|
137
|
+
if (this.cache && resolution.success && matchResult.capability
|
|
138
|
+
&& matchResult.capability.privacy.level === 'public') {
|
|
139
|
+
const queryKey = normalizeQuery(query);
|
|
140
|
+
await this.cache.set(queryKey, matchResult);
|
|
141
|
+
}
|
|
145
142
|
// ── Step 6: Build reasoning array ────────────────────────────────────────
|
|
146
143
|
const reasoning = [];
|
|
147
144
|
if (matchResult.candidates.length) {
|
|
@@ -212,6 +209,41 @@ export class CapmanEngine {
|
|
|
212
209
|
if (this.cache)
|
|
213
210
|
await this.cache.clear();
|
|
214
211
|
}
|
|
212
|
+
checkManifestVersion(manifest) {
|
|
213
|
+
if (!manifest.version)
|
|
214
|
+
return;
|
|
215
|
+
const SEMVER_RE = /^\d+\.\d+\.\d+$/;
|
|
216
|
+
if (SEMVER_RE.test(manifest.version) && SEMVER_RE.test(VERSION)) {
|
|
217
|
+
const [mMaj, mMin] = manifest.version.split('.').map(Number);
|
|
218
|
+
const [eMaj, eMin] = VERSION.split('.').map(Number);
|
|
219
|
+
if (mMaj !== eMaj || mMin !== eMin) {
|
|
220
|
+
console.warn(`[capman] Manifest version "${manifest.version}" was generated with a ` +
|
|
221
|
+
`different engine version than "${VERSION}". This is usually fine across patch versions. ` +
|
|
222
|
+
`If you experience unexpected matching issues, regenerate with: npx capman generate`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else if (manifest.version !== VERSION) {
|
|
226
|
+
console.warn(`[capman] Manifest version "${manifest.version}" could not be compared ` +
|
|
227
|
+
`to engine version "${VERSION}" — version strings are not valid semver.`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Replaces the active manifest without creating a new engine instance.
|
|
232
|
+
* Useful for hot-reloading manifests in long-running servers without
|
|
233
|
+
* losing cache, learning history, or rate limiter state.
|
|
234
|
+
*
|
|
235
|
+
* Note: clears the cache automatically — cached results from the old
|
|
236
|
+
* manifest are no longer valid after the manifest changes.
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* const newManifest = generate(updatedConfig)
|
|
240
|
+
* await engine.loadManifest(newManifest)
|
|
241
|
+
*/
|
|
242
|
+
async loadManifest(manifest) {
|
|
243
|
+
this.checkManifestVersion(manifest);
|
|
244
|
+
this.manifest = manifest;
|
|
245
|
+
await this.clearCache();
|
|
246
|
+
}
|
|
215
247
|
/**
|
|
216
248
|
* Explain what would happen for a query — without executing it.
|
|
217
249
|
* Shows matched capability, all candidate scores with reasoning,
|
|
@@ -239,6 +271,12 @@ export class CapmanEngine {
|
|
|
239
271
|
const start = Date.now();
|
|
240
272
|
// ── Match — shared with ask() via _runMatch() ─────────────────────────────
|
|
241
273
|
let { matchResult, resolvedVia: _resolvedVia } = await this._runMatch(query);
|
|
274
|
+
// explain() never reads from cache — it always runs a fresh match.
|
|
275
|
+
// This assertion catches any future refactor that accidentally adds
|
|
276
|
+
// cache reads to _runMatch() when called from explain().
|
|
277
|
+
if (_resolvedVia === 'cache') {
|
|
278
|
+
throw new Error('Invariant violation: explain() must never resolve via cache');
|
|
279
|
+
}
|
|
242
280
|
let resolvedVia = _resolvedVia;
|
|
243
281
|
// ── Apply learning boost (same as ask()) ─────────────────────────────────
|
|
244
282
|
matchResult = await this.applyBoostToMatchResult(query, matchResult);
|
|
@@ -438,6 +476,11 @@ export class CapmanEngine {
|
|
|
438
476
|
async _runMatch(query, steps) {
|
|
439
477
|
let matchResult;
|
|
440
478
|
let resolvedVia = 'keyword';
|
|
479
|
+
// Fuzzy options — never applied in cheap mode
|
|
480
|
+
const fuzzyOpts = {
|
|
481
|
+
fuzzyMatch: this.fuzzyMatch,
|
|
482
|
+
fuzzyThreshold: this.fuzzyThreshold,
|
|
483
|
+
};
|
|
441
484
|
switch (this.mode) {
|
|
442
485
|
case 'cheap': {
|
|
443
486
|
const t = Date.now();
|
|
@@ -451,7 +494,7 @@ export class CapmanEngine {
|
|
|
451
494
|
if (skipReason) {
|
|
452
495
|
logger.warn(`LLM skipped — ${skipReason} — falling back to keyword`);
|
|
453
496
|
const t = Date.now();
|
|
454
|
-
matchResult = _match(query, this.manifest);
|
|
497
|
+
matchResult = _match(query, this.manifest, fuzzyOpts);
|
|
455
498
|
steps?.push({ type: 'keyword_match', status: 'pass', durationMs: Date.now() - t, detail: `llm skipped: ${skipReason}` });
|
|
456
499
|
}
|
|
457
500
|
else {
|
|
@@ -461,7 +504,7 @@ export class CapmanEngine {
|
|
|
461
504
|
this.recordLLMSuccess();
|
|
462
505
|
resolvedVia = 'llm';
|
|
463
506
|
// Merge keyword scores into LLM candidates so boost has real signal for alternatives
|
|
464
|
-
const kwResult = _match(query, this.manifest);
|
|
507
|
+
const kwResult = _match(query, this.manifest, fuzzyOpts);
|
|
465
508
|
matchResult = {
|
|
466
509
|
...matchResult,
|
|
467
510
|
candidates: matchResult.candidates.map(c => ({
|
|
@@ -479,7 +522,7 @@ export class CapmanEngine {
|
|
|
479
522
|
this.recordLLMFailure();
|
|
480
523
|
logger.warn(`LLM call failed — falling back to keyword: ${err instanceof Error ? err.message : String(err)}`);
|
|
481
524
|
const t2 = Date.now();
|
|
482
|
-
matchResult = _match(query, this.manifest);
|
|
525
|
+
matchResult = _match(query, this.manifest, fuzzyOpts);
|
|
483
526
|
steps?.push({ type: 'llm_match', status: 'fail', durationMs: Date.now() - t, detail: String(err) });
|
|
484
527
|
steps?.push({ type: 'keyword_match', status: 'pass', durationMs: Date.now() - t2, detail: 'fallback after llm failure' });
|
|
485
528
|
}
|
|
@@ -488,7 +531,7 @@ export class CapmanEngine {
|
|
|
488
531
|
else {
|
|
489
532
|
logger.warn('accurate mode requires llm — falling back to keyword');
|
|
490
533
|
const t = Date.now();
|
|
491
|
-
matchResult = _match(query, this.manifest);
|
|
534
|
+
matchResult = _match(query, this.manifest, fuzzyOpts);
|
|
492
535
|
steps?.push({ type: 'keyword_match', status: 'pass', durationMs: Date.now() - t, detail: 'llm not provided, used keyword' });
|
|
493
536
|
}
|
|
494
537
|
break;
|
|
@@ -496,7 +539,7 @@ export class CapmanEngine {
|
|
|
496
539
|
case 'balanced':
|
|
497
540
|
default: {
|
|
498
541
|
const t1 = Date.now();
|
|
499
|
-
const keywordResult = _match(query, this.manifest);
|
|
542
|
+
const keywordResult = _match(query, this.manifest, fuzzyOpts);
|
|
500
543
|
steps?.push({ type: 'keyword_match', status: 'pass', durationMs: Date.now() - t1, detail: `confidence: ${keywordResult.confidence}%` });
|
|
501
544
|
if (keywordResult.confidence >= this.threshold || !this.llm) {
|
|
502
545
|
matchResult = keywordResult;
|
|
@@ -509,7 +552,8 @@ export class CapmanEngine {
|
|
|
509
552
|
matchResult = keywordResult;
|
|
510
553
|
}
|
|
511
554
|
else {
|
|
512
|
-
logger.info(`Low confidence (${keywordResult.confidence}%) — escalating to LLM`);
|
|
555
|
+
logger.info(`Low keyword confidence (${keywordResult.confidence}%) — escalating to LLM`);
|
|
556
|
+
logger.debug(`Query escalated to LLM: "${query}"`);
|
|
513
557
|
const t2 = Date.now();
|
|
514
558
|
try {
|
|
515
559
|
matchResult = await _matchWithLLM(query, this.manifest, { llm: this.llm });
|
package/dist/esm/generator.js
CHANGED
|
@@ -37,8 +37,23 @@ export function loadConfig(configPath) {
|
|
|
37
37
|
raw = mod.default ?? mod;
|
|
38
38
|
}
|
|
39
39
|
catch (err) {
|
|
40
|
+
const code = err.code;
|
|
41
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
42
|
+
// ERR_REQUIRE_ESM — file is an ES module (Node v12–v21)
|
|
43
|
+
// On Node v22+, the error message changed but code remains ERR_REQUIRE_ESM
|
|
44
|
+
// for .mjs files; .js files in ESM packages may show a different message.
|
|
45
|
+
const isESM = code === 'ERR_REQUIRE_ESM' ||
|
|
46
|
+
message.includes('require() of ES Module') ||
|
|
47
|
+
message.includes('must use import to load ES Module');
|
|
48
|
+
if (isESM) {
|
|
49
|
+
throw new Error(`Config file "${resolved}" is an ES module but capman requires CommonJS.\n` +
|
|
50
|
+
`Solutions:\n` +
|
|
51
|
+
` 1. Rename to capman.config.cjs\n` +
|
|
52
|
+
` 2. Change to: module.exports = { ... }\n` +
|
|
53
|
+
` 3. Remove "type": "module" from your package.json`);
|
|
54
|
+
}
|
|
40
55
|
throw new Error(`Failed to load config at ${resolved}:\n` +
|
|
41
|
-
` ${
|
|
56
|
+
` ${message}\n\n` +
|
|
42
57
|
`Check your config file for syntax errors.`);
|
|
43
58
|
}
|
|
44
59
|
// Catch invalid config structure
|
package/dist/esm/learning.d.ts
CHANGED
|
@@ -28,19 +28,31 @@ export interface LearningStore {
|
|
|
28
28
|
}>>;
|
|
29
29
|
/** Returns the live keyword index without rebuilding — O(1) */
|
|
30
30
|
getIndex(): Promise<Record<string, Record<string, number>>>;
|
|
31
|
+
/**
|
|
32
|
+
* Removes this store from the exit flush registry and flushes any pending data.
|
|
33
|
+
* Call when the store is no longer needed to prevent memory leaks.
|
|
34
|
+
* Must be awaited — final flush is async.
|
|
35
|
+
*/
|
|
36
|
+
destroy(): Promise<void>;
|
|
31
37
|
}
|
|
32
38
|
export declare class FileLearningStore implements LearningStore {
|
|
33
39
|
private filePath;
|
|
34
40
|
private entries;
|
|
35
|
-
private
|
|
41
|
+
private loadPromise;
|
|
36
42
|
private saveQueue;
|
|
37
|
-
private
|
|
38
|
-
private
|
|
43
|
+
private learningIndex;
|
|
44
|
+
private dirty;
|
|
45
|
+
private saveTimer;
|
|
39
46
|
constructor(filePath?: string);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
flushSync(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Removes this store from the exit flush registry and cancels any pending save timer.
|
|
50
|
+
* Call when the store is no longer needed to prevent memory leaks in long-running servers.
|
|
51
|
+
*/
|
|
52
|
+
destroy(): Promise<void>;
|
|
43
53
|
private load;
|
|
54
|
+
private _doLoad;
|
|
55
|
+
private scheduleSave;
|
|
44
56
|
private save;
|
|
45
57
|
private _doSave;
|
|
46
58
|
record(entry: LearningEntry): Promise<void>;
|
|
@@ -54,16 +66,14 @@ export declare class FileLearningStore implements LearningStore {
|
|
|
54
66
|
}
|
|
55
67
|
export declare class MemoryLearningStore implements LearningStore {
|
|
56
68
|
private entries;
|
|
57
|
-
private
|
|
58
|
-
private statsCounter;
|
|
69
|
+
private learningIndex;
|
|
59
70
|
record(entry: LearningEntry): Promise<void>;
|
|
60
71
|
getStats(): Promise<KeywordStats>;
|
|
61
72
|
getIndex(): Promise<Record<string, Record<string, number>>>;
|
|
62
|
-
private updateIndex;
|
|
63
|
-
private subtractFromIndex;
|
|
64
73
|
getTopCapabilities(limit?: number): Promise<Array<{
|
|
65
74
|
id: string;
|
|
66
75
|
hits: number;
|
|
67
76
|
}>>;
|
|
68
77
|
clear(): Promise<void>;
|
|
78
|
+
destroy(): Promise<void>;
|
|
69
79
|
}
|