@principles/core 1.174.0 → 1.175.0
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/dist/runtime-v2/__tests__/architecture-regression.test.js +39 -0
- package/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
- package/dist/runtime-v2/detection/__tests__/detection-funnel-policy.test.d.ts +13 -0
- package/dist/runtime-v2/detection/__tests__/detection-funnel-policy.test.d.ts.map +1 -0
- package/dist/runtime-v2/detection/__tests__/detection-funnel-policy.test.js +111 -0
- package/dist/runtime-v2/detection/__tests__/detection-funnel-policy.test.js.map +1 -0
- package/dist/runtime-v2/detection/detection-funnel-policy.d.ts +103 -0
- package/dist/runtime-v2/detection/detection-funnel-policy.d.ts.map +1 -0
- package/dist/runtime-v2/detection/detection-funnel-policy.js +135 -0
- package/dist/runtime-v2/detection/detection-funnel-policy.js.map +1 -0
- package/dist/runtime-v2/detection/index.d.ts +10 -0
- package/dist/runtime-v2/detection/index.d.ts.map +1 -0
- package/dist/runtime-v2/detection/index.js +9 -0
- package/dist/runtime-v2/detection/index.js.map +1 -0
- package/dist/runtime-v2/evidence-triage/__tests__/observation-resolver.test.d.ts +12 -0
- package/dist/runtime-v2/evidence-triage/__tests__/observation-resolver.test.d.ts.map +1 -0
- package/dist/runtime-v2/evidence-triage/__tests__/observation-resolver.test.js +137 -0
- package/dist/runtime-v2/evidence-triage/__tests__/observation-resolver.test.js.map +1 -0
- package/dist/runtime-v2/evidence-triage/__tests__/triage-policy.test.js +104 -1
- package/dist/runtime-v2/evidence-triage/__tests__/triage-policy.test.js.map +1 -1
- package/dist/runtime-v2/evidence-triage/index.d.ts +3 -0
- package/dist/runtime-v2/evidence-triage/index.d.ts.map +1 -1
- package/dist/runtime-v2/evidence-triage/index.js +3 -0
- package/dist/runtime-v2/evidence-triage/index.js.map +1 -1
- package/dist/runtime-v2/evidence-triage/observation-resolver.d.ts +107 -0
- package/dist/runtime-v2/evidence-triage/observation-resolver.d.ts.map +1 -0
- package/dist/runtime-v2/evidence-triage/observation-resolver.js +175 -0
- package/dist/runtime-v2/evidence-triage/observation-resolver.js.map +1 -0
- package/dist/runtime-v2/evidence-triage/triage-policy.d.ts +4 -0
- package/dist/runtime-v2/evidence-triage/triage-policy.d.ts.map +1 -1
- package/dist/runtime-v2/evidence-triage/triage-policy.js +66 -9
- package/dist/runtime-v2/evidence-triage/triage-policy.js.map +1 -1
- package/dist/runtime-v2/evidence-triage/types.d.ts +14 -0
- package/dist/runtime-v2/evidence-triage/types.d.ts.map +1 -1
- package/dist/runtime-v2/evidence-triage/types.js.map +1 -1
- package/dist/runtime-v2/index.d.ts +6 -2
- package/dist/runtime-v2/index.d.ts.map +1 -1
- package/dist/runtime-v2/index.js +3 -1
- package/dist/runtime-v2/index.js.map +1 -1
- package/dist/runtime-v2/pain-gate/__tests__/pain-diagnostic-gate-policy.test.d.ts +15 -0
- package/dist/runtime-v2/pain-gate/__tests__/pain-diagnostic-gate-policy.test.d.ts.map +1 -0
- package/dist/runtime-v2/pain-gate/__tests__/pain-diagnostic-gate-policy.test.js +179 -0
- package/dist/runtime-v2/pain-gate/__tests__/pain-diagnostic-gate-policy.test.js.map +1 -0
- package/dist/runtime-v2/pain-gate/index.d.ts +10 -0
- package/dist/runtime-v2/pain-gate/index.d.ts.map +1 -0
- package/dist/runtime-v2/pain-gate/index.js +9 -0
- package/dist/runtime-v2/pain-gate/index.js.map +1 -0
- package/dist/runtime-v2/pain-gate/pain-diagnostic-gate-policy.d.ts +110 -0
- package/dist/runtime-v2/pain-gate/pain-diagnostic-gate-policy.d.ts.map +1 -0
- package/dist/runtime-v2/pain-gate/pain-diagnostic-gate-policy.js +146 -0
- package/dist/runtime-v2/pain-gate/pain-diagnostic-gate-policy.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detection Funnel Policy Tests — PRI-446
|
|
3
|
+
*
|
|
4
|
+
* Tests the pure core DetectionFunnelCore directly with injected stubs for
|
|
5
|
+
* matcher/hasher/gate (no crypto, no dictionary). The plugin's
|
|
6
|
+
* detection-funnel-characterization.test.ts (10 tests) exercises the same logic
|
|
7
|
+
* through the wired adapter; this file gives core its own direct coverage.
|
|
8
|
+
*
|
|
9
|
+
* ERR checklist:
|
|
10
|
+
* - ERR-025: tests the real DetectionFunnelCore path.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=detection-funnel-policy.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detection-funnel-policy.test.d.ts","sourceRoot":"","sources":["../../../../src/runtime-v2/detection/__tests__/detection-funnel-policy.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detection Funnel Policy Tests — PRI-446
|
|
3
|
+
*
|
|
4
|
+
* Tests the pure core DetectionFunnelCore directly with injected stubs for
|
|
5
|
+
* matcher/hasher/gate (no crypto, no dictionary). The plugin's
|
|
6
|
+
* detection-funnel-characterization.test.ts (10 tests) exercises the same logic
|
|
7
|
+
* through the wired adapter; this file gives core its own direct coverage.
|
|
8
|
+
*
|
|
9
|
+
* ERR checklist:
|
|
10
|
+
* - ERR-025: tests the real DetectionFunnelCore path.
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect } from 'vitest';
|
|
13
|
+
import { DetectionFunnelCore, SimpleLRU, } from '../detection-funnel-policy.js';
|
|
14
|
+
function makeConfig(overrides = {}) {
|
|
15
|
+
return {
|
|
16
|
+
match: () => undefined,
|
|
17
|
+
hash: (t) => `hash:${t}`,
|
|
18
|
+
shouldIgnoreProtocol: () => false,
|
|
19
|
+
...overrides,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
describe('DetectionFunnelCore: L1 exact', () => {
|
|
23
|
+
it('returns l1_exact with ruleId+severity on match', () => {
|
|
24
|
+
const funnel = new DetectionFunnelCore(makeConfig({
|
|
25
|
+
match: (t) => (t === 'i am confused' ? { ruleId: 'P_CONFUSION_EN', severity: 35 } : undefined),
|
|
26
|
+
}));
|
|
27
|
+
const r = funnel.detect('i am confused');
|
|
28
|
+
expect(r).toEqual({ detected: true, severity: 35, ruleId: 'P_CONFUSION_EN', source: 'l1_exact' });
|
|
29
|
+
});
|
|
30
|
+
it('protocol token gate short-circuits before L1', () => {
|
|
31
|
+
const funnel = new DetectionFunnelCore(makeConfig({
|
|
32
|
+
match: () => ({ ruleId: 'X', severity: 10 }),
|
|
33
|
+
shouldIgnoreProtocol: (t) => t === 'HEARTBEAT_OK',
|
|
34
|
+
}));
|
|
35
|
+
const r = funnel.detect('HEARTBEAT_OK');
|
|
36
|
+
expect(r).toEqual({ detected: false, source: 'l1_exact' });
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe('DetectionFunnelCore: L3 async queue', () => {
|
|
40
|
+
it('no match + no cache → l3_async_queued', () => {
|
|
41
|
+
const funnel = new DetectionFunnelCore(makeConfig());
|
|
42
|
+
expect(funnel.detect('unseen')).toEqual({ detected: false, source: 'l3_async_queued' });
|
|
43
|
+
});
|
|
44
|
+
it('flushQueue returns queued texts and clears', () => {
|
|
45
|
+
const funnel = new DetectionFunnelCore(makeConfig());
|
|
46
|
+
funnel.detect('a');
|
|
47
|
+
funnel.detect('b');
|
|
48
|
+
expect(funnel.flushQueue()).toEqual(['a', 'b']);
|
|
49
|
+
expect(funnel.flushQueue()).toEqual([]);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('DetectionFunnelCore: L2 cache', () => {
|
|
53
|
+
it('updateCache then detect returns l2_cache with stored result', () => {
|
|
54
|
+
const funnel = new DetectionFunnelCore(makeConfig());
|
|
55
|
+
funnel.updateCache('text', { detected: true, severity: 72 });
|
|
56
|
+
const r = funnel.detect('text');
|
|
57
|
+
expect(r.source).toBe('l2_cache');
|
|
58
|
+
expect(r).toMatchObject({ detected: true, severity: 72 });
|
|
59
|
+
});
|
|
60
|
+
it('L1 takes precedence over a poisoned L2 cache', () => {
|
|
61
|
+
const funnel = new DetectionFunnelCore(makeConfig({
|
|
62
|
+
match: (t) => (t === 'x' ? { ruleId: 'R', severity: 35 } : undefined),
|
|
63
|
+
}));
|
|
64
|
+
funnel.updateCache('x', { detected: false });
|
|
65
|
+
const r = funnel.detect('x');
|
|
66
|
+
expect(r.source).toBe('l1_exact');
|
|
67
|
+
expect(r.detected).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('DetectionFunnelCore: LRU eviction', () => {
|
|
71
|
+
it('evicts oldest beyond capacity', () => {
|
|
72
|
+
const funnel = new DetectionFunnelCore(makeConfig({ cacheSize: 2 }));
|
|
73
|
+
funnel.updateCache('a', { detected: true });
|
|
74
|
+
funnel.updateCache('b', { detected: true });
|
|
75
|
+
funnel.updateCache('c', { detected: true }); // evicts 'a'
|
|
76
|
+
expect(funnel.detect('a').source).toBe('l3_async_queued');
|
|
77
|
+
expect(funnel.detect('c').source).toBe('l2_cache');
|
|
78
|
+
});
|
|
79
|
+
it('refreshes LRU position on get', () => {
|
|
80
|
+
const funnel = new DetectionFunnelCore(makeConfig({ cacheSize: 2 }));
|
|
81
|
+
funnel.updateCache('a', { detected: true });
|
|
82
|
+
funnel.updateCache('b', { detected: true });
|
|
83
|
+
funnel.detect('a'); // refresh 'a' -> now 'b' is oldest
|
|
84
|
+
funnel.updateCache('c', { detected: true }); // evicts 'b'
|
|
85
|
+
expect(funnel.detect('a').source).toBe('l2_cache');
|
|
86
|
+
expect(funnel.detect('b').source).toBe('l3_async_queued');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('DetectionFunnelCore: queue capacity', () => {
|
|
90
|
+
it('bounds the queue at queueCapacity', () => {
|
|
91
|
+
const funnel = new DetectionFunnelCore(makeConfig({ queueCapacity: 2 }));
|
|
92
|
+
funnel.detect('a');
|
|
93
|
+
funnel.detect('b');
|
|
94
|
+
funnel.detect('c'); // exceeds capacity, dropped
|
|
95
|
+
expect(funnel.flushQueue()).toEqual(['a', 'b']);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe('SimpleLRU', () => {
|
|
99
|
+
it('get returns undefined for missing key', () => {
|
|
100
|
+
const lru = new SimpleLRU(10);
|
|
101
|
+
expect(lru.get('missing')).toBeUndefined();
|
|
102
|
+
});
|
|
103
|
+
it('default capacity is 100', () => {
|
|
104
|
+
const lru = new SimpleLRU();
|
|
105
|
+
for (let i = 0; i < 150; i++)
|
|
106
|
+
lru.set(`k${i}`, i);
|
|
107
|
+
expect(lru.get('k0')).toBeUndefined(); // evicted
|
|
108
|
+
expect(lru.get('k149')).toBe(149);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
//# sourceMappingURL=detection-funnel-policy.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detection-funnel-policy.test.js","sourceRoot":"","sources":["../../../../src/runtime-v2/detection/__tests__/detection-funnel-policy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detection Funnel Policy — PRI-446 (migrated from the plugin adapter)
|
|
3
|
+
*
|
|
4
|
+
* Pure three-layer detection funnel logic:
|
|
5
|
+
* L1 — exact dictionary match (sync, delegated via injected matchFn)
|
|
6
|
+
* L2 — LRU cache of prior L3 resolutions (sync)
|
|
7
|
+
* L3 — async semantic queue (caller drains via flushQueue + updateCache)
|
|
8
|
+
*
|
|
9
|
+
* This module is fully pure: no I/O, no crypto, no plugin imports. The two
|
|
10
|
+
* environment-coupled dependencies — the dictionary matcher and the text hasher —
|
|
11
|
+
* are injected, so core owns the funnel *logic* while the plugin owns the
|
|
12
|
+
* concrete crypto.createHash and the PainDictionary.
|
|
13
|
+
*
|
|
14
|
+
* The plugin-side detection-funnel.ts is now a thin shell that wires
|
|
15
|
+
* DetectionFunnelCore with crypto + dictionary and re-exports the DetectionFunnel
|
|
16
|
+
* name, so DetectionService and evolution-worker keep working unchanged.
|
|
17
|
+
*
|
|
18
|
+
* ERR checklist:
|
|
19
|
+
* - ERR-001: no `as` casts; cache reads validated.
|
|
20
|
+
* - ERR-002: results carry structured source + severity.
|
|
21
|
+
* - EP-01: queue is bounded.
|
|
22
|
+
*/
|
|
23
|
+
export interface DetectionResult {
|
|
24
|
+
detected: boolean;
|
|
25
|
+
severity?: number;
|
|
26
|
+
ruleId?: string;
|
|
27
|
+
source: 'l1_exact' | 'l2_cache' | 'l3_async_queued' | 'l3_semantic_hit';
|
|
28
|
+
}
|
|
29
|
+
/** A matched pain rule (subset of PainDictionary's match return). */
|
|
30
|
+
export interface PainMatchResult {
|
|
31
|
+
ruleId: string;
|
|
32
|
+
severity: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Matcher contract injected by the plugin (backed by PainDictionary).
|
|
36
|
+
* Returns the matched rule + severity for L1 exact matching, or undefined.
|
|
37
|
+
*/
|
|
38
|
+
export type PainMatcher = (text: string) => PainMatchResult | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Hash function injected by the plugin (backed by crypto.createHash).
|
|
41
|
+
* Used for L2 cache keys.
|
|
42
|
+
*/
|
|
43
|
+
export type TextHasher = (text: string) => string;
|
|
44
|
+
/**
|
|
45
|
+
* Predicate injected by the plugin (backed by shouldIgnorePainProtocolText).
|
|
46
|
+
* Returns true for protocol tokens that short-circuit before any layer.
|
|
47
|
+
*/
|
|
48
|
+
export type ProtocolTokenGate = (text: string) => boolean;
|
|
49
|
+
/**
|
|
50
|
+
* A simple LRU Cache implementation using Map.
|
|
51
|
+
* Migrated verbatim from the prior plugin detection-funnel.ts.
|
|
52
|
+
*/
|
|
53
|
+
export declare class SimpleLRU<K, V> {
|
|
54
|
+
private readonly cache;
|
|
55
|
+
private readonly maxSize;
|
|
56
|
+
constructor(maxSize?: number);
|
|
57
|
+
get(key: K): V | undefined;
|
|
58
|
+
set(key: K, value: V): void;
|
|
59
|
+
}
|
|
60
|
+
export interface DetectionFunnelConfig {
|
|
61
|
+
/** L1 exact-match delegate. */
|
|
62
|
+
match: PainMatcher;
|
|
63
|
+
/** Text hasher for L2 cache keys. */
|
|
64
|
+
hash: TextHasher;
|
|
65
|
+
/** Protocol-token gate that runs before any layer. */
|
|
66
|
+
shouldIgnoreProtocol: ProtocolTokenGate;
|
|
67
|
+
/** L2 cache capacity. Default 100. */
|
|
68
|
+
cacheSize?: number;
|
|
69
|
+
/** L3 async queue capacity. Default 1000. */
|
|
70
|
+
queueCapacity?: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Orchestrates the three-layer detection funnel for pain signals.
|
|
74
|
+
*
|
|
75
|
+
* Pure logic: holds only in-memory cache and queue state. All environment
|
|
76
|
+
* coupling (dictionary, crypto) is injected via config.
|
|
77
|
+
*/
|
|
78
|
+
export declare class DetectionFunnelCore {
|
|
79
|
+
private readonly cache;
|
|
80
|
+
private asyncQueue;
|
|
81
|
+
private readonly match;
|
|
82
|
+
private readonly hash;
|
|
83
|
+
private readonly shouldIgnoreProtocol;
|
|
84
|
+
private readonly queueCapacity;
|
|
85
|
+
constructor(config: DetectionFunnelConfig);
|
|
86
|
+
/**
|
|
87
|
+
* Detects pain in the given text using L1 (Exact), L2 (Cache), and L3 (Async).
|
|
88
|
+
*/
|
|
89
|
+
detect(text: string): DetectionResult;
|
|
90
|
+
private enqueueAsync;
|
|
91
|
+
/**
|
|
92
|
+
* Internal method for the worker to update the cache after a semantic hit.
|
|
93
|
+
*/
|
|
94
|
+
updateCache(text: string, result: {
|
|
95
|
+
detected: boolean;
|
|
96
|
+
severity?: number;
|
|
97
|
+
}): void;
|
|
98
|
+
/**
|
|
99
|
+
* Retrieves and clears the current asynchronous queue.
|
|
100
|
+
*/
|
|
101
|
+
flushQueue(): string[];
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=detection-funnel-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detection-funnel-policy.d.ts","sourceRoot":"","sources":["../../../src/runtime-v2/detection/detection-funnel-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;CACzE;AAED,qEAAqE;AACrE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,eAAe,GAAG,SAAS,CAAC;AAExE;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAElD;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;AAI1D;;;GAGG;AACH,qBAAa,SAAS,CAAC,CAAC,EAAE,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBACrB,OAAO,SAAM;IAKzB,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAU1B,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;CAY5B;AAID,MAAM,WAAW,qBAAqB;IACpC,+BAA+B;IAC/B,KAAK,EAAE,WAAW,CAAC;IACnB,qCAAqC;IACrC,IAAI,EAAE,UAAU,CAAC;IACjB,sDAAsD;IACtD,oBAAoB,EAAE,iBAAiB,CAAC;IACxC,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAID;;;;;GAKG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA8D;IACpF,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAoB;IACzD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,MAAM,EAAE,qBAAqB;IAQzC;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe;IAoCrC,OAAO,CAAC,YAAY;IAOpB;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAKjF;;OAEG;IACH,UAAU,IAAI,MAAM,EAAE;CAKvB"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detection Funnel Policy — PRI-446 (migrated from the plugin adapter)
|
|
3
|
+
*
|
|
4
|
+
* Pure three-layer detection funnel logic:
|
|
5
|
+
* L1 — exact dictionary match (sync, delegated via injected matchFn)
|
|
6
|
+
* L2 — LRU cache of prior L3 resolutions (sync)
|
|
7
|
+
* L3 — async semantic queue (caller drains via flushQueue + updateCache)
|
|
8
|
+
*
|
|
9
|
+
* This module is fully pure: no I/O, no crypto, no plugin imports. The two
|
|
10
|
+
* environment-coupled dependencies — the dictionary matcher and the text hasher —
|
|
11
|
+
* are injected, so core owns the funnel *logic* while the plugin owns the
|
|
12
|
+
* concrete crypto.createHash and the PainDictionary.
|
|
13
|
+
*
|
|
14
|
+
* The plugin-side detection-funnel.ts is now a thin shell that wires
|
|
15
|
+
* DetectionFunnelCore with crypto + dictionary and re-exports the DetectionFunnel
|
|
16
|
+
* name, so DetectionService and evolution-worker keep working unchanged.
|
|
17
|
+
*
|
|
18
|
+
* ERR checklist:
|
|
19
|
+
* - ERR-001: no `as` casts; cache reads validated.
|
|
20
|
+
* - ERR-002: results carry structured source + severity.
|
|
21
|
+
* - EP-01: queue is bounded.
|
|
22
|
+
*/
|
|
23
|
+
// ── LRU cache ───────────────────────────────────────────────────────────────
|
|
24
|
+
/**
|
|
25
|
+
* A simple LRU Cache implementation using Map.
|
|
26
|
+
* Migrated verbatim from the prior plugin detection-funnel.ts.
|
|
27
|
+
*/
|
|
28
|
+
export class SimpleLRU {
|
|
29
|
+
cache;
|
|
30
|
+
maxSize;
|
|
31
|
+
constructor(maxSize = 100) {
|
|
32
|
+
this.cache = new Map();
|
|
33
|
+
this.maxSize = maxSize;
|
|
34
|
+
}
|
|
35
|
+
get(key) {
|
|
36
|
+
const item = this.cache.get(key);
|
|
37
|
+
if (item !== undefined) {
|
|
38
|
+
// Refresh: delete and re-insert
|
|
39
|
+
this.cache.delete(key);
|
|
40
|
+
this.cache.set(key, item);
|
|
41
|
+
}
|
|
42
|
+
return item;
|
|
43
|
+
}
|
|
44
|
+
set(key, value) {
|
|
45
|
+
if (this.cache.has(key)) {
|
|
46
|
+
this.cache.delete(key);
|
|
47
|
+
}
|
|
48
|
+
else if (this.cache.size >= this.maxSize) {
|
|
49
|
+
// Remove the oldest (first) item
|
|
50
|
+
const firstKey = this.cache.keys().next().value;
|
|
51
|
+
if (firstKey !== undefined) {
|
|
52
|
+
this.cache.delete(firstKey);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this.cache.set(key, value);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// ── Funnel ──────────────────────────────────────────────────────────────────
|
|
59
|
+
/**
|
|
60
|
+
* Orchestrates the three-layer detection funnel for pain signals.
|
|
61
|
+
*
|
|
62
|
+
* Pure logic: holds only in-memory cache and queue state. All environment
|
|
63
|
+
* coupling (dictionary, crypto) is injected via config.
|
|
64
|
+
*/
|
|
65
|
+
export class DetectionFunnelCore {
|
|
66
|
+
cache;
|
|
67
|
+
asyncQueue = [];
|
|
68
|
+
match;
|
|
69
|
+
hash;
|
|
70
|
+
shouldIgnoreProtocol;
|
|
71
|
+
queueCapacity;
|
|
72
|
+
constructor(config) {
|
|
73
|
+
this.match = config.match;
|
|
74
|
+
this.hash = config.hash;
|
|
75
|
+
this.shouldIgnoreProtocol = config.shouldIgnoreProtocol;
|
|
76
|
+
this.cache = new SimpleLRU(config.cacheSize ?? 100);
|
|
77
|
+
this.queueCapacity = config.queueCapacity ?? 1000;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Detects pain in the given text using L1 (Exact), L2 (Cache), and L3 (Async).
|
|
81
|
+
*/
|
|
82
|
+
detect(text) {
|
|
83
|
+
if (this.shouldIgnoreProtocol(text)) {
|
|
84
|
+
return { detected: false, source: 'l1_exact' };
|
|
85
|
+
}
|
|
86
|
+
// --- Layer 1: Exact Match (Sync) ---
|
|
87
|
+
const exactMatch = this.match(text);
|
|
88
|
+
if (exactMatch) {
|
|
89
|
+
return {
|
|
90
|
+
detected: true,
|
|
91
|
+
severity: exactMatch.severity,
|
|
92
|
+
ruleId: exactMatch.ruleId,
|
|
93
|
+
source: 'l1_exact',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// --- Layer 2: LRU Cache (Sync) ---
|
|
97
|
+
const hash = this.hash(text);
|
|
98
|
+
const cached = this.cache.get(hash);
|
|
99
|
+
if (cached) {
|
|
100
|
+
return {
|
|
101
|
+
detected: cached.detected,
|
|
102
|
+
severity: cached.severity,
|
|
103
|
+
source: 'l2_cache',
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// --- Layer 3: Async Semantic Queue ---
|
|
107
|
+
this.enqueueAsync(text);
|
|
108
|
+
return {
|
|
109
|
+
detected: false,
|
|
110
|
+
source: 'l3_async_queued',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
enqueueAsync(text) {
|
|
114
|
+
if (this.asyncQueue.length < this.queueCapacity) {
|
|
115
|
+
this.asyncQueue.push(text);
|
|
116
|
+
}
|
|
117
|
+
// Worker will pick this up and perform semantic search, then updateCache.
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Internal method for the worker to update the cache after a semantic hit.
|
|
121
|
+
*/
|
|
122
|
+
updateCache(text, result) {
|
|
123
|
+
const hash = this.hash(text);
|
|
124
|
+
this.cache.set(hash, result);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Retrieves and clears the current asynchronous queue.
|
|
128
|
+
*/
|
|
129
|
+
flushQueue() {
|
|
130
|
+
const queue = [...this.asyncQueue];
|
|
131
|
+
this.asyncQueue = [];
|
|
132
|
+
return queue;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=detection-funnel-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detection-funnel-policy.js","sourceRoot":"","sources":["../../../src/runtime-v2/detection/detection-funnel-policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detection — PRI-446
|
|
3
|
+
*
|
|
4
|
+
* Pure three-layer pain detection funnel logic. No I/O, no crypto, no plugin
|
|
5
|
+
* imports. Matcher/hasher/gate are injected so core owns the funnel logic while
|
|
6
|
+
* the plugin owns the concrete crypto + dictionary.
|
|
7
|
+
*/
|
|
8
|
+
export type { DetectionResult, PainMatchResult, PainMatcher, TextHasher, ProtocolTokenGate, DetectionFunnelConfig, } from './detection-funnel-policy.js';
|
|
9
|
+
export { SimpleLRU, DetectionFunnelCore } from './detection-funnel-policy.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/runtime-v2/detection/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,YAAY,EACV,eAAe,EACf,eAAe,EACf,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detection — PRI-446
|
|
3
|
+
*
|
|
4
|
+
* Pure three-layer pain detection funnel logic. No I/O, no crypto, no plugin
|
|
5
|
+
* imports. Matcher/hasher/gate are injected so core owns the funnel logic while
|
|
6
|
+
* the plugin owns the concrete crypto + dictionary.
|
|
7
|
+
*/
|
|
8
|
+
export { SimpleLRU, DetectionFunnelCore } from './detection-funnel-policy.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/runtime-v2/detection/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observation Resolver Tests — PRI-446
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the plugin-side raw-observation-adapter.test.ts to keep coverage on
|
|
5
|
+
* the migrated core module. The plugin file now re-exports these symbols.
|
|
6
|
+
*
|
|
7
|
+
* ERR checklist:
|
|
8
|
+
* - ERR-001: Source kind resolved from runtime values, not `as` casts.
|
|
9
|
+
* - ERR-025: Tests the real resolveSourceKind path.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=observation-resolver.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observation-resolver.test.d.ts","sourceRoot":"","sources":["../../../../src/runtime-v2/evidence-triage/__tests__/observation-resolver.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observation Resolver Tests — PRI-446
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the plugin-side raw-observation-adapter.test.ts to keep coverage on
|
|
5
|
+
* the migrated core module. The plugin file now re-exports these symbols.
|
|
6
|
+
*
|
|
7
|
+
* ERR checklist:
|
|
8
|
+
* - ERR-001: Source kind resolved from runtime values, not `as` casts.
|
|
9
|
+
* - ERR-025: Tests the real resolveSourceKind path.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect } from 'vitest';
|
|
12
|
+
import { resolveSourceKind, buildToolFailureObservation, buildLlmDetectionObservation, } from '../observation-resolver.js';
|
|
13
|
+
function obs(overrides = {}) {
|
|
14
|
+
return { observedAt: '2026-06-23T00:00:00.000Z', ...overrides };
|
|
15
|
+
}
|
|
16
|
+
describe('resolveSourceKind: per-kind mapping', () => {
|
|
17
|
+
it('isManualEntry → owner_reported', () => {
|
|
18
|
+
expect(resolveSourceKind(obs({ isManualEntry: true }))).toBe('owner_reported');
|
|
19
|
+
});
|
|
20
|
+
it('isGateBlock → rulehost_block', () => {
|
|
21
|
+
expect(resolveSourceKind(obs({ isGateBlock: true }))).toBe('rulehost_block');
|
|
22
|
+
});
|
|
23
|
+
it('isSubagentError → subagent_error', () => {
|
|
24
|
+
expect(resolveSourceKind(obs({ isSubagentError: true }))).toBe('subagent_error');
|
|
25
|
+
});
|
|
26
|
+
it('isRateLimit true → rate_limit', () => {
|
|
27
|
+
expect(resolveSourceKind(obs({ isRateLimit: true }))).toBe('rate_limit');
|
|
28
|
+
});
|
|
29
|
+
it('isRateLimit false → provider_failure', () => {
|
|
30
|
+
expect(resolveSourceKind(obs({ isRateLimit: false }))).toBe('provider_failure');
|
|
31
|
+
});
|
|
32
|
+
it("pain tool + openclaw_context_bound → agent_on_owner_request", () => {
|
|
33
|
+
expect(resolveSourceKind(obs({ toolName: 'pain', provenance: 'openclaw_context_bound' }))).toBe('agent_on_owner_request');
|
|
34
|
+
});
|
|
35
|
+
it("skill:pain + openclaw_context_bound → agent_on_owner_request", () => {
|
|
36
|
+
expect(resolveSourceKind(obs({ toolName: 'skill:pain', provenance: 'openclaw_context_bound' }))).toBe('agent_on_owner_request');
|
|
37
|
+
});
|
|
38
|
+
it('pain tool without openclaw_context_bound → owner_reported', () => {
|
|
39
|
+
expect(resolveSourceKind(obs({ toolName: 'pain' }))).toBe('owner_reported');
|
|
40
|
+
});
|
|
41
|
+
it('failureSource tool_failure → tool_failure', () => {
|
|
42
|
+
expect(resolveSourceKind(obs({ toolName: 'write', failureSource: 'tool_failure' }))).toBe('tool_failure');
|
|
43
|
+
});
|
|
44
|
+
it('failureSource dispatch_error → dispatch_error', () => {
|
|
45
|
+
expect(resolveSourceKind(obs({ toolName: 'read', failureSource: 'dispatch_error' }))).toBe('dispatch_error');
|
|
46
|
+
});
|
|
47
|
+
it('toolNotFound → dispatch_error', () => {
|
|
48
|
+
expect(resolveSourceKind(obs({ toolNotFound: true }))).toBe('dispatch_error');
|
|
49
|
+
});
|
|
50
|
+
it('nonZeroExit → tool_failure', () => {
|
|
51
|
+
expect(resolveSourceKind(obs({ toolName: 'read', nonZeroExit: true }))).toBe('tool_failure');
|
|
52
|
+
});
|
|
53
|
+
it('timedOut → tool_failure', () => {
|
|
54
|
+
expect(resolveSourceKind(obs({ toolName: 'exec', timedOut: true }))).toBe('tool_failure');
|
|
55
|
+
});
|
|
56
|
+
it('isGfiTriggered → gfi_threshold', () => {
|
|
57
|
+
expect(resolveSourceKind(obs({ detectionSource: 'llm_x', isGfiTriggered: true }))).toBe('gfi_threshold');
|
|
58
|
+
});
|
|
59
|
+
it("detectionSource 'llm_paralysis' → llm_paralysis", () => {
|
|
60
|
+
expect(resolveSourceKind(obs({ detectionSource: 'llm_paralysis' }))).toBe('llm_paralysis');
|
|
61
|
+
});
|
|
62
|
+
it("detectionSource 'llm_*' (not paralysis) → semantic", () => {
|
|
63
|
+
expect(resolveSourceKind(obs({ detectionSource: 'llm_repetition' }))).toBe('semantic');
|
|
64
|
+
});
|
|
65
|
+
it("detectionSource 'user_empathy' → empathy_inferred", () => {
|
|
66
|
+
expect(resolveSourceKind(obs({ detectionSource: 'user_empathy' }))).toBe('empathy_inferred');
|
|
67
|
+
});
|
|
68
|
+
it('empty observation → unknown', () => {
|
|
69
|
+
expect(resolveSourceKind(obs())).toBe('unknown');
|
|
70
|
+
});
|
|
71
|
+
it('unknown detectionSource → unknown', () => {
|
|
72
|
+
expect(resolveSourceKind(obs({ detectionSource: 'something_else' }))).toBe('unknown');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe('resolveSourceKind: field precedence', () => {
|
|
76
|
+
it('manual entry takes precedence over tool failure', () => {
|
|
77
|
+
expect(resolveSourceKind(obs({ isManualEntry: true, toolName: 'read', failureSource: 'tool_failure' }))).toBe('owner_reported');
|
|
78
|
+
});
|
|
79
|
+
it('gate block takes precedence over tool failure', () => {
|
|
80
|
+
expect(resolveSourceKind(obs({ isGateBlock: true, toolName: 'write', failureSource: 'tool_failure' }))).toBe('rulehost_block');
|
|
81
|
+
});
|
|
82
|
+
it('GFI triggered takes precedence over detection source prefix', () => {
|
|
83
|
+
expect(resolveSourceKind(obs({ detectionSource: 'llm_paralysis', isGfiTriggered: true }))).toBe('gfi_threshold');
|
|
84
|
+
});
|
|
85
|
+
it('subagent error takes precedence over tool failure', () => {
|
|
86
|
+
expect(resolveSourceKind(obs({ isSubagentError: true, failureSource: 'tool_failure' }))).toBe('subagent_error');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('buildToolFailureObservation', () => {
|
|
90
|
+
it('empty/whitespace tool name + error → dispatch_error', () => {
|
|
91
|
+
const o = buildToolFailureObservation({ toolName: '', error: 'boom' });
|
|
92
|
+
expect(o.failureSource).toBe('dispatch_error');
|
|
93
|
+
expect(o.toolName).toBe('');
|
|
94
|
+
});
|
|
95
|
+
it("'tool not found' message → dispatch_error", () => {
|
|
96
|
+
const o = buildToolFailureObservation({ toolName: 'read', error: 'tool not found' });
|
|
97
|
+
expect(o.failureSource).toBe('dispatch_error');
|
|
98
|
+
});
|
|
99
|
+
it("'unknown tool' message → dispatch_error", () => {
|
|
100
|
+
const o = buildToolFailureObservation({ toolName: 'read', error: 'unknown tool foo' });
|
|
101
|
+
expect(o.failureSource).toBe('dispatch_error');
|
|
102
|
+
});
|
|
103
|
+
it('generic tool error → tool_failure', () => {
|
|
104
|
+
const o = buildToolFailureObservation({ toolName: 'write', error: 'permission denied' });
|
|
105
|
+
expect(o.failureSource).toBe('tool_failure');
|
|
106
|
+
expect(o.nonZeroExit).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
it('non-zero exit code marks nonZeroExit true', () => {
|
|
109
|
+
const o = buildToolFailureObservation({ toolName: 'write', error: 'err', exitCode: 1 });
|
|
110
|
+
expect(o.nonZeroExit).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
it('zero exit code marks nonZeroExit false', () => {
|
|
113
|
+
const o = buildToolFailureObservation({ toolName: 'write', error: 'err', exitCode: 0 });
|
|
114
|
+
expect(o.nonZeroExit).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
it('no error and zero/undefined exit → failureSource undefined (not a failure context)', () => {
|
|
117
|
+
const o = buildToolFailureObservation({ toolName: 'write', error: null, exitCode: 0 });
|
|
118
|
+
expect(o.failureSource).toBeUndefined();
|
|
119
|
+
});
|
|
120
|
+
it('preserves provenance', () => {
|
|
121
|
+
const o = buildToolFailureObservation({ toolName: 'write', error: 'x', provenance: 'automatic_hook' });
|
|
122
|
+
expect(o.provenance).toBe('automatic_hook');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe('buildLlmDetectionObservation', () => {
|
|
126
|
+
it('builds observation with detectionSource and isGfiTriggered', () => {
|
|
127
|
+
const o = buildLlmDetectionObservation({ detectionSource: 'llm_loop', isGfiTriggered: true });
|
|
128
|
+
expect(o.detectionSource).toBe('llm_loop');
|
|
129
|
+
expect(o.isGfiTriggered).toBe(true);
|
|
130
|
+
expect(o.observedAt).toBeTruthy();
|
|
131
|
+
});
|
|
132
|
+
it('feeds into resolveSourceKind correctly', () => {
|
|
133
|
+
const o = buildLlmDetectionObservation({ detectionSource: 'llm_loop', isGfiTriggered: false });
|
|
134
|
+
expect(resolveSourceKind(o)).toBe('semantic');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
//# sourceMappingURL=observation-resolver.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observation-resolver.test.js","sourceRoot":"","sources":["../../../../src/runtime-v2/evidence-triage/__tests__/observation-resolver.test.ts"],"names":[],"mappings":"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"}
|