@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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -0
  3. package/agent_instructions.md +123 -0
  4. package/benchmarks/benchmark_retrieval.md +94 -0
  5. package/benchmarks/contextplus-standalone-speed.md +185 -0
  6. package/benchmarks/run_retrieval_stub.py +343 -0
  7. package/benchmarks/speed-test-results.md +163 -0
  8. package/benchmarks/three-way-speed-comparison.md +238 -0
  9. package/dist/embeddings.d.ts +32 -0
  10. package/dist/embeddings.d.ts.map +1 -0
  11. package/dist/embeddings.js +140 -0
  12. package/dist/embeddings.js.map +1 -0
  13. package/dist/graphql.d.ts +81 -0
  14. package/dist/graphql.d.ts.map +1 -0
  15. package/dist/graphql.js +389 -0
  16. package/dist/graphql.js.map +1 -0
  17. package/dist/index.d.ts +55 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +645 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/kv-store.d.ts +32 -0
  22. package/dist/kv-store.d.ts.map +1 -0
  23. package/dist/kv-store.js +103 -0
  24. package/dist/kv-store.js.map +1 -0
  25. package/dist/memory-graph.d.ts +122 -0
  26. package/dist/memory-graph.d.ts.map +1 -0
  27. package/dist/memory-graph.js +345 -0
  28. package/dist/memory-graph.js.map +1 -0
  29. package/dist/peek.d.ts +67 -0
  30. package/dist/peek.d.ts.map +1 -0
  31. package/dist/peek.js +61 -0
  32. package/dist/peek.js.map +1 -0
  33. package/dist/q-promise-bridge.d.ts +58 -0
  34. package/dist/q-promise-bridge.d.ts.map +1 -0
  35. package/dist/q-promise-bridge.js +88 -0
  36. package/dist/q-promise-bridge.js.map +1 -0
  37. package/dist/solution-engine.d.ts +54 -0
  38. package/dist/solution-engine.d.ts.map +1 -0
  39. package/dist/solution-engine.js +73 -0
  40. package/dist/solution-engine.js.map +1 -0
  41. 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
@@ -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
+ }