@unclick/mcp-server 0.3.14 → 0.3.15
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/memory/__tests__/load-memory-invalidation.test.d.ts +16 -0
- package/dist/memory/__tests__/load-memory-invalidation.test.d.ts.map +1 -0
- package/dist/memory/__tests__/load-memory-invalidation.test.js +91 -0
- package/dist/memory/__tests__/load-memory-invalidation.test.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression: load_memory.active_facts must exclude invalidated facts.
|
|
3
|
+
*
|
|
4
|
+
* Closes the parity gap between load_memory and search_memory documented in
|
|
5
|
+
* supabase/migrations/20260428210000_load_memory_invalidation_parity.sql.
|
|
6
|
+
*
|
|
7
|
+
* Pure unit case (always runs): assert the in-memory predicate that mirrors
|
|
8
|
+
* the SQL fact-selection filter excludes invalidated rows. This catches
|
|
9
|
+
* accidental removal of the bi-temporal predicate at the SQL level via the
|
|
10
|
+
* shared shape, without needing a live database.
|
|
11
|
+
*
|
|
12
|
+
* Integration case (skipped unless SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY
|
|
13
|
+
* are set): exercises SupabaseBackend.getStartupContext end to end.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=load-memory-invalidation.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-memory-invalidation.test.d.ts","sourceRoot":"","sources":["../../../src/memory/__tests__/load-memory-invalidation.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression: load_memory.active_facts must exclude invalidated facts.
|
|
3
|
+
*
|
|
4
|
+
* Closes the parity gap between load_memory and search_memory documented in
|
|
5
|
+
* supabase/migrations/20260428210000_load_memory_invalidation_parity.sql.
|
|
6
|
+
*
|
|
7
|
+
* Pure unit case (always runs): assert the in-memory predicate that mirrors
|
|
8
|
+
* the SQL fact-selection filter excludes invalidated rows. This catches
|
|
9
|
+
* accidental removal of the bi-temporal predicate at the SQL level via the
|
|
10
|
+
* shared shape, without needing a live database.
|
|
11
|
+
*
|
|
12
|
+
* Integration case (skipped unless SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY
|
|
13
|
+
* are set): exercises SupabaseBackend.getStartupContext end to end.
|
|
14
|
+
*/
|
|
15
|
+
import { describe, it, before } from "node:test";
|
|
16
|
+
import assert from "node:assert/strict";
|
|
17
|
+
/**
|
|
18
|
+
* Mirror of the `active_facts` predicate in mc_get_startup_context /
|
|
19
|
+
* get_startup_context after migration 20260428210000.
|
|
20
|
+
*/
|
|
21
|
+
function isActiveHotFact(row, now = new Date()) {
|
|
22
|
+
if (row.status !== "active")
|
|
23
|
+
return false;
|
|
24
|
+
if (row.decay_tier !== "hot")
|
|
25
|
+
return false;
|
|
26
|
+
if (row.invalidated_at !== null)
|
|
27
|
+
return false;
|
|
28
|
+
if (row.valid_to !== null && new Date(row.valid_to) <= now)
|
|
29
|
+
return false;
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
describe("load_memory active_facts predicate (pure)", () => {
|
|
33
|
+
const base = {
|
|
34
|
+
id: "f1",
|
|
35
|
+
status: "active",
|
|
36
|
+
decay_tier: "hot",
|
|
37
|
+
invalidated_at: null,
|
|
38
|
+
valid_to: null,
|
|
39
|
+
};
|
|
40
|
+
it("includes a live, hot, active, non-invalidated fact", () => {
|
|
41
|
+
assert.equal(isActiveHotFact(base), true);
|
|
42
|
+
});
|
|
43
|
+
it("excludes an invalidated fact (invalidated_at IS NOT NULL)", () => {
|
|
44
|
+
const invalidated = { ...base, invalidated_at: new Date().toISOString() };
|
|
45
|
+
assert.equal(isActiveHotFact(invalidated), false, "invalidated fact must not surface in load_memory.active_facts");
|
|
46
|
+
});
|
|
47
|
+
it("excludes a fact whose valid_to has passed", () => {
|
|
48
|
+
const expired = { ...base, valid_to: new Date(Date.now() - 1000).toISOString() };
|
|
49
|
+
assert.equal(isActiveHotFact(expired), false);
|
|
50
|
+
});
|
|
51
|
+
it("includes a fact whose valid_to is in the future", () => {
|
|
52
|
+
const future = { ...base, valid_to: new Date(Date.now() + 60_000).toISOString() };
|
|
53
|
+
assert.equal(isActiveHotFact(future), true);
|
|
54
|
+
});
|
|
55
|
+
it("excludes a superseded fact", () => {
|
|
56
|
+
assert.equal(isActiveHotFact({ ...base, status: "superseded" }), false);
|
|
57
|
+
});
|
|
58
|
+
it("excludes a non-hot tier fact", () => {
|
|
59
|
+
assert.equal(isActiveHotFact({ ...base, decay_tier: "warm" }), false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
// ---- Integration test (skipped without live creds) ----
|
|
63
|
+
const LIVE = !!(process.env.SUPABASE_URL && process.env.SUPABASE_SERVICE_ROLE_KEY);
|
|
64
|
+
describe("load_memory invalidation parity (Supabase integration)", { skip: !LIVE }, () => {
|
|
65
|
+
let testSession;
|
|
66
|
+
before(() => {
|
|
67
|
+
testSession = `test-load-memory-invalidation-${Date.now()}`;
|
|
68
|
+
});
|
|
69
|
+
it("excludes invalidated fact from active_facts after invalidation", async () => {
|
|
70
|
+
const { SupabaseBackend } = await import("../supabase.js");
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
|
+
const backend = new SupabaseBackend();
|
|
73
|
+
const factText = `load-memory-invalidation-${Date.now()}`;
|
|
74
|
+
const { id } = await backend.addFact({
|
|
75
|
+
fact: factText,
|
|
76
|
+
category: "test",
|
|
77
|
+
confidence: 0.9,
|
|
78
|
+
source_session_id: testSession,
|
|
79
|
+
});
|
|
80
|
+
// Confirm fact is visible in active_facts before invalidation.
|
|
81
|
+
const before = (await backend.getStartupContext(5));
|
|
82
|
+
const beforeFacts = (before.active_facts ?? []).map((f) => f.fact);
|
|
83
|
+
assert.ok(beforeFacts.includes(factText), "fact should appear in active_facts before invalidation");
|
|
84
|
+
await backend.invalidateFact({ fact_id: id, reason: "regression test", session_id: testSession });
|
|
85
|
+
// After invalidation, fact must NOT appear in active_facts.
|
|
86
|
+
const after = (await backend.getStartupContext(5));
|
|
87
|
+
const afterFacts = (after.active_facts ?? []).map((f) => f.fact);
|
|
88
|
+
assert.ok(!afterFacts.includes(factText), "invalidated fact must not appear in active_facts (load_memory parity with search_memory)");
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=load-memory-invalidation.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-memory-invalidation.test.js","sourceRoot":"","sources":["../../../src/memory/__tests__/load-memory-invalidation.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unclick/mcp-server",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.15",
|
|
4
4
|
"mcpName": "io.github.malamutemayhem/unclick-mcp-server",
|
|
5
5
|
"description": "MCP server for the UnClick tool marketplace — lets AI agents discover and use every UnClick tool",
|
|
6
6
|
"type": "module",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"build": "tsc",
|
|
23
23
|
"dev": "tsx src/index.ts",
|
|
24
24
|
"start": "node dist/index.js",
|
|
25
|
-
"test": "tsx --test src/memory/__tests__/hybrid-search.test.ts src/memory/__tests__/bitemporal.test.ts src/memory/__tests__/response-bounds.test.ts && vitest run",
|
|
25
|
+
"test": "tsx --test src/memory/__tests__/hybrid-search.test.ts src/memory/__tests__/bitemporal.test.ts src/memory/__tests__/response-bounds.test.ts src/memory/__tests__/load-memory-invalidation.test.ts && vitest run",
|
|
26
26
|
"prepublishOnly": "npm run build"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|