@mcp-abap-adt/llm-agent-libs 25.0.0 → 27.0.1

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 (94) hide show
  1. package/README.md +11 -14
  2. package/dist/agent.d.ts +2 -1
  3. package/dist/agent.d.ts.map +1 -1
  4. package/dist/agent.js +6 -5
  5. package/dist/agent.js.map +1 -1
  6. package/dist/builder.d.ts +36 -14
  7. package/dist/builder.d.ts.map +1 -1
  8. package/dist/builder.js +472 -348
  9. package/dist/builder.js.map +1 -1
  10. package/dist/config/config-watcher.d.ts.map +1 -1
  11. package/dist/config/config-watcher.js +9 -5
  12. package/dist/config/config-watcher.js.map +1 -1
  13. package/dist/index.d.ts +1 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -5
  16. package/dist/index.js.map +1 -1
  17. package/dist/mcp/vectorize-mcp-tools.d.ts.map +1 -1
  18. package/dist/mcp/vectorize-mcp-tools.js +21 -2
  19. package/dist/mcp/vectorize-mcp-tools.js.map +1 -1
  20. package/dist/pipeline/handlers/history-upsert.d.ts.map +1 -1
  21. package/dist/pipeline/handlers/history-upsert.js +8 -1
  22. package/dist/pipeline/handlers/history-upsert.js.map +1 -1
  23. package/dist/pipeline/handlers/rag-query.d.ts +1 -1
  24. package/dist/pipeline/handlers/rag-query.d.ts.map +1 -1
  25. package/dist/pipeline/handlers/rag-query.js +10 -2
  26. package/dist/pipeline/handlers/rag-query.js.map +1 -1
  27. package/dist/plugins/index.d.ts +1 -1
  28. package/dist/plugins/index.d.ts.map +1 -1
  29. package/dist/plugins/index.js +1 -1
  30. package/dist/plugins/index.js.map +1 -1
  31. package/dist/plugins/types.d.ts +7 -0
  32. package/dist/plugins/types.d.ts.map +1 -1
  33. package/dist/plugins/types.js +85 -14
  34. package/dist/plugins/types.js.map +1 -1
  35. package/dist/providers.d.ts.map +1 -1
  36. package/dist/providers.js +8 -5
  37. package/dist/providers.js.map +1 -1
  38. package/dist/session/session-graph-factory.d.ts +49 -16
  39. package/dist/session/session-graph-factory.d.ts.map +1 -1
  40. package/dist/session/session-graph-factory.js +140 -62
  41. package/dist/session/session-graph-factory.js.map +1 -1
  42. package/dist/skills/plugin-host/qdrant-store.d.ts +6 -1
  43. package/dist/skills/plugin-host/qdrant-store.d.ts.map +1 -1
  44. package/dist/skills/plugin-host/qdrant-store.js +7 -7
  45. package/dist/skills/plugin-host/qdrant-store.js.map +1 -1
  46. package/dist/util/stop-all.d.ts +19 -0
  47. package/dist/util/stop-all.d.ts.map +1 -0
  48. package/dist/util/stop-all.js +30 -0
  49. package/dist/util/stop-all.js.map +1 -0
  50. package/package.json +5 -35
  51. package/dist/coordinator/dag/reviewer-error-strategy.d.ts +0 -15
  52. package/dist/coordinator/dag/reviewer-error-strategy.d.ts.map +0 -1
  53. package/dist/coordinator/dag/reviewer-error-strategy.js +0 -38
  54. package/dist/coordinator/dag/reviewer-error-strategy.js.map +0 -1
  55. package/dist/skills/plugin-host/chunker.test.d.ts +0 -2
  56. package/dist/skills/plugin-host/chunker.test.d.ts.map +0 -1
  57. package/dist/skills/plugin-host/chunker.test.js +0 -31
  58. package/dist/skills/plugin-host/chunker.test.js.map +0 -1
  59. package/dist/skills/plugin-host/compatible-skills-rag.test.d.ts +0 -2
  60. package/dist/skills/plugin-host/compatible-skills-rag.test.d.ts.map +0 -1
  61. package/dist/skills/plugin-host/compatible-skills-rag.test.js +0 -174
  62. package/dist/skills/plugin-host/compatible-skills-rag.test.js.map +0 -1
  63. package/dist/skills/plugin-host/http-marketplace-source.test.d.ts +0 -2
  64. package/dist/skills/plugin-host/http-marketplace-source.test.d.ts.map +0 -1
  65. package/dist/skills/plugin-host/http-marketplace-source.test.js +0 -71
  66. package/dist/skills/plugin-host/http-marketplace-source.test.js.map +0 -1
  67. package/dist/skills/plugin-host/in-memory-store.test.d.ts +0 -2
  68. package/dist/skills/plugin-host/in-memory-store.test.d.ts.map +0 -1
  69. package/dist/skills/plugin-host/in-memory-store.test.js +0 -100
  70. package/dist/skills/plugin-host/in-memory-store.test.js.map +0 -1
  71. package/dist/skills/plugin-host/marketplace-adapter.test.d.ts +0 -2
  72. package/dist/skills/plugin-host/marketplace-adapter.test.d.ts.map +0 -1
  73. package/dist/skills/plugin-host/marketplace-adapter.test.js +0 -46
  74. package/dist/skills/plugin-host/marketplace-adapter.test.js.map +0 -1
  75. package/dist/skills/plugin-host/qdrant-store.test.d.ts +0 -2
  76. package/dist/skills/plugin-host/qdrant-store.test.d.ts.map +0 -1
  77. package/dist/skills/plugin-host/qdrant-store.test.js +0 -396
  78. package/dist/skills/plugin-host/qdrant-store.test.js.map +0 -1
  79. package/dist/skills/plugin-host/skill-plugin-host.ingest.test.d.ts +0 -2
  80. package/dist/skills/plugin-host/skill-plugin-host.ingest.test.d.ts.map +0 -1
  81. package/dist/skills/plugin-host/skill-plugin-host.ingest.test.js +0 -520
  82. package/dist/skills/plugin-host/skill-plugin-host.ingest.test.js.map +0 -1
  83. package/dist/skills/plugin-host/skill-plugin-host.recall-and-reload.test.d.ts +0 -2
  84. package/dist/skills/plugin-host/skill-plugin-host.recall-and-reload.test.d.ts.map +0 -1
  85. package/dist/skills/plugin-host/skill-plugin-host.recall-and-reload.test.js +0 -328
  86. package/dist/skills/plugin-host/skill-plugin-host.recall-and-reload.test.js.map +0 -1
  87. package/dist/skills/plugin-host/skills-rag-source.test.d.ts +0 -2
  88. package/dist/skills/plugin-host/skills-rag-source.test.d.ts.map +0 -1
  89. package/dist/skills/plugin-host/skills-rag-source.test.js +0 -69
  90. package/dist/skills/plugin-host/skills-rag-source.test.js.map +0 -1
  91. package/dist/skills/plugin-host/source-strategies.test.d.ts +0 -2
  92. package/dist/skills/plugin-host/source-strategies.test.d.ts.map +0 -1
  93. package/dist/skills/plugin-host/source-strategies.test.js +0 -52
  94. package/dist/skills/plugin-host/source-strategies.test.js.map +0 -1
