@axiom-lattice/pg-stores 3.3.4 → 3.3.5

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @axiom-lattice/pg-stores@3.3.4 build /home/runner/work/agentic/agentic/packages/pg-stores
2
+ > @axiom-lattice/pg-stores@3.3.5 build /home/runner/work/agentic/agentic/packages/pg-stores
3
3
  > tsup src/index.ts --format cjs,esm --dts --sourcemap
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -10,11 +10,11 @@
10
10
  ESM Build start
11
11
  CJS dist/index.js 443.09 KB
12
12
  CJS dist/index.js.map 807.74 KB
13
- CJS ⚡️ Build success in 1017ms
13
+ CJS ⚡️ Build success in 694ms
14
14
  ESM dist/index.mjs 434.40 KB
15
15
  ESM dist/index.mjs.map 807.39 KB
16
- ESM ⚡️ Build success in 1084ms
16
+ ESM ⚡️ Build success in 695ms
17
17
  DTS Build start
18
- DTS ⚡️ Build success in 14116ms
18
+ DTS ⚡️ Build success in 10825ms
19
19
  DTS dist/index.d.ts 93.36 KB
20
20
  DTS dist/index.d.mts 93.36 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @axiom-lattice/pg-stores
2
2
 
3
+ ## 3.3.5
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [cb75780]
8
+ - Updated dependencies [c0f75e2]
9
+ - Updated dependencies [48e3ce3]
10
+ - Updated dependencies [3e923d0]
11
+ - Updated dependencies [99bf609]
12
+ - Updated dependencies [92bffbb]
13
+ - Updated dependencies [a19e0aa]
14
+ - @axiom-lattice/core@6.4.1
15
+
3
16
  ## 3.3.4
4
17
 
5
18
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiom-lattice/pg-stores",
3
- "version": "3.3.4",
3
+ "version": "3.3.5",
4
4
  "description": "PG stores implementation for Axiom Lattice framework",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -25,7 +25,7 @@
25
25
  "@langchain/core": "1.1.30",
26
26
  "pg": "^8.16.3",
27
27
  "uuid": "^9.0.1",
28
- "@axiom-lattice/core": "6.4.0",
28
+ "@axiom-lattice/core": "6.4.1",
29
29
  "@axiom-lattice/protocols": "4.8.0"
30
30
  },
