@drqedwards/pmll 2.0.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/LICENSE +21 -0
- package/README.md +26 -0
- package/agent_instructions.md +123 -0
- package/benchmarks/benchmark_retrieval.md +94 -0
- package/benchmarks/contextplus-standalone-speed.md +185 -0
- package/benchmarks/run_retrieval_stub.py +343 -0
- package/benchmarks/speed-test-results.md +163 -0
- package/benchmarks/three-way-speed-comparison.md +238 -0
- package/dist/embeddings.d.ts +32 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +140 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/graphql.d.ts +81 -0
- package/dist/graphql.d.ts.map +1 -0
- package/dist/graphql.js +389 -0
- package/dist/graphql.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +645 -0
- package/dist/index.js.map +1 -0
- package/dist/kv-store.d.ts +32 -0
- package/dist/kv-store.d.ts.map +1 -0
- package/dist/kv-store.js +103 -0
- package/dist/kv-store.js.map +1 -0
- package/dist/memory-graph.d.ts +122 -0
- package/dist/memory-graph.d.ts.map +1 -0
- package/dist/memory-graph.js +345 -0
- package/dist/memory-graph.js.map +1 -0
- package/dist/peek.d.ts +67 -0
- package/dist/peek.d.ts.map +1 -0
- package/dist/peek.js +61 -0
- package/dist/peek.js.map +1 -0
- package/dist/q-promise-bridge.d.ts +58 -0
- package/dist/q-promise-bridge.d.ts.map +1 -0
- package/dist/q-promise-bridge.js +88 -0
- package/dist/q-promise-bridge.js.map +1 -0
- package/dist/solution-engine.d.ts +54 -0
- package/dist/solution-engine.d.ts.map +1 -0
- package/dist/solution-engine.js +73 -0
- package/dist/solution-engine.js.map +1 -0
- package/package.json +69 -0
package/dist/peek.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* peek.ts — Main guard function for MCP context deduplication.
|
|
3
|
+
*
|
|
4
|
+
* `peekContext()` is the primary primitive that agents call **before**
|
|
5
|
+
* any expensive MCP tool invocation (e.g. a Playwright navigation or a
|
|
6
|
+
* remote API call). It checks two layers:
|
|
7
|
+
*
|
|
8
|
+
* 1. The in-process KV store (`PMMemoryStore`) — analogous to reading the
|
|
9
|
+
* PMLL memory silo (PMLL.c::init_silo / update_silo) for a cached result.
|
|
10
|
+
* 2. The Q-promise registry (`QPromiseRegistry`) — analogous to checking
|
|
11
|
+
* whether a `QMemNode` chain is still in-flight (Q_promise_lib pending).
|
|
12
|
+
*
|
|
13
|
+
* If both layers miss, the caller is expected to proceed with the real tool
|
|
14
|
+
* call and then invoke `store.set(key, value)` to populate the cache for
|
|
15
|
+
* future agents.
|
|
16
|
+
*/
|
|
17
|
+
import { PMMemoryStore } from "./kv-store.js";
|
|
18
|
+
import { QPromiseRegistry } from "./q-promise-bridge.js";
|
|
19
|
+
/** Return shape for a KV cache hit. */
|
|
20
|
+
export interface PeekHitResult {
|
|
21
|
+
hit: true;
|
|
22
|
+
value: string;
|
|
23
|
+
index: number;
|
|
24
|
+
}
|
|
25
|
+
/** Return shape for a Q-promise pending hit. */
|
|
26
|
+
export interface PeekPendingResult {
|
|
27
|
+
hit: true;
|
|
28
|
+
status: "pending";
|
|
29
|
+
promise_id: string;
|
|
30
|
+
}
|
|
31
|
+
/** Return shape for a full miss. */
|
|
32
|
+
export interface PeekMissResult {
|
|
33
|
+
hit: false;
|
|
34
|
+
}
|
|
35
|
+
export type PeekContextResult = PeekHitResult | PeekPendingResult | PeekMissResult;
|
|
36
|
+
/**
|
|
37
|
+
* Check whether `key` is already resolved for `sessionId`.
|
|
38
|
+
*
|
|
39
|
+
* The function implements a two-stage guard:
|
|
40
|
+
*
|
|
41
|
+
* Stage 1 — KV store hit (PMLL silo cache):
|
|
42
|
+
* If the key exists and is resolved, return the cached value
|
|
43
|
+
* immediately. This eliminates the need to re-invoke the
|
|
44
|
+
* corresponding MCP tool entirely.
|
|
45
|
+
*
|
|
46
|
+
* Stage 2 — Q-promise pending check:
|
|
47
|
+
* If the key is registered as an in-flight promise, return a
|
|
48
|
+
* `pending` indicator so the caller can await resolution rather
|
|
49
|
+
* than launching a duplicate tool call.
|
|
50
|
+
*
|
|
51
|
+
* Stage 3 — Full miss:
|
|
52
|
+
* Neither layer has seen this key. The caller should proceed with
|
|
53
|
+
* the real MCP tool call and then call `store.set(key, value)`
|
|
54
|
+
* to populate the cache.
|
|
55
|
+
*
|
|
56
|
+
* @param key The context key to look up (e.g. a URL, task ID).
|
|
57
|
+
* @param sessionId Identifies the current agent session (for logging).
|
|
58
|
+
* @param store The session's `PMMemoryStore` instance.
|
|
59
|
+
* @param promiseRegistry Shared `QPromiseRegistry` instance.
|
|
60
|
+
*
|
|
61
|
+
* @returns One of:
|
|
62
|
+
* - `{ hit: true, value: string, index: number }` — KV hit
|
|
63
|
+
* - `{ hit: true, status: "pending", promise_id: string }` — in-flight
|
|
64
|
+
* - `{ hit: false }` — full miss
|
|
65
|
+
*/
|
|
66
|
+
export declare function peekContext(key: string, sessionId: string, store: PMMemoryStore, promiseRegistry: QPromiseRegistry): PeekContextResult;
|
|
67
|
+
//# sourceMappingURL=peek.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peek.d.ts","sourceRoot":"","sources":["../src/peek.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,IAAI,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,gDAAgD;AAChD,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,IAAI,CAAC;IACV,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,oCAAoC;AACpC,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,KAAK,CAAC;CACZ;AAED,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,iBAAiB,GAAG,cAAc,CAAC;AAEnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,aAAa,EACpB,eAAe,EAAE,gBAAgB,GAChC,iBAAiB,CAenB"}
|
package/dist/peek.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* peek.ts — Main guard function for MCP context deduplication.
|
|
3
|
+
*
|
|
4
|
+
* `peekContext()` is the primary primitive that agents call **before**
|
|
5
|
+
* any expensive MCP tool invocation (e.g. a Playwright navigation or a
|
|
6
|
+
* remote API call). It checks two layers:
|
|
7
|
+
*
|
|
8
|
+
* 1. The in-process KV store (`PMMemoryStore`) — analogous to reading the
|
|
9
|
+
* PMLL memory silo (PMLL.c::init_silo / update_silo) for a cached result.
|
|
10
|
+
* 2. The Q-promise registry (`QPromiseRegistry`) — analogous to checking
|
|
11
|
+
* whether a `QMemNode` chain is still in-flight (Q_promise_lib pending).
|
|
12
|
+
*
|
|
13
|
+
* If both layers miss, the caller is expected to proceed with the real tool
|
|
14
|
+
* call and then invoke `store.set(key, value)` to populate the cache for
|
|
15
|
+
* future agents.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Check whether `key` is already resolved for `sessionId`.
|
|
19
|
+
*
|
|
20
|
+
* The function implements a two-stage guard:
|
|
21
|
+
*
|
|
22
|
+
* Stage 1 — KV store hit (PMLL silo cache):
|
|
23
|
+
* If the key exists and is resolved, return the cached value
|
|
24
|
+
* immediately. This eliminates the need to re-invoke the
|
|
25
|
+
* corresponding MCP tool entirely.
|
|
26
|
+
*
|
|
27
|
+
* Stage 2 — Q-promise pending check:
|
|
28
|
+
* If the key is registered as an in-flight promise, return a
|
|
29
|
+
* `pending` indicator so the caller can await resolution rather
|
|
30
|
+
* than launching a duplicate tool call.
|
|
31
|
+
*
|
|
32
|
+
* Stage 3 — Full miss:
|
|
33
|
+
* Neither layer has seen this key. The caller should proceed with
|
|
34
|
+
* the real MCP tool call and then call `store.set(key, value)`
|
|
35
|
+
* to populate the cache.
|
|
36
|
+
*
|
|
37
|
+
* @param key The context key to look up (e.g. a URL, task ID).
|
|
38
|
+
* @param sessionId Identifies the current agent session (for logging).
|
|
39
|
+
* @param store The session's `PMMemoryStore` instance.
|
|
40
|
+
* @param promiseRegistry Shared `QPromiseRegistry` instance.
|
|
41
|
+
*
|
|
42
|
+
* @returns One of:
|
|
43
|
+
* - `{ hit: true, value: string, index: number }` — KV hit
|
|
44
|
+
* - `{ hit: true, status: "pending", promise_id: string }` — in-flight
|
|
45
|
+
* - `{ hit: false }` — full miss
|
|
46
|
+
*/
|
|
47
|
+
export function peekContext(key, sessionId, store, promiseRegistry) {
|
|
48
|
+
// Stage 1: KV store check (PMLL silo read)
|
|
49
|
+
const [hit, value, index] = store.peek(key);
|
|
50
|
+
if (hit) {
|
|
51
|
+
return { hit: true, value: value, index: index };
|
|
52
|
+
}
|
|
53
|
+
// Stage 2: Q-promise in-flight check
|
|
54
|
+
const [found, status] = promiseRegistry.peekPromise(key);
|
|
55
|
+
if (found && status === "pending") {
|
|
56
|
+
return { hit: true, status: "pending", promise_id: key };
|
|
57
|
+
}
|
|
58
|
+
// Stage 3: Full miss — caller proceeds with the actual tool call
|
|
59
|
+
return { hit: false };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=peek.js.map
|
package/dist/peek.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peek.js","sourceRoot":"","sources":["../src/peek.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA0BH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,WAAW,CACzB,GAAW,EACX,SAAiB,EACjB,KAAoB,EACpB,eAAiC;IAEjC,2CAA2C;IAC3C,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,GAAG,EAAE,CAAC;QACR,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,KAAM,EAAE,KAAK,EAAE,KAAM,EAAE,CAAC;IACrD,CAAC;IAED,qCAAqC;IACrC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,eAAe,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACzD,IAAI,KAAK,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC;IAC3D,CAAC;IAED,iEAAiE;IACjE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* q-promise-bridge.ts — Registry for in-flight Q-promise continuations.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the `QMemNode` singly-linked chain defined in
|
|
5
|
+
* `Q_promise_lib/Q_promises.h`:
|
|
6
|
+
*
|
|
7
|
+
* typedef struct QMemNode {
|
|
8
|
+
* long index;
|
|
9
|
+
* const char *payload;
|
|
10
|
+
* struct QMemNode *next;
|
|
11
|
+
* } QMemNode;
|
|
12
|
+
*
|
|
13
|
+
* In the C library a chain is traversed via `q_then(head, cb)`, invoking a
|
|
14
|
+
* callback for every node. Here we model the same lifecycle in pure TypeScript:
|
|
15
|
+
* each promise starts as `"pending"` (`QMemNode.payload == NULL`) and
|
|
16
|
+
* transitions to `"resolved"` once `resolve()` is called with a payload.
|
|
17
|
+
*
|
|
18
|
+
* The `peekPromise()` method is a non-destructive status check — the
|
|
19
|
+
* TypeScript equivalent of walking the chain without modifying it.
|
|
20
|
+
*/
|
|
21
|
+
/** Result of a `peekPromise()` call: `[found, status, payload]`. */
|
|
22
|
+
export type PeekPromiseResult = [boolean, string | null, string | null];
|
|
23
|
+
/**
|
|
24
|
+
* In-process registry of Q-promise continuations.
|
|
25
|
+
*
|
|
26
|
+
* Provides the same lifecycle as the C `QMemNode` chain:
|
|
27
|
+
* - `register()` — allocate a new pending node
|
|
28
|
+
* - `resolve()` — write the payload (analogous to q_then callback)
|
|
29
|
+
* - `peekPromise()` — read status without consuming the entry
|
|
30
|
+
*
|
|
31
|
+
* Multiple sessions share a single registry; the `promiseId` is the
|
|
32
|
+
* caller's responsibility to namespace (e.g. `"{sessionId}:{key}"`).
|
|
33
|
+
*/
|
|
34
|
+
export declare class QPromiseRegistry {
|
|
35
|
+
private _promises;
|
|
36
|
+
/** Add a new pending promise (allocate a QMemNode with NULL payload). */
|
|
37
|
+
register(promiseId: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* Mark `promiseId` as resolved with `payload`.
|
|
40
|
+
*
|
|
41
|
+
* Mirrors the `QThenCallback` being invoked for a node.
|
|
42
|
+
*
|
|
43
|
+
* @returns true if the promise existed and was resolved; false if unknown.
|
|
44
|
+
*/
|
|
45
|
+
resolve(promiseId: string, payload: string): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Non-destructive status check.
|
|
48
|
+
*
|
|
49
|
+
* @returns `[found, status, payload]` — `found` is false when the promise
|
|
50
|
+
* ID is unknown.
|
|
51
|
+
*/
|
|
52
|
+
peekPromise(promiseId: string): PeekPromiseResult;
|
|
53
|
+
get size(): number;
|
|
54
|
+
has(promiseId: string): boolean;
|
|
55
|
+
/** Exposed for testing: clear all promises. */
|
|
56
|
+
clear(): void;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=q-promise-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"q-promise-bridge.d.ts","sourceRoot":"","sources":["../src/q-promise-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAgBH,oEAAoE;AACpE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;AAExE;;;;;;;;;;GAUG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAAoC;IAMrD,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAQjC;;;;;;OAMG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAUpD;;;;;OAKG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB;IAYjD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAI/B,+CAA+C;IAC/C,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* q-promise-bridge.ts — Registry for in-flight Q-promise continuations.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the `QMemNode` singly-linked chain defined in
|
|
5
|
+
* `Q_promise_lib/Q_promises.h`:
|
|
6
|
+
*
|
|
7
|
+
* typedef struct QMemNode {
|
|
8
|
+
* long index;
|
|
9
|
+
* const char *payload;
|
|
10
|
+
* struct QMemNode *next;
|
|
11
|
+
* } QMemNode;
|
|
12
|
+
*
|
|
13
|
+
* In the C library a chain is traversed via `q_then(head, cb)`, invoking a
|
|
14
|
+
* callback for every node. Here we model the same lifecycle in pure TypeScript:
|
|
15
|
+
* each promise starts as `"pending"` (`QMemNode.payload == NULL`) and
|
|
16
|
+
* transitions to `"resolved"` once `resolve()` is called with a payload.
|
|
17
|
+
*
|
|
18
|
+
* The `peekPromise()` method is a non-destructive status check — the
|
|
19
|
+
* TypeScript equivalent of walking the chain without modifying it.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* In-process registry of Q-promise continuations.
|
|
23
|
+
*
|
|
24
|
+
* Provides the same lifecycle as the C `QMemNode` chain:
|
|
25
|
+
* - `register()` — allocate a new pending node
|
|
26
|
+
* - `resolve()` — write the payload (analogous to q_then callback)
|
|
27
|
+
* - `peekPromise()` — read status without consuming the entry
|
|
28
|
+
*
|
|
29
|
+
* Multiple sessions share a single registry; the `promiseId` is the
|
|
30
|
+
* caller's responsibility to namespace (e.g. `"{sessionId}:{key}"`).
|
|
31
|
+
*/
|
|
32
|
+
export class QPromiseRegistry {
|
|
33
|
+
_promises = new Map();
|
|
34
|
+
// ------------------------------------------------------------------
|
|
35
|
+
// Core operations
|
|
36
|
+
// ------------------------------------------------------------------
|
|
37
|
+
/** Add a new pending promise (allocate a QMemNode with NULL payload). */
|
|
38
|
+
register(promiseId) {
|
|
39
|
+
this._promises.set(promiseId, {
|
|
40
|
+
promiseId,
|
|
41
|
+
status: "pending",
|
|
42
|
+
payload: null,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Mark `promiseId` as resolved with `payload`.
|
|
47
|
+
*
|
|
48
|
+
* Mirrors the `QThenCallback` being invoked for a node.
|
|
49
|
+
*
|
|
50
|
+
* @returns true if the promise existed and was resolved; false if unknown.
|
|
51
|
+
*/
|
|
52
|
+
resolve(promiseId, payload) {
|
|
53
|
+
const promise = this._promises.get(promiseId);
|
|
54
|
+
if (promise === undefined) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
promise.status = "resolved";
|
|
58
|
+
promise.payload = payload;
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Non-destructive status check.
|
|
63
|
+
*
|
|
64
|
+
* @returns `[found, status, payload]` — `found` is false when the promise
|
|
65
|
+
* ID is unknown.
|
|
66
|
+
*/
|
|
67
|
+
peekPromise(promiseId) {
|
|
68
|
+
const promise = this._promises.get(promiseId);
|
|
69
|
+
if (promise === undefined) {
|
|
70
|
+
return [false, null, null];
|
|
71
|
+
}
|
|
72
|
+
return [true, promise.status, promise.payload];
|
|
73
|
+
}
|
|
74
|
+
// ------------------------------------------------------------------
|
|
75
|
+
// Introspection helpers
|
|
76
|
+
// ------------------------------------------------------------------
|
|
77
|
+
get size() {
|
|
78
|
+
return this._promises.size;
|
|
79
|
+
}
|
|
80
|
+
has(promiseId) {
|
|
81
|
+
return this._promises.has(promiseId);
|
|
82
|
+
}
|
|
83
|
+
/** Exposed for testing: clear all promises. */
|
|
84
|
+
clear() {
|
|
85
|
+
this._promises.clear();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=q-promise-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"q-promise-bridge.js","sourceRoot":"","sources":["../src/q-promise-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAmBH;;;;;;;;;;GAUG;AACH,MAAM,OAAO,gBAAgB;IACnB,SAAS,GAA0B,IAAI,GAAG,EAAE,CAAC;IAErD,qEAAqE;IACrE,kBAAkB;IAClB,qEAAqE;IAErE,yEAAyE;IACzE,QAAQ,CAAC,SAAiB;QACxB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE;YAC5B,SAAS;YACT,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,SAAiB,EAAE,OAAe;QACxC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,CAAC,MAAM,GAAG,UAAU,CAAC;QAC5B,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;OAKG;IACH,WAAW,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAED,qEAAqE;IACrE,wBAAwB;IACxB,qEAAqE;IAErE,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;IAC7B,CAAC;IAED,GAAG,CAAC,SAAiB;QACnB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,+CAA+C;IAC/C,KAAK;QACH,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* solution-engine.ts — Context+ Solution Engine Processor for PMLL MCP.
|
|
3
|
+
*
|
|
4
|
+
* Integrates the Context+ semantic intelligence approach as the long-term
|
|
5
|
+
* memory and solution engine for the PMLL persistent memory logic loop.
|
|
6
|
+
*
|
|
7
|
+
* The solution engine:
|
|
8
|
+
* 1. Bridges short-term KV cache (existing 5 tools) with the long-term
|
|
9
|
+
* memory graph (new 6 tools from Context+)
|
|
10
|
+
* 2. Provides a unified context resolution path: short-term → long-term
|
|
11
|
+
* 3. Auto-promotes frequently accessed short-term cache entries to the
|
|
12
|
+
* long-term memory graph
|
|
13
|
+
* 4. Implements the Context+ decay scoring and similarity-based retrieval
|
|
14
|
+
*
|
|
15
|
+
* Designed to improve context retention and retrieval for coding agents by combining:
|
|
16
|
+
* - Immediate context via KV cache (short-term)
|
|
17
|
+
* - Long-term knowledge via the memory graph
|
|
18
|
+
* - Semantic search across both layers
|
|
19
|
+
*/
|
|
20
|
+
import { PMMemoryStore } from "./kv-store.js";
|
|
21
|
+
import { type NodeType } from "./memory-graph.js";
|
|
22
|
+
/**
|
|
23
|
+
* Resolve context by checking both short-term (KV) and long-term (graph)
|
|
24
|
+
* memory layers. Returns the best match from either layer.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveContext(sessionId: string, key: string, store: PMMemoryStore): {
|
|
27
|
+
source: "short_term" | "long_term" | "miss";
|
|
28
|
+
value: string | null;
|
|
29
|
+
score: number;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Promote a short-term KV entry to the long-term memory graph.
|
|
33
|
+
* This creates a persistent memory node from a frequently accessed cache entry.
|
|
34
|
+
*/
|
|
35
|
+
export declare function promoteToLongTerm(sessionId: string, key: string, value: string, nodeType?: NodeType, metadata?: Record<string, string>): {
|
|
36
|
+
promoted: boolean;
|
|
37
|
+
nodeId: string | null;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Get a unified status view of both short-term and long-term memory.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getMemoryStatus(sessionId: string, store: PMMemoryStore): {
|
|
43
|
+
shortTerm: {
|
|
44
|
+
slots: number;
|
|
45
|
+
siloSize: number;
|
|
46
|
+
};
|
|
47
|
+
longTerm: {
|
|
48
|
+
nodes: number;
|
|
49
|
+
edges: number;
|
|
50
|
+
types: Record<string, number>;
|
|
51
|
+
};
|
|
52
|
+
promotionThreshold: number;
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=solution-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solution-engine.d.ts","sourceRoot":"","sources":["../src/solution-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAIL,KAAK,QAAQ,EACd,MAAM,mBAAmB,CAAC;AAO3B;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,aAAa,GACnB;IAAE,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAmBtF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,QAAoB,EAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAG9C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,aAAa,GACnB;IACD,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IAC1E,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAeA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* solution-engine.ts — Context+ Solution Engine Processor for PMLL MCP.
|
|
3
|
+
*
|
|
4
|
+
* Integrates the Context+ semantic intelligence approach as the long-term
|
|
5
|
+
* memory and solution engine for the PMLL persistent memory logic loop.
|
|
6
|
+
*
|
|
7
|
+
* The solution engine:
|
|
8
|
+
* 1. Bridges short-term KV cache (existing 5 tools) with the long-term
|
|
9
|
+
* memory graph (new 6 tools from Context+)
|
|
10
|
+
* 2. Provides a unified context resolution path: short-term → long-term
|
|
11
|
+
* 3. Auto-promotes frequently accessed short-term cache entries to the
|
|
12
|
+
* long-term memory graph
|
|
13
|
+
* 4. Implements the Context+ decay scoring and similarity-based retrieval
|
|
14
|
+
*
|
|
15
|
+
* Designed to improve context retention and retrieval for coding agents by combining:
|
|
16
|
+
* - Immediate context via KV cache (short-term)
|
|
17
|
+
* - Long-term knowledge via the memory graph
|
|
18
|
+
* - Semantic search across both layers
|
|
19
|
+
*/
|
|
20
|
+
import { upsertNode, searchGraph, getGraphStats, } from "./memory-graph.js";
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Promotion threshold: entries accessed >= this count get promoted
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
const PROMOTION_THRESHOLD = 3;
|
|
25
|
+
/**
|
|
26
|
+
* Resolve context by checking both short-term (KV) and long-term (graph)
|
|
27
|
+
* memory layers. Returns the best match from either layer.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveContext(sessionId, key, store) {
|
|
30
|
+
// Layer 1: Short-term KV cache
|
|
31
|
+
const [hit, value] = store.peek(key);
|
|
32
|
+
if (hit && value !== null) {
|
|
33
|
+
return { source: "short_term", value, score: 1.0 };
|
|
34
|
+
}
|
|
35
|
+
// Layer 2: Long-term memory graph (semantic search)
|
|
36
|
+
const graphResult = searchGraph(sessionId, key, 1, 1);
|
|
37
|
+
if (graphResult.direct.length > 0) {
|
|
38
|
+
const top = graphResult.direct[0];
|
|
39
|
+
return {
|
|
40
|
+
source: "long_term",
|
|
41
|
+
value: top.node.content,
|
|
42
|
+
score: top.relevanceScore / 100,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return { source: "miss", value: null, score: 0 };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Promote a short-term KV entry to the long-term memory graph.
|
|
49
|
+
* This creates a persistent memory node from a frequently accessed cache entry.
|
|
50
|
+
*/
|
|
51
|
+
export function promoteToLongTerm(sessionId, key, value, nodeType = "concept", metadata) {
|
|
52
|
+
const node = upsertNode(sessionId, nodeType, key, value, metadata);
|
|
53
|
+
return { promoted: true, nodeId: node.id };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get a unified status view of both short-term and long-term memory.
|
|
57
|
+
*/
|
|
58
|
+
export function getMemoryStatus(sessionId, store) {
|
|
59
|
+
const stats = getGraphStats(sessionId);
|
|
60
|
+
return {
|
|
61
|
+
shortTerm: {
|
|
62
|
+
slots: store.size,
|
|
63
|
+
siloSize: store.siloSize,
|
|
64
|
+
},
|
|
65
|
+
longTerm: {
|
|
66
|
+
nodes: stats.nodes,
|
|
67
|
+
edges: stats.edges,
|
|
68
|
+
types: stats.types,
|
|
69
|
+
},
|
|
70
|
+
promotionThreshold: PROMOTION_THRESHOLD,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=solution-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solution-engine.js","sourceRoot":"","sources":["../src/solution-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EACL,UAAU,EACV,WAAW,EACX,aAAa,GAEd,MAAM,mBAAmB,CAAC;AAE3B,8EAA8E;AAC9E,mEAAmE;AACnE,8EAA8E;AAC9E,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,SAAiB,EACjB,GAAW,EACX,KAAoB;IAEpB,+BAA+B;IAC/B,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,GAAG,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1B,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACrD,CAAC;IAED,oDAAoD;IACpD,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACtD,IAAI,WAAW,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAClC,OAAO;YACL,MAAM,EAAE,WAAW;YACnB,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO;YACvB,KAAK,EAAE,GAAG,CAAC,cAAc,GAAG,GAAG;SAChC,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAAiB,EACjB,GAAW,EACX,KAAa,EACb,WAAqB,SAAS,EAC9B,QAAiC;IAEjC,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IACnE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;AAC7C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,SAAiB,EACjB,KAAoB;IAMpB,MAAM,KAAK,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAEvC,OAAO;QACL,SAAS,EAAE;YACT,KAAK,EAAE,KAAK,CAAC,IAAI;YACjB,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB;QACD,QAAQ,EAAE;YACR,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB;QACD,kBAAkB,EAAE,mBAAmB;KACxC,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@drqedwards/pmll",
|
|
3
|
+
"version": "2.0.4",
|
|
4
|
+
"mcpName": "io.github.drqedwards/pmll-memory-mcp",
|
|
5
|
+
"description": "Persistent Memory Logic Loop (PMLL) MCP server. Short name for pmll-memory-mcp; npx pmll starts the server.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"pmll": "dist/index.js",
|
|
10
|
+
"pmll-memory-mcp": "dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"agent_instructions.md",
|
|
15
|
+
"benchmarks"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"prepublishOnly": "npm run build",
|
|
20
|
+
"start": "node dist/index.js",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:watch": "vitest",
|
|
23
|
+
"lint": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"mcp",
|
|
27
|
+
"model-context-protocol",
|
|
28
|
+
"pmll",
|
|
29
|
+
"memory",
|
|
30
|
+
"kv-store",
|
|
31
|
+
"q-promise",
|
|
32
|
+
"context-plus",
|
|
33
|
+
"memory-graph",
|
|
34
|
+
"semantic-search",
|
|
35
|
+
"solution-engine",
|
|
36
|
+
"agent-instructions",
|
|
37
|
+
"benchmarks",
|
|
38
|
+
"peek-cache"
|
|
39
|
+
],
|
|
40
|
+
"author": "drQedwards",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/drQedwards/PPM.git"
|
|
45
|
+
},
|
|
46
|
+
"funding": [
|
|
47
|
+
{
|
|
48
|
+
"type": "github",
|
|
49
|
+
"url": "https://github.com/sponsors/DrQedwards"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"type": "buymeacoffee",
|
|
53
|
+
"url": "https://buymeacoffee.com/drqedwards"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"type": "coingecko",
|
|
57
|
+
"url": "https://www.coingecko.com/en/portfolios/public/jkdrq"
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
62
|
+
"zod": "^3.24.4"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@types/node": "^22.15.0",
|
|
66
|
+
"typescript": "^5.9.3",
|
|
67
|
+
"vitest": "^3.1.4"
|
|
68
|
+
}
|
|
69
|
+
}
|