@@ -1,71 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { test } from 'node:test';
3
- import { makeHttpMarketplaceSource } from './http-marketplace-source.js';
4
- const SKILL_MD = `---\nname: s\ndescription: d\n---\n## H\nbody`;
5
- test('fetches manifest + SKILL.md via injected transport, builds ingest result, zero FS', async () => {
6
- const src = makeHttpMarketplaceSource({
7
- source: 'vendor',
8
- enabled: ['p1'],
9
- transport: {
10
- async listPlugins() {
11
- return [{ plugin: 'p1', version: '1', skills: ['s'] }];
12
- },
13
- async fetchSkillMd(plugin, skill) {
14
- assert.equal(plugin, 'p1');
15
- assert.equal(skill, 's');
16
- return SKILL_MD;
17
- },
18
- },
19
- chunk: { maxChars: 1000 },
20
- });
21
- const res = await src.acquire();
22
- assert.deepEqual(res.collections.map((c) => c.group), ['p1']);
23
- assert.ok(res.records.length >= 1);
24
- });
25
- test('enabled "*" loads every offered plugin; empty enabled is a caller error', async () => {
26
- const transport = {
27
- async listPlugins() {
28
- return [
29
- { plugin: 'a', version: '1', skills: ['s'] },
30
- { plugin: 'b', version: '1', skills: ['s'] },
31
- ];
32
- },
33
- async fetchSkillMd() {
34
- return SKILL_MD;
35
- },
36
- };
37
- const all = makeHttpMarketplaceSource({
38
- source: 'v',
39
- enabled: ['*'],
40
- transport,
41
- chunk: { maxChars: 1000 },
42
- });
43
- assert.equal((await all.acquire()).collections.length, 2);
44
- assert.throws(() => makeHttpMarketplaceSource({
45
- source: 'v',
46
- enabled: [],
47
- transport,
48
- chunk: { maxChars: 1000 },
49
- }), /enabled/);
50
- });
51
- test('only the enabled subset of offered plugins is acquired', async () => {
52
- const src = makeHttpMarketplaceSource({
53
- source: 'v',
54
- enabled: ['b'],
55
- transport: {
56
- async listPlugins() {
57
- return [
58
- { plugin: 'a', version: '1', skills: ['s'] },
59
- { plugin: 'b', version: '1', skills: ['s'] },
60
- ];
61
- },
62
- async fetchSkillMd() {
63
- return SKILL_MD;
64
- },
65
- },
66
- chunk: { maxChars: 1000 },
67
- });
68
- const res = await src.acquire();
69
- assert.deepEqual(res.collections.map((c) => c.group), ['b']);
70
- });
71
- //# sourceMappingURL=http-marketplace-source.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"http-marketplace-source.test.js","sourceRoot":"","sources":["../../../src/skills/plugin-host/http-marketplace-source.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=in-memory-store.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"in-memory-store.test.d.ts","sourceRoot":"","sources":["../../../src/skills/plugin-host/in-memory-store.test.ts"],"names":[],"mappings":""}
@@ -1,100 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { test } from 'node:test';
3
- import { CatalogCasError } from '@mcp-abap-adt/llm-agent';
4
- import { makeInMemoryStoreProvider } from './in-memory-store.js';
5
- const MANIFEST = {
6
- embeddingSpaceId: 'sp',
7
- dimension: 3,
8
- retrievalSchemaVersion: 1,
9
- };
10
- const rec = (id, group, vec) => ({
11
- record: {
12
- id,
13
- sourceId: 's',
14
- group,
15
- name: id,
16
- retrievalText: id,
17
- content: `c-${id}`,
18
- provenance: id,
19
- },
20
- vector: vec,
21
- });
22
- test('build inactive → publishCatalog activates → query + activeSnapshot from catalog', async () => {
23
- const p = makeInMemoryStoreProvider();
24
- const store = p.forGroup('g1');
25
- const { generation } = await store.beginGeneration();
26
- // upsert is embedding-agnostic here: the in-memory store accepts pre-vectorised rows via a test seam
27
- await p._seed(generation, [
28
- rec('a', 'g1', [1, 0, 0]),
29
- rec('b', 'g1', [0, 1, 0]),
30
- ]);
31
- // nothing serves yet
32
- assert.equal(await store.activeSnapshot(), null);
33
- const before = await p.readCatalog();
34
- await p.publishCatalog(before.catalogRevision, [
35
- {
36
- collection: { group: 'g1', description: 'd', collection: 'g1' },
37
- sources: ['s'],
38
- generation,
39
- manifest: MANIFEST,
40
- },
41
- ]);
42
- const snap = await store.activeSnapshot();
43
- assert.equal(snap?.revision, generation);
44
- assert.deepEqual(snap?.manifest, MANIFEST);
45
- const hits = await store.queryRevision(generation, [1, 0, 0], 1);
46
- assert.equal(hits[0].record.id, 'a');
47
- });
48
- test('publishCatalog CAS rejects a stale expectedRevision with CatalogCasError', async () => {
49
- const p = makeInMemoryStoreProvider();
50
- const r0 = (await p.readCatalog()).catalogRevision;
51
- await p.publishCatalog(r0, []); // bumps to r1
52
- await assert.rejects(() => p.publishCatalog(r0, []), (e) => e instanceof CatalogCasError);
53
- });
54
- test('EXACT retention via lease: a generation pinned by activeSnapshot survives a concurrent reclaim until release', async () => {
55
- const p = makeInMemoryStoreProvider();
56
- const backend = p.forGroup('g1');
57
- const g = await backend.beginGeneration();
58
- await p._seed(g.generation, [rec('a', 'g1', [1, 0, 0])]);
59
- await p.publishCatalog((await p.readCatalog()).catalogRevision, [
60
- {
61
- collection: { group: 'g1', description: 'd', collection: 'g1' },
62
- sources: ['s'],
63
- generation: g.generation,
64
- manifest: MANIFEST,
65
- },
66
- ]);
67
- // a reader resolves (PINS) the generation...
68
- const snap = await backend.activeSnapshot();
69
- // ...then the catalog rotates it out AND a reclaim is attempted (simulating a concurrent load)
70
- await p.publishCatalog((await p.readCatalog()).catalogRevision, []); // retire g1
71
- await p.forGroup('g1').discardGeneration(snap.revision); // reclaim attempt — DEFERRED (leased)
72
- // the in-flight reader's query STILL succeeds (no read-under-delete):
73
- assert.equal((await backend.queryRevision(snap.revision, [1, 0, 0], 1)).length, 1);
74
- // release the lease → the deferred reclaim now runs
75
- backend.release(snap.revision);
76
- await assert.rejects(() => backend.queryRevision(snap.revision, [1, 0, 0], 1), /unknown generation/i);
77
- });
78
- test('an ACTIVE generation is retained; dropCollection only reclaims a NON-active one', async () => {
79
- const p = makeInMemoryStoreProvider();
80
- const g = await p.forGroup('g1').beginGeneration();
81
- await p._seed(g.generation, [rec('a', 'g1', [1, 0, 0])]);
82
- const r0 = (await p.readCatalog()).catalogRevision;
83
- await p.publishCatalog(r0, [
84
- {
85
- collection: { group: 'g1', description: 'd', collection: 'g1' },
86
- sources: ['s'],
87
- generation: g.generation,
88
- manifest: MANIFEST,
89
- },
90
- ]);
91
- // dropCollection on an ACTIVE collection must NOT delete its served generation
92
- await p.dropCollection('g1');
93
- assert.equal((await p.forGroup('g1').queryRevision(g.generation, [1, 0, 0], 1)).length, 1);
94
- // now retire g1 from the catalog (publish WITHOUT it) → the generation becomes non-active
95
- const r1 = (await p.readCatalog()).catalogRevision;
96
- await p.publishCatalog(r1, []);
97
- await p.dropCollection('g1'); // reclaims the now-non-active generation
98
- await assert.rejects(() => p.forGroup('g1').queryRevision(g.generation, [1, 0, 0], 1), /unknown generation|not found/i);
99
- });
100
- //# sourceMappingURL=in-memory-store.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"in-memory-store.test.js","sourceRoot":"","sources":["../../../src/skills/plugin-host/in-memory-store.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=marketplace-adapter.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"marketplace-adapter.test.d.ts","sourceRoot":"","sources":["../../../src/skills/plugin-host/marketplace-adapter.test.ts"],"names":[],"mappings":""}
@@ -1,46 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { test } from 'node:test';
3
- import { buildIngestResult } from './marketplace-adapter.js';
4
- const SKILL_MD = `---\nname: do-thing\ndescription: How to do the thing\n---\n# Do Thing\n## Step\nrun it`;
5
- test('manifest + SKILL.md → records placed one-group-per-plugin + catalog', () => {
6
- const res = buildIngestResult({
7
- source: 'vendor',
8
- plugins: [
9
- {
10
- plugin: 'p1',
11
- version: '1',
12
- skills: [{ skill: 'do-thing', skillMd: SKILL_MD }],
13
- },
14
- ],
15
- chunk: { maxChars: 1000 },
16
- placement: (plugin) => ({ group: plugin, description: `plugin ${plugin}` }),
17
- });
18
- assert.deepEqual(res.collections.map((c) => c.group), ['p1']);
19
- assert.equal(res.collections[0].description, 'plugin p1');
20
- assert.ok(res.records.length >= 1);
21
- assert.ok(res.records.every((r) => r.group === 'p1'));
22
- const groups = new Set(res.collections.map((c) => c.group));
23
- assert.ok(res.records.every((r) => groups.has(r.group)));
24
- });
25
- test('a strategy may bundle plugins into one group', () => {
26
- const res = buildIngestResult({
27
- source: 'vendor',
28
- plugins: [
29
- {
30
- plugin: 'a',
31
- version: '1',
32
- skills: [{ skill: 's', skillMd: SKILL_MD }],
33
- },
34
- {
35
- plugin: 'b',
36
- version: '1',
37
- skills: [{ skill: 's', skillMd: SKILL_MD }],
38
- },
39
- ],
40
- chunk: { maxChars: 1000 },
41
- placement: () => ({ group: 'bundle', description: 'bundle' }),
42
- });
43
- assert.deepEqual(res.collections.map((c) => c.group), ['bundle']);
44
- assert.ok(res.records.every((r) => r.group === 'bundle'));
45
- });
46
- //# sourceMappingURL=marketplace-adapter.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"marketplace-adapter.test.js","sourceRoot":"","sources":["../../../src/skills/plugin-host/marketplace-adapter.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=qdrant-store.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"qdrant-store.test.d.ts","sourceRoot":"","sources":["../../../src/skills/plugin-host/qdrant-store.test.ts"],"names":[],"mappings":""}
@@ -1,396 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { test } from 'node:test';
3
- import { CatalogCasError } from '@mcp-abap-adt/llm-agent';
4
- import { makeInProcessCatalogStore, makePgCatalogStore, makeQdrantBackendProvider, makeQdrantStoreProvider, pointId, } from './qdrant-store.js';
5
- const MANIFEST = {
6
- embeddingSpaceId: 'sp',
7
- dimension: 3,
8
- retrievalSchemaVersion: 1,
9
- };
10
- // deterministic 3-dim embed (like the other tests): hash text → unit-ish vector
11
- const embed = async (text) => {
12
- let a = 0;
13
- let b = 0;
14
- let c = 0;
15
- for (let i = 0; i < text.length; i++) {
16
- const code = text.charCodeAt(i);
17
- if (i % 3 === 0)
18
- a += code;
19
- else if (i % 3 === 1)
20
- b += code;
21
- else
22
- c += code;
23
- }
24
- return [a, b, c];
25
- };
26
- const rec = (id, group = 'g1', sourceId = 's') => ({
27
- id,
28
- sourceId,
29
- group,
30
- name: id,
31
- retrievalText: id,
32
- content: `c-${id}`,
33
- provenance: id,
34
- });
35
- function eq(a, b) {
36
- return JSON.stringify(a) === JSON.stringify(b);
37
- }
38
- function makeMockClient() {
39
- const points = new Map();
40
- const matches = (p, filter) => {
41
- if (filter.generation !== undefined &&
42
- p.payload.generation !== filter.generation)
43
- return false;
44
- if (Array.isArray(filter.sourceId)) {
45
- if (!filter.sourceId.includes(p.payload.sourceId))
46
- return false;
47
- }
48
- return true;
49
- };
50
- const cosine = (x, y) => {
51
- let dot = 0;
52
- let nx = 0;
53
- let ny = 0;
54
- for (let i = 0; i < x.length; i++) {
55
- dot += x[i] * y[i];
56
- nx += x[i] * x[i];
57
- ny += y[i] * y[i];
58
- }
59
- return nx && ny ? dot / (Math.sqrt(nx) * Math.sqrt(ny)) : 0;
60
- };
61
- return {
62
- _points: points,
63
- async upsertPoints(pts) {
64
- for (const p of pts)
65
- points.set(p.id, { ...p });
66
- },
67
- async deleteByFilter(filter) {
68
- for (const [id, p] of [...points.entries()]) {
69
- if (matches(p, filter))
70
- points.delete(id);
71
- }
72
- },
73
- async search(filter, vector, k) {
74
- return [...points.values()]
75
- .filter((p) => matches(p, filter))
76
- .map((p) => ({ payload: p.payload, score: cosine(vector, p.vector) }))
77
- .sort((a, b) => b.score - a.score)
78
- .slice(0, k);
79
- },
80
- async scroll(filter, cursor) {
81
- const all = [...points.values()].filter((p) => matches(p, filter));
82
- // paginate in pages of 2 to exercise the cursor loop
83
- const start = cursor ? Number(cursor) : 0;
84
- const page = all.slice(start, start + 2);
85
- const nextStart = start + 2;
86
- return {
87
- points: page.map((p) => ({
88
- id: p.id,
89
- vector: p.vector,
90
- payload: p.payload,
91
- })),
92
- next: nextStart < all.length ? String(nextStart) : undefined,
93
- };
94
- },
95
- };
96
- }
97
- const fixedNow = () => 1_000_000;
98
- function makeProvider(client, opts = {}) {
99
- return makeQdrantStoreProvider({
100
- client,
101
- collection: 'skills',
102
- catalogStore: opts.catalogStore ?? makeInProcessCatalogStore(),
103
- embed,
104
- pointId,
105
- now: opts.now ?? fixedNow,
106
- retiredGraceMs: opts.retiredGraceMs ?? 30_000,
107
- orphanGraceMs: opts.orphanGraceMs ?? 3_600_000,
108
- });
109
- }
110
- test('point ids are deterministic UUIDv5 of generation:recordId', () => {
111
- const a = pointId('g0', 'r1');
112
- const b = pointId('g0', 'r1');
113
- const c = pointId('g1', 'r1');
114
- assert.equal(a, b);
115
- assert.notEqual(a, c);
116
- assert.match(a, /^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
117
- });
118
- test('build inactive → publishCatalog (store-generated rev) → queryRevision by gen → activeSnapshot from catalog', async () => {
119
- const client = makeMockClient();
120
- const p = makeProvider(client);
121
- const store = p.forGroup('g1');
122
- const { generation } = await store.beginGeneration();
123
- await store.upsert(generation, [rec('a'), rec('b')]);
124
- // nothing serves yet
125
- assert.equal(await store.activeSnapshot(), null);
126
- const before = await p.readCatalog();
127
- const entry = {
128
- collection: { group: 'g1', description: 'd', collection: 'skills' },
129
- sources: ['s'],
130
- generation,
131
- manifest: MANIFEST,
132
- };
133
- const snap = await p.publishCatalog(before.catalogRevision, [entry]);
134
- // store generated a (different) revision
135
- assert.notEqual(snap.catalogRevision, before.catalogRevision);
136
- const active = await store.activeSnapshot();
137
- assert.equal(active?.revision, generation);
138
- assert.deepEqual(active?.manifest, MANIFEST);
139
- const hits = await store.queryRevision(generation, await embed('a'), 1);
140
- assert.equal(hits[0].record.id, 'a');
141
- assert.equal(hits[0].record.content, 'c-a');
142
- assert.equal(hits[0].record.group, 'g1');
143
- });
144
- test('publishCatalog stale expected → CatalogCasError (in-process)', async () => {
145
- const client = makeMockClient();
146
- const p = makeProvider(client);
147
- const r0 = (await p.readCatalog()).catalogRevision;
148
- await p.publishCatalog(r0, []); // advances
149
- await assert.rejects(() => p.publishCatalog(r0, []), (e) => e instanceof CatalogCasError);
150
- });
151
- test('makePgCatalogStore: stale expected (fake pool UPDATE rowCount 0) → CatalogCasError', async () => {
152
- // fake pool: read returns empty initial row; UPDATE always rowCount 0 → stale
153
- const pool = {
154
- async query(sql) {
155
- if (/^select/i.test(sql.trim()))
156
- return { rows: [], rowCount: 0 };
157
- if (/^insert/i.test(sql.trim()))
158
- return { rows: [], rowCount: 1 };
159
- // UPDATE ... WHERE revision=$expected → no match
160
- return { rows: [], rowCount: 0 };
161
- },
162
- };
163
- const store = makePgCatalogStore({ pool, table: 'skills_catalog' });
164
- await assert.rejects(() => store.casPublish('whatever', [], 123), (e) => e instanceof CatalogCasError);
165
- });
166
- test('makePgCatalogStore: successful casPublish (fake pool UPDATE rowCount 1) returns committed snapshot', async () => {
167
- let stored = {
168
- revision: 'r-init',
169
- snapshot: { catalogRevision: 'r-init', entries: [], retired: [] },
170
- };
171
- const pool = {
172
- async query(sql, params) {
173
- const s = sql.trim();
174
- if (/^select/i.test(s)) {
175
- return stored
176
- ? {
177
- rows: [{ revision: stored.revision, snapshot: stored.snapshot }],
178
- rowCount: 1,
179
- }
180
- : { rows: [], rowCount: 0 };
181
- }
182
- if (/^update/i.test(s)) {
183
- const expected = params?.[2];
184
- if (stored && stored.revision === expected) {
185
- stored = {
186
- revision: params?.[0],
187
- snapshot: params?.[1],
188
- };
189
- return { rows: [], rowCount: 1 };
190
- }
191
- return { rows: [], rowCount: 0 };
192
- }
193
- return { rows: [], rowCount: 1 };
194
- },
195
- };
196
- const store = makePgCatalogStore({ pool, table: 'skills_catalog' });
197
- const entry = {
198
- collection: { group: 'g1', description: 'd', collection: 'skills' },
199
- sources: ['s'],
200
- generation: 'g-1',
201
- manifest: MANIFEST,
202
- };
203
- const snap = await store.casPublish('r-init', [entry], 555);
204
- assert.notEqual(snap.catalogRevision, 'r-init');
205
- assert.equal(snap.entries.length, 1);
206
- const re = await store.read();
207
- assert.equal(re.catalogRevision, snap.catalogRevision);
208
- assert.equal(re.entries.length, 1);
209
- });
210
- test('carryForward SCROLLS the live generation by sourceId and re-ids with pointId(newGen, recordId)', async () => {
211
- const client = makeMockClient();
212
- const p = makeProvider(client);
213
- const store = p.forGroup('g1');
214
- const g0 = (await store.beginGeneration()).generation;
215
- await store.upsert(g0, [
216
- rec('a', 'g1', 's1'),
217
- rec('b', 'g1', 's2'),
218
- rec('c', 'g1', 's3'),
219
- ]);
220
- await p.publishCatalog((await p.readCatalog()).catalogRevision, [
221
- {
222
- collection: { group: 'g1', description: 'd', collection: 'skills' },
223
- sources: ['s1', 's2', 's3'],
224
- generation: g0,
225
- manifest: MANIFEST,
226
- },
227
- ]);
228
- // new generation carries forward only s1 and s2
229
- const g1 = (await store.beginGeneration()).generation;
230
- await store.carryForward(g1, ['s1', 's2']);
231
- // points for g1 must exist under the NEW deterministic ids
232
- assert.ok(client._points.has(pointId(g1, 'a')));
233
- assert.ok(client._points.has(pointId(g1, 'b')));
234
- assert.ok(!client._points.has(pointId(g1, 'c')));
235
- // and they carry the new generation tag
236
- assert.equal(client._points.get(pointId(g1, 'a'))?.payload.generation, g1);
237
- const hits = await store.queryRevision(g1, await embed('a'), 5);
238
- const ids = hits.map((h) => h.record.id).sort();
239
- assert.deepEqual(ids, ['a', 'b']);
240
- });
241
- test('durable retirement: a commit dropping G stamps retired[{G, retiredAt}] readable via catalog.read()', async () => {
242
- const client = makeMockClient();
243
- const catalogStore = makeInProcessCatalogStore();
244
- const p = makeProvider(client, { catalogStore, now: () => 7777 });
245
- const store = p.forGroup('g1');
246
- const g0 = (await store.beginGeneration()).generation;
247
- await store.upsert(g0, [rec('a')]);
248
- await p.publishCatalog((await p.readCatalog()).catalogRevision, [
249
- {
250
- collection: { group: 'g1', description: 'd', collection: 'skills' },
251
- sources: ['s'],
252
- generation: g0,
253
- manifest: MANIFEST,
254
- },
255
- ]);
256
- // now publish WITHOUT g0 → it is retired
257
- await p.publishCatalog((await p.readCatalog()).catalogRevision, []);
258
- const snap = await catalogStore.read();
259
- assert.ok(snap.retired?.some((r) => r.generation === g0 && r.retiredAt === 7777));
260
- assert.equal(snap.retired?.find((r) => r.generation === g0)?.group, 'g1');
261
- });
262
- test('sweep + retiredGraceMs: before grace no delete; at/after grace deletes points + pruneRetired', async () => {
263
- const client = makeMockClient();
264
- const catalogStore = makeInProcessCatalogStore();
265
- const clock = 1000;
266
- const p = makeProvider(client, {
267
- catalogStore,
268
- now: () => clock,
269
- retiredGraceMs: 30_000,
270
- });
271
- const store = p.forGroup('g1');
272
- const g0 = (await store.beginGeneration()).generation;
273
- await store.upsert(g0, [rec('a')]);
274
- await p.publishCatalog((await p.readCatalog()).catalogRevision, [
275
- {
276
- collection: { group: 'g1', description: 'd', collection: 'skills' },
277
- sources: ['s'],
278
- generation: g0,
279
- manifest: MANIFEST,
280
- },
281
- ]);
282
- // retire g0 at clock=1000
283
- await p.publishCatalog((await p.readCatalog()).catalogRevision, []);
284
- assert.ok(client._points.size > 0);
285
- // sweep BEFORE grace → no delete, retired still present
286
- await p.sweep?.(1000 + 29_999);
287
- assert.ok(client._points.size > 0);
288
- assert.ok((await catalogStore.read()).retired?.some((r) => r.generation === g0));
289
- // sweep AT/after grace → delete + prune
290
- await p.sweep?.(1000 + 30_000);
291
- assert.equal([...client._points.values()].filter((pt) => pt.payload.generation === g0)
292
- .length, 0);
293
- assert.ok(!(await catalogStore.read()).retired?.some((r) => r.generation === g0));
294
- });
295
- test('orphan reconcile age-protected: old orphan swept, young (in-build) orphan kept', async () => {
296
- const client = makeMockClient();
297
- const catalogStore = makeInProcessCatalogStore();
298
- const NOW = 10_000_000;
299
- const p = makeProvider(client, {
300
- catalogStore,
301
- now: () => NOW,
302
- orphanGraceMs: 3_600_000, // 1h
303
- });
304
- // Inject an OLD orphan generation (createdAt far in the past) — neither active nor retired.
305
- await client.upsertPoints([
306
- {
307
- id: pointId('orphan-old', 'x'),
308
- vector: [1, 0, 0],
309
- payload: {
310
- generation: 'orphan-old',
311
- group: 'g1',
312
- recordId: 'x',
313
- content: 'cx',
314
- name: 'x',
315
- provenance: 'x',
316
- sourceId: 's',
317
- createdAt: NOW - 3_600_001, // older than grace
318
- },
319
- },
320
- ]);
321
- // Inject a YOUNG orphan (concurrent in-build loader) — createdAt within grace.
322
- await client.upsertPoints([
323
- {
324
- id: pointId('orphan-young', 'y'),
325
- vector: [0, 1, 0],
326
- payload: {
327
- generation: 'orphan-young',
328
- group: 'g1',
329
- recordId: 'y',
330
- content: 'cy',
331
- name: 'y',
332
- provenance: 'y',
333
- sourceId: 's',
334
- createdAt: NOW - 1000, // younger than grace
335
- },
336
- },
337
- ]);
338
- await p.sweep?.(NOW);
339
- // old orphan swept, young orphan retained
340
- assert.equal([...client._points.values()].filter((pt) => pt.payload.generation === 'orphan-old').length, 0);
341
- assert.equal([...client._points.values()].filter((pt) => pt.payload.generation === 'orphan-young').length, 1);
342
- });
343
- test('makeQdrantBackendProvider over IQdrantReader+ICatalogReader is read-only and serves query/activeSnapshot/readCatalog', async () => {
344
- const client = makeMockClient();
345
- const catalogStore = makeInProcessCatalogStore();
346
- const p = makeProvider(client, { catalogStore });
347
- const store = p.forGroup('g1');
348
- const g0 = (await store.beginGeneration()).generation;
349
- await store.upsert(g0, [rec('a'), rec('b')]);
350
- await p.publishCatalog((await p.readCatalog()).catalogRevision, [
351
- {
352
- collection: { group: 'g1', description: 'd', collection: 'skills' },
353
- sources: ['s'],
354
- generation: g0,
355
- manifest: MANIFEST,
356
- },
357
- ]);
358
- // build a READER-ONLY provider over reader interfaces (no write methods)
359
- const reader = { search: client.search, scroll: client.scroll };
360
- const catalogReader = { read: () => catalogStore.read() };
361
- const backendProvider = makeQdrantBackendProvider({
362
- reader,
363
- catalogReader,
364
- collection: 'skills',
365
- });
366
- // compile-time read-only: the reader object has no upsert/delete
367
- assert.equal(reader.upsertPoints, undefined);
368
- assert.equal(backendProvider.publishCatalog, undefined);
369
- const cat = await backendProvider.readCatalog();
370
- assert.equal(cat.entries.length, 1);
371
- const backend = backendProvider.forGroup('g1');
372
- const active = await backend.activeSnapshot();
373
- assert.equal(active?.revision, g0);
374
- const hits = await backend.queryRevision(g0, await embed('a'), 1);
375
- assert.equal(hits[0].record.id, 'a');
376
- });
377
- test('asBackendProvider() gives an in-process read view', async () => {
378
- const client = makeMockClient();
379
- const p = makeProvider(client);
380
- const store = p.forGroup('g1');
381
- const g0 = (await store.beginGeneration()).generation;
382
- await store.upsert(g0, [rec('a')]);
383
- await p.publishCatalog((await p.readCatalog()).catalogRevision, [
384
- {
385
- collection: { group: 'g1', description: 'd', collection: 'skills' },
386
- sources: ['s'],
387
- generation: g0,
388
- manifest: MANIFEST,
389
- },
390
- ]);
391
- const bp = p.asBackendProvider();
392
- const active = await bp.forGroup('g1').activeSnapshot();
393
- assert.equal(active?.revision, g0);
394
- assert.ok(eq((await bp.readCatalog()).entries.length, 1));
395
- });
396
- //# sourceMappingURL=qdrant-store.test.js.map