31
31
  "devDependencies": {
@@ -0,0 +1,74 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { Pool } from "pg";
3
+ import { PostgreSQLProjectRoomMessageStore } from "../stores/PostgreSQLProjectRoomMessageStore";
4
+ import { MigrationManager } from "../migrations/migration";
5
+ import { createProjectRoomTables } from "../migrations/project_room_migration";
6
+
7
+ const databaseUrl = process.env.TEST_DATABASE_URL;
8
+ const describeIfDatabase = databaseUrl ? describe : describe.skip;
9
+
10
+ /**
11
+ * Real-PostgreSQL contract for the source-less correlated interrupt write path
12
+ * used by Project Task HITL cards: a bot-authored message with no human source
13
+ * (`reply_to_message_id` NULL) that is idempotent on the caller key.
14
+ */
15
+ describeIfDatabase("PostgreSQLProjectRoomMessageStore correlated interrupt", () => {
16
+ let pool: Pool;
17
+ let store: PostgreSQLProjectRoomMessageStore;
18
+ const tenantId = `correlated-interrupt-${randomUUID()}`;
19
+
20
+ beforeAll(async () => {
21
+ pool = new Pool({ connectionString: databaseUrl });
22
+ const mm = new MigrationManager(pool);
23
+ mm.register(createProjectRoomTables);
24
+ await mm.migrate();
25
+ store = new PostgreSQLProjectRoomMessageStore({ pool });
26
+ });
27
+
28
+ afterEach(async () => {
29
+ await pool.query("DELETE FROM lattice_project_room_messages WHERE tenant_id = $1", [tenantId]);
30
+ });
31
+
32
+ afterAll(async () => {
33
+ await pool.end();
34
+ });
35
+
36
+ it("round-trips a source-less bot interrupt without a reply target", async () => {
37
+ const candidate = {
38
+ id: randomUUID(), tenantId, workspaceId: "w1", projectId: "p1", roomId: "r1",
39
+ author: { type: "bot" as const, membershipId: "m1", assistantId: "a1" },
40
+ content: { type: "text" as const, text: "```clarify\n{}\n```" }, mentions: [],
41
+ source: "agent" as const, sourceId: "interrupt:task:t1:run1", idempotencyKey: "room-interrupt:m1:task:t1:run1",
42
+ };
43
+
44
+ const first = await store.createIdempotent(candidate);
45
+ expect(first.author).toEqual({ type: "bot", membershipId: "m1", assistantId: "a1" });
46
+ expect(first.source).toBe("agent");
47
+ expect(first.sourceId).toBe("interrupt:task:t1:run1");
48
+ expect(first.replyToMessageId).toBeUndefined();
49
+ expect(first.idempotencyKey).toBe("room-interrupt:m1:task:t1:run1");
50
+
51
+ const reloaded = await store.findById(tenantId, first.id);
52
+ expect(reloaded).toMatchObject({ id: first.id, source: "agent", sourceId: "interrupt:task:t1:run1" });
53
+ expect(reloaded?.replyToMessageId).toBeUndefined();
54
+ });
55
+
56
+ it("is idempotent on the caller correlation key", async () => {
57
+ const base = {
58
+ tenantId, workspaceId: "w1", projectId: "p1", roomId: "r1",
59
+ author: { type: "bot" as const, membershipId: "m1", assistantId: "a1" },
60
+ content: { type: "text" as const, text: "```clarify\n{}\n```" }, mentions: [],
61
+ source: "agent" as const, sourceId: "interrupt:task:t2:run1", idempotencyKey: "room-interrupt:m1:task:t2:run1",
62
+ };
63
+
64
+ const first = await store.createIdempotent({ ...base, id: randomUUID() });
65
+ const second = await store.createIdempotent({ ...base, id: randomUUID() });
66
+ expect(second.id).toBe(first.id);
67
+
68
+ const rows = await pool.query<{ count: string }>(
69
+ "SELECT COUNT(*)::text AS count FROM lattice_project_room_messages WHERE tenant_id = $1 AND idempotency_key = $2",
70
+ [tenantId, base.idempotencyKey],
71
+ );
72
+ expect(rows.rows[0]?.count).toBe("1");
73
+ });
74
+ });
@@ -139,4 +139,27 @@ describe("PostgreSQLProjectRoomMessageStore", () => {
139
139
  await store.deleteChatOlderThan({ before: now, limit: 99_999 });
140
140
  expect(query.mock.calls[0][1]).toEqual([now, 1_000]);
141
141
  });
142
+
143
+ it("maps a source-less bot interrupt with a NULL reply target and caller idempotency", async () => {
144
+ const botAuthor = { type: "bot", membershipId: "m1", assistantId: "a1" };
145
+ query.mockResolvedValueOnce({
146
+ rows: [{
147
+ ...row, author: botAuthor, content: { type: "text", text: "```clarify\n{}\n```" },
148
+ mentions: [], reply_to_message_id: null, source: "agent",
149
+ source_id: "interrupt:task:t1:run1", idempotency_key: "room-interrupt:m1:task:t1:run1",
150
+ }],
151
+ });
152
+ const store = new PostgreSQLProjectRoomMessageStore({ pool });
153
+ await store.createIdempotent({
154
+ id: "candidate", tenantId: "tenant-1", workspaceId: "workspace-1", projectId: "project-1", roomId: "room-1",
155
+ author: botAuthor, content: { type: "text", text: "```clarify\n{}\n```" }, mentions: [],
156
+ source: "agent", sourceId: "interrupt:task:t1:run1", idempotencyKey: "room-interrupt:m1:task:t1:run1",
157
+ });
158
+ const params = query.mock.calls[0][1] as unknown[];
159
+ expect(params[8]).toBeNull();
160
+ expect(params[5]).toBe(JSON.stringify(botAuthor));
161
+ expect(params[9]).toBe("agent");
162
+ expect(params[10]).toBe("interrupt:task:t1:run1");
163
+ expect(params[11]).toBe("room-interrupt:m1:task:t1:run1");
164
+ });
142
165
  });