@kernl-sdk/libsql 0.1.38 → 0.1.39

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 (137) hide show
  1. package/.turbo/turbo-build.log +5 -4
  2. package/CHANGELOG.md +8 -0
  3. package/README.md +225 -0
  4. package/dist/__tests__/constraints.test.d.ts +2 -0
  5. package/dist/__tests__/constraints.test.d.ts.map +1 -0
  6. package/dist/__tests__/constraints.test.js +97 -0
  7. package/dist/__tests__/helpers.d.ts +36 -0
  8. package/dist/__tests__/helpers.d.ts.map +1 -0
  9. package/dist/__tests__/helpers.js +80 -0
  10. package/dist/__tests__/memory.create-get.test.d.ts +2 -0
  11. package/dist/__tests__/memory.create-get.test.d.ts.map +1 -0
  12. package/dist/__tests__/memory.create-get.test.js +8 -0
  13. package/dist/__tests__/memory.delete.test.d.ts +2 -0
  14. package/dist/__tests__/memory.delete.test.d.ts.map +1 -0
  15. package/dist/__tests__/memory.delete.test.js +6 -0
  16. package/dist/__tests__/memory.list.test.d.ts +2 -0
  17. package/dist/__tests__/memory.list.test.d.ts.map +1 -0
  18. package/dist/__tests__/memory.list.test.js +8 -0
  19. package/dist/__tests__/memory.update.test.d.ts +2 -0
  20. package/dist/__tests__/memory.update.test.d.ts.map +1 -0
  21. package/dist/__tests__/memory.update.test.js +8 -0
  22. package/dist/__tests__/migrations.test.d.ts +2 -0
  23. package/dist/__tests__/migrations.test.d.ts.map +1 -0
  24. package/dist/__tests__/migrations.test.js +68 -0
  25. package/dist/__tests__/row-codecs.test.d.ts +2 -0
  26. package/dist/__tests__/row-codecs.test.d.ts.map +1 -0
  27. package/dist/__tests__/row-codecs.test.js +175 -0
  28. package/dist/__tests__/sql-utils.test.d.ts +2 -0
  29. package/dist/__tests__/sql-utils.test.d.ts.map +1 -0
  30. package/dist/__tests__/sql-utils.test.js +45 -0
  31. package/dist/__tests__/storage.init.test.d.ts +2 -0
  32. package/dist/__tests__/storage.init.test.d.ts.map +1 -0
  33. package/dist/__tests__/storage.init.test.js +63 -0
  34. package/dist/__tests__/thread.lifecycle.test.d.ts +2 -0
  35. package/dist/__tests__/thread.lifecycle.test.d.ts.map +1 -0
  36. package/dist/__tests__/thread.lifecycle.test.js +172 -0
  37. package/dist/__tests__/transaction.test.d.ts +2 -0
  38. package/dist/__tests__/transaction.test.d.ts.map +1 -0
  39. package/dist/__tests__/transaction.test.js +16 -0
  40. package/dist/__tests__/utils.test.d.ts +2 -0
  41. package/dist/__tests__/utils.test.d.ts.map +1 -0
  42. package/dist/__tests__/utils.test.js +31 -0
  43. package/dist/client.d.ts +46 -0
  44. package/dist/client.d.ts.map +1 -0
  45. package/dist/client.js +46 -0
  46. package/dist/index.d.ts +5 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +5 -1
  49. package/dist/memory/__tests__/create-get.test.d.ts +2 -0
  50. package/dist/memory/__tests__/create-get.test.d.ts.map +1 -0
  51. package/dist/memory/__tests__/create-get.test.js +126 -0
  52. package/dist/memory/__tests__/delete.test.d.ts +2 -0
  53. package/dist/memory/__tests__/delete.test.d.ts.map +1 -0
  54. package/dist/memory/__tests__/delete.test.js +96 -0
  55. package/dist/memory/__tests__/list.test.d.ts +2 -0
  56. package/dist/memory/__tests__/list.test.d.ts.map +1 -0
  57. package/dist/memory/__tests__/list.test.js +168 -0
  58. package/dist/memory/__tests__/sql.test.d.ts +2 -0
  59. package/dist/memory/__tests__/sql.test.d.ts.map +1 -0
  60. package/dist/memory/__tests__/sql.test.js +159 -0
  61. package/dist/memory/__tests__/update.test.d.ts +2 -0
  62. package/dist/memory/__tests__/update.test.d.ts.map +1 -0
  63. package/dist/memory/__tests__/update.test.js +113 -0
  64. package/dist/memory/row.d.ts +11 -0
  65. package/dist/memory/row.d.ts.map +1 -0
  66. package/dist/memory/row.js +29 -0
  67. package/dist/memory/sql.d.ts +34 -0
  68. package/dist/memory/sql.d.ts.map +1 -0
  69. package/dist/memory/sql.js +109 -0
  70. package/dist/memory/store.d.ts +41 -0
  71. package/dist/memory/store.d.ts.map +1 -0
  72. package/dist/memory/store.js +132 -0
  73. package/dist/migrations.d.ts +32 -0
  74. package/dist/migrations.d.ts.map +1 -0
  75. package/dist/migrations.js +157 -0
  76. package/dist/sql.d.ts +28 -0
  77. package/dist/sql.d.ts.map +1 -0
  78. package/dist/sql.js +22 -0
  79. package/dist/storage.d.ts +75 -0
  80. package/dist/storage.d.ts.map +1 -0
  81. package/dist/storage.js +123 -0
  82. package/dist/thread/__tests__/append.test.d.ts +2 -0
  83. package/dist/thread/__tests__/append.test.d.ts.map +1 -0
  84. package/dist/thread/__tests__/append.test.js +141 -0
  85. package/dist/thread/__tests__/history.test.d.ts +2 -0
  86. package/dist/thread/__tests__/history.test.d.ts.map +1 -0
  87. package/dist/thread/__tests__/history.test.js +146 -0
  88. package/dist/thread/__tests__/sql.test.d.ts +2 -0
  89. package/dist/thread/__tests__/sql.test.d.ts.map +1 -0
  90. package/dist/thread/__tests__/sql.test.js +129 -0
  91. package/dist/thread/__tests__/store.test.d.ts +2 -0
  92. package/dist/thread/__tests__/store.test.d.ts.map +1 -0
  93. package/dist/thread/__tests__/store.test.js +170 -0
  94. package/dist/thread/row.d.ts +19 -0
  95. package/dist/thread/row.d.ts.map +1 -0
  96. package/dist/thread/row.js +65 -0
  97. package/dist/thread/sql.d.ts +33 -0
  98. package/dist/thread/sql.d.ts.map +1 -0
  99. package/dist/thread/sql.js +112 -0
  100. package/dist/thread/store.d.ts +67 -0
  101. package/dist/thread/store.d.ts.map +1 -0
  102. package/dist/thread/store.js +282 -0
  103. package/dist/utils.d.ts +10 -0
  104. package/dist/utils.d.ts.map +1 -0
  105. package/dist/utils.js +21 -0
  106. package/package.json +15 -11
  107. package/src/__tests__/constraints.test.ts +123 -0
  108. package/src/__tests__/helpers.ts +98 -0
  109. package/src/__tests__/migrations.test.ts +114 -0
  110. package/src/__tests__/row-codecs.test.ts +201 -0
  111. package/src/__tests__/sql-utils.test.ts +52 -0
  112. package/src/__tests__/storage.init.test.ts +92 -0
  113. package/src/__tests__/thread.lifecycle.test.ts +234 -0
  114. package/src/__tests__/transaction.test.ts +25 -0
  115. package/src/__tests__/utils.test.ts +38 -0
  116. package/src/client.ts +71 -0
  117. package/src/index.ts +10 -0
  118. package/src/memory/__tests__/create-get.test.ts +161 -0
  119. package/src/memory/__tests__/delete.test.ts +124 -0
  120. package/src/memory/__tests__/list.test.ts +198 -0
  121. package/src/memory/__tests__/sql.test.ts +186 -0
  122. package/src/memory/__tests__/update.test.ts +148 -0
  123. package/src/memory/row.ts +36 -0
  124. package/src/memory/sql.ts +142 -0
  125. package/src/memory/store.ts +173 -0
  126. package/src/migrations.ts +206 -0
  127. package/src/sql.ts +35 -0
  128. package/src/storage.ts +170 -0
  129. package/src/thread/__tests__/append.test.ts +201 -0
  130. package/src/thread/__tests__/history.test.ts +198 -0
  131. package/src/thread/__tests__/sql.test.ts +154 -0
  132. package/src/thread/__tests__/store.test.ts +219 -0
  133. package/src/thread/row.ts +77 -0
  134. package/src/thread/sql.ts +153 -0
  135. package/src/thread/store.ts +381 -0
  136. package/src/utils.ts +20 -0
  137. package/LICENSE +0 -201
@@ -0,0 +1,129 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { SQL_WHERE, SQL_ORDER, SQL_UPDATE } from "../sql.js";
3
+ describe("LibSQL thread SQL codecs", () => {
4
+ describe("SQL_WHERE", () => {
5
+ it("returns empty clause when no filters", () => {
6
+ const result = SQL_WHERE.encode({ filter: undefined });
7
+ expect(result.sql).toBe("");
8
+ expect(result.params).toEqual([]);
9
+ });
10
+ it("encodes namespace filter", () => {
11
+ const result = SQL_WHERE.encode({
12
+ filter: { namespace: "default" },
13
+ });
14
+ expect(result.sql).toBe("namespace = ?");
15
+ expect(result.params).toEqual(["default"]);
16
+ });
17
+ it("encodes agentId filter", () => {
18
+ const result = SQL_WHERE.encode({
19
+ filter: { agentId: "agent-1" },
20
+ });
21
+ expect(result.sql).toBe("agent_id = ?");
22
+ expect(result.params).toEqual(["agent-1"]);
23
+ });
24
+ it("encodes single state filter", () => {
25
+ const result = SQL_WHERE.encode({
26
+ filter: { state: "stopped" },
27
+ });
28
+ expect(result.sql).toBe("state = ?");
29
+ expect(result.params).toEqual(["stopped"]);
30
+ });
31
+ it("encodes state array filter with IN clause", () => {
32
+ const result = SQL_WHERE.encode({
33
+ filter: { state: ["stopped", "running", "interruptible"] },
34
+ });
35
+ expect(result.sql).toBe("state IN (?, ?, ?)");
36
+ expect(result.params).toEqual(["stopped", "running", "interruptible"]);
37
+ });
38
+ it("encodes parentTaskId filter", () => {
39
+ const result = SQL_WHERE.encode({
40
+ filter: { parentTaskId: "task-1" },
41
+ });
42
+ expect(result.sql).toBe("parent_task_id = ?");
43
+ expect(result.params).toEqual(["task-1"]);
44
+ });
45
+ it("encodes date range filters", () => {
46
+ const after = new Date("2024-01-01");
47
+ const before = new Date("2024-12-31");
48
+ const result = SQL_WHERE.encode({
49
+ filter: { createdAfter: after, createdBefore: before },
50
+ });
51
+ // Uses > and < not >= and <=
52
+ expect(result.sql).toBe("created_at > ? AND created_at < ?");
53
+ expect(result.params).toEqual([after.getTime(), before.getTime()]);
54
+ });
55
+ it("combines multiple filters with AND", () => {
56
+ const result = SQL_WHERE.encode({
57
+ filter: {
58
+ namespace: "default",
59
+ agentId: "agent-1",
60
+ state: "stopped",
61
+ },
62
+ });
63
+ // Order depends on filter processing order, check contains
64
+ expect(result.sql).toContain("namespace = ?");
65
+ expect(result.sql).toContain("agent_id = ?");
66
+ expect(result.sql).toContain("state = ?");
67
+ expect(result.sql).toContain(" AND ");
68
+ expect(result.params).toContain("default");
69
+ expect(result.params).toContain("agent-1");
70
+ expect(result.params).toContain("stopped");
71
+ });
72
+ });
73
+ describe("SQL_ORDER", () => {
74
+ it("returns default ordering", () => {
75
+ const result = SQL_ORDER.encode({ order: undefined });
76
+ expect(result).toBe("created_at DESC");
77
+ });
78
+ it("encodes createdAt asc", () => {
79
+ const result = SQL_ORDER.encode({ order: { createdAt: "asc" } });
80
+ expect(result).toBe("created_at ASC");
81
+ });
82
+ it("encodes createdAt desc", () => {
83
+ const result = SQL_ORDER.encode({ order: { createdAt: "desc" } });
84
+ expect(result).toBe("created_at DESC");
85
+ });
86
+ it("encodes updatedAt asc", () => {
87
+ const result = SQL_ORDER.encode({ order: { updatedAt: "asc" } });
88
+ expect(result).toBe("updated_at ASC");
89
+ });
90
+ it("encodes updatedAt desc", () => {
91
+ const result = SQL_ORDER.encode({ order: { updatedAt: "desc" } });
92
+ expect(result).toBe("updated_at DESC");
93
+ });
94
+ });
95
+ describe("SQL_UPDATE", () => {
96
+ it("encodes tick update", () => {
97
+ const result = SQL_UPDATE.encode({ patch: { tick: 5 } });
98
+ expect(result.sql).toContain("tick = ?");
99
+ expect(result.params).toContain(5);
100
+ });
101
+ it("encodes state update", () => {
102
+ const result = SQL_UPDATE.encode({ patch: { state: "running" } });
103
+ expect(result.sql).toContain("state = ?");
104
+ expect(result.params).toContain("running");
105
+ });
106
+ it("encodes metadata update with JSON stringify", () => {
107
+ const metadata = { title: "Test" };
108
+ const result = SQL_UPDATE.encode({ patch: { metadata } });
109
+ expect(result.sql).toContain("metadata = ?");
110
+ expect(result.params).toContain(JSON.stringify(metadata));
111
+ });
112
+ it("always includes updated_at", () => {
113
+ const before = Date.now();
114
+ const result = SQL_UPDATE.encode({ patch: { tick: 1 } });
115
+ const after = Date.now();
116
+ expect(result.sql).toContain("updated_at = ?");
117
+ const updatedAt = result.params.find((p) => typeof p === "number" && p >= before && p <= after);
118
+ expect(updatedAt).toBeDefined();
119
+ });
120
+ it("combines multiple updates", () => {
121
+ const result = SQL_UPDATE.encode({
122
+ patch: { tick: 3, state: "interruptible" },
123
+ });
124
+ expect(result.sql).toContain("tick = ?");
125
+ expect(result.sql).toContain("state = ?");
126
+ expect(result.sql).toContain("updated_at = ?");
127
+ });
128
+ });
129
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=store.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../../../src/thread/__tests__/store.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,170 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { message } from "@kernl-sdk/protocol";
3
+ import { create_client, create_storage, create_mock_registries, testid, } from "../../__tests__/helpers.js";
4
+ /** Create a message ThreadEvent */
5
+ function evt(id, tid, seq, timestamp) {
6
+ return {
7
+ ...message({ role: "user", text: `msg-${seq}` }),
8
+ id,
9
+ tid,
10
+ seq,
11
+ timestamp,
12
+ metadata: {},
13
+ };
14
+ }
15
+ describe("LibSQLThreadStore", () => {
16
+ let client;
17
+ let storage;
18
+ beforeEach(async () => {
19
+ client = create_client();
20
+ storage = create_storage(client);
21
+ storage.bind(create_mock_registries());
22
+ await storage.memories.list(); // init
23
+ });
24
+ afterEach(() => {
25
+ client.close();
26
+ });
27
+ it("inserts and gets a thread", async () => {
28
+ const tid = testid("thread");
29
+ const inserted = await storage.threads.insert({
30
+ id: tid,
31
+ namespace: "default",
32
+ agentId: "test-agent",
33
+ model: "test/model",
34
+ context: { userId: "user-1" },
35
+ metadata: { title: "Test" },
36
+ });
37
+ expect(inserted.tid).toBe(tid);
38
+ expect(inserted.namespace).toBe("default");
39
+ const found = await storage.threads.get(tid);
40
+ expect(found).not.toBeNull();
41
+ expect(found?.tid).toBe(tid);
42
+ expect(found?.metadata).toEqual({ title: "Test" });
43
+ });
44
+ it("returns null for non-existent thread", async () => {
45
+ const found = await storage.threads.get("nonexistent");
46
+ expect(found).toBeNull();
47
+ });
48
+ it("updates thread fields and metadata", async () => {
49
+ const tid = testid("thread");
50
+ await storage.threads.insert({
51
+ id: tid,
52
+ namespace: "default",
53
+ agentId: "test-agent",
54
+ model: "test/model",
55
+ });
56
+ const updated = await storage.threads.update(tid, {
57
+ tick: 5,
58
+ state: "running",
59
+ metadata: { title: "Updated", score: 100 },
60
+ });
61
+ expect(updated._tick).toBe(5);
62
+ expect(updated.state).toBe("running");
63
+ expect(updated.metadata).toEqual({ title: "Updated", score: 100 });
64
+ // Verify persisted
65
+ const found = await storage.threads.get(tid);
66
+ expect(found?._tick).toBe(5);
67
+ expect(found?.state).toBe("running");
68
+ });
69
+ it("lists threads with filters and pagination", async () => {
70
+ // Create multiple threads
71
+ await storage.threads.insert({
72
+ id: testid("t1"),
73
+ namespace: "ns1",
74
+ agentId: "test-agent",
75
+ model: "test/model",
76
+ state: "stopped",
77
+ });
78
+ await storage.threads.insert({
79
+ id: testid("t2"),
80
+ namespace: "ns1",
81
+ agentId: "test-agent",
82
+ model: "test/model",
83
+ state: "running",
84
+ });
85
+ await storage.threads.insert({
86
+ id: testid("t3"),
87
+ namespace: "ns2",
88
+ agentId: "test-agent",
89
+ model: "test/model",
90
+ state: "stopped",
91
+ });
92
+ // Filter by namespace
93
+ const ns1Threads = await storage.threads.list({
94
+ filter: { namespace: "ns1" },
95
+ });
96
+ expect(ns1Threads.length).toBe(2);
97
+ // Filter by state
98
+ const stoppedThreads = await storage.threads.list({
99
+ filter: { state: "stopped" },
100
+ });
101
+ expect(stoppedThreads.length).toBe(2);
102
+ // Filter by state array
103
+ const activeThreads = await storage.threads.list({
104
+ filter: { state: ["stopped", "running"] },
105
+ });
106
+ expect(activeThreads.length).toBe(3);
107
+ // Pagination
108
+ const page1 = await storage.threads.list({ limit: 2 });
109
+ expect(page1.length).toBe(2);
110
+ const page2 = await storage.threads.list({ limit: 2, offset: 2 });
111
+ expect(page2.length).toBe(1);
112
+ });
113
+ it("orders threads by createdAt", async () => {
114
+ const t1 = testid("t1");
115
+ const t2 = testid("t2");
116
+ await storage.threads.insert({
117
+ id: t1,
118
+ namespace: "default",
119
+ agentId: "test-agent",
120
+ model: "test/model",
121
+ });
122
+ // Small delay to ensure different timestamps
123
+ await new Promise((r) => setTimeout(r, 10));
124
+ await storage.threads.insert({
125
+ id: t2,
126
+ namespace: "default",
127
+ agentId: "test-agent",
128
+ model: "test/model",
129
+ });
130
+ const asc = await storage.threads.list({
131
+ order: { createdAt: "asc" },
132
+ });
133
+ expect(asc[0].tid).toBe(t1);
134
+ expect(asc[1].tid).toBe(t2);
135
+ const desc = await storage.threads.list({
136
+ order: { createdAt: "desc" },
137
+ });
138
+ expect(desc[0].tid).toBe(t2);
139
+ expect(desc[1].tid).toBe(t1);
140
+ });
141
+ it("deletes thread", async () => {
142
+ const tid = testid("thread");
143
+ await storage.threads.insert({
144
+ id: tid,
145
+ namespace: "default",
146
+ agentId: "test-agent",
147
+ model: "test/model",
148
+ });
149
+ // Add some events
150
+ await storage.threads.append([evt("evt-1", tid, 1, new Date())]);
151
+ await storage.threads.delete(tid);
152
+ const found = await storage.threads.get(tid);
153
+ expect(found).toBeNull();
154
+ // Events should also be deleted (cascade)
155
+ const history = await storage.threads.history(tid);
156
+ expect(history.length).toBe(0);
157
+ });
158
+ it("returns null when agent/model not in registry", async () => {
159
+ const tid = testid("thread");
160
+ // Insert with non-existent agent
161
+ await client.execute({
162
+ sql: `INSERT INTO kernl_threads (id, namespace, agent_id, model, context, tick, state, created_at, updated_at)
163
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
164
+ args: [tid, "default", "unknown-agent", "unknown/model", "{}", 0, "idle", Date.now(), Date.now()],
165
+ });
166
+ // Get should return null (graceful degradation)
167
+ const found = await storage.threads.get(tid);
168
+ expect(found).toBeNull();
169
+ });
170
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * LibSQL row codecs for thread data.
3
+ */
4
+ import type { Row } from "@libsql/client";
5
+ import type { Codec } from "@kernl-sdk/shared/lib";
6
+ import type { ThreadRecord, ThreadEventRecord } from "@kernl-sdk/storage";
7
+ /**
8
+ * Codec for converting LibSQL rows to ThreadRecord.
9
+ */
10
+ export declare const RowToThreadRecord: Codec<Row, ThreadRecord>;
11
+ /**
12
+ * Codec for converting LibSQL rows (from JOIN query) to ThreadEventRecord.
13
+ */
14
+ export declare const RowToEventRecord: Codec<Row, ThreadEventRecord>;
15
+ /**
16
+ * Codec for converting LibSQL rows to ThreadEventRecord (direct query).
17
+ */
18
+ export declare const RowToEventRecordDirect: Codec<Row, ThreadEventRecord>;
19
+ //# sourceMappingURL=row.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"row.d.ts","sourceRoot":"","sources":["../../src/thread/row.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAI1E;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,GAAG,EAAE,YAAY,CAoBtD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,GAAG,EAAE,iBAAiB,CAgB1D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,GAAG,EAAE,iBAAiB,CAgBhE,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * LibSQL row codecs for thread data.
3
+ */
4
+ import { parsejson } from "../utils.js";
5
+ /**
6
+ * Codec for converting LibSQL rows to ThreadRecord.
7
+ */
8
+ export const RowToThreadRecord = {
9
+ encode(row) {
10
+ return {
11
+ id: row.id,
12
+ namespace: row.namespace,
13
+ agent_id: row.agent_id,
14
+ model: row.model,
15
+ context: parsejson(row.context) ?? {},
16
+ tick: row.tick,
17
+ state: row.state,
18
+ parent_task_id: row.parent_task_id,
19
+ metadata: parsejson(row.metadata),
20
+ created_at: row.created_at,
21
+ updated_at: row.updated_at,
22
+ };
23
+ },
24
+ decode() {
25
+ throw new Error("RowToThreadRecord.decode not implemented");
26
+ },
27
+ };
28
+ /**
29
+ * Codec for converting LibSQL rows (from JOIN query) to ThreadEventRecord.
30
+ */
31
+ export const RowToEventRecord = {
32
+ encode(row) {
33
+ return {
34
+ id: row.event_id,
35
+ tid: row.event_tid,
36
+ seq: row.seq,
37
+ kind: row.event_kind,
38
+ timestamp: Number(row.timestamp),
39
+ data: parsejson(row.data),
40
+ metadata: parsejson(row.event_metadata),
41
+ };
42
+ },
43
+ decode() {
44
+ throw new Error("RowToEventRecord.decode not implemented");
45
+ },
46
+ };
47
+ /**
48
+ * Codec for converting LibSQL rows to ThreadEventRecord (direct query).
49
+ */
50
+ export const RowToEventRecordDirect = {
51
+ encode(row) {
52
+ return {
53
+ id: row.id,
54
+ tid: row.tid,
55
+ seq: row.seq,
56
+ kind: row.kind,
57
+ timestamp: Number(row.timestamp),
58
+ data: parsejson(row.data),
59
+ metadata: parsejson(row.metadata),
60
+ };
61
+ },
62
+ decode() {
63
+ throw new Error("RowToEventRecordDirect.decode not implemented");
64
+ },
65
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Thread SQL conversion codecs for LibSQL.
3
+ *
4
+ * Uses ? placeholders instead of PostgreSQL's $1, $2, etc.
5
+ */
6
+ import type { ThreadFilter, ThreadUpdate, SortOrder } from "kernl";
7
+ import type { Codec } from "@kernl-sdk/shared/lib";
8
+ import { type SQLClause } from "../sql.js";
9
+ export interface WhereInput {
10
+ filter?: ThreadFilter;
11
+ }
12
+ /**
13
+ * Encode ThreadFilter to SQL WHERE clause with ? placeholders.
14
+ */
15
+ export declare const SQL_WHERE: Codec<WhereInput, SQLClause>;
16
+ export interface OrderInput {
17
+ order?: {
18
+ createdAt?: SortOrder;
19
+ updatedAt?: SortOrder;
20
+ };
21
+ }
22
+ /**
23
+ * Encode order options to SQL ORDER BY clause.
24
+ */
25
+ export declare const SQL_ORDER: Codec<OrderInput, string>;
26
+ export interface UpdateInput {
27
+ patch: ThreadUpdate;
28
+ }
29
+ /**
30
+ * Encode ThreadUpdate to SQL SET clause with ? placeholders.
31
+ */
32
+ export declare const SQL_UPDATE: Codec<UpdateInput, SQLClause>;
33
+ //# sourceMappingURL=sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/thread/sql.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACnE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,QAAQ,CAAC;AAErD,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,UAAU,EAAE,SAAS,CAsDlD,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE;QACN,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,SAAS,CAAC,EAAE,SAAS,CAAC;KACvB,CAAC;CACH;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,UAAU,EAAE,MAAM,CAqB/C,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,YAAY,CAAC;CACrB;AAED;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,WAAW,EAAE,SAAS,CAsCpD,CAAC"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Thread SQL conversion codecs for LibSQL.
3
+ *
4
+ * Uses ? placeholders instead of PostgreSQL's $1, $2, etc.
5
+ */
6
+ import { expandarray } from "../sql.js";
7
+ /**
8
+ * Encode ThreadFilter to SQL WHERE clause with ? placeholders.
9
+ */
10
+ export const SQL_WHERE = {
11
+ encode({ filter }) {
12
+ if (!filter) {
13
+ return { sql: "", params: [] };
14
+ }
15
+ const conditions = [];
16
+ const params = [];
17
+ if (filter.namespace !== undefined) {
18
+ conditions.push(`namespace = ?`);
19
+ params.push(filter.namespace);
20
+ }
21
+ if (filter.state !== undefined) {
22
+ if (Array.isArray(filter.state)) {
23
+ const { placeholders, params: stateParams } = expandarray(filter.state);
24
+ conditions.push(`state IN (${placeholders})`);
25
+ params.push(...stateParams);
26
+ }
27
+ else {
28
+ conditions.push(`state = ?`);
29
+ params.push(filter.state);
30
+ }
31
+ }
32
+ if (filter.agentId !== undefined) {
33
+ conditions.push(`agent_id = ?`);
34
+ params.push(filter.agentId);
35
+ }
36
+ if (filter.parentTaskId !== undefined) {
37
+ conditions.push(`parent_task_id = ?`);
38
+ params.push(filter.parentTaskId);
39
+ }
40
+ if (filter.createdAfter !== undefined) {
41
+ conditions.push(`created_at > ?`);
42
+ params.push(filter.createdAfter.getTime());
43
+ }
44
+ if (filter.createdBefore !== undefined) {
45
+ conditions.push(`created_at < ?`);
46
+ params.push(filter.createdBefore.getTime());
47
+ }
48
+ return {
49
+ sql: conditions.length > 0 ? conditions.join(" AND ") : "",
50
+ params,
51
+ };
52
+ },
53
+ decode() {
54
+ throw new Error("SQL_WHERE.decode not implemented");
55
+ },
56
+ };
57
+ /**
58
+ * Encode order options to SQL ORDER BY clause.
59
+ */
60
+ export const SQL_ORDER = {
61
+ encode({ order }) {
62
+ const clauses = [];
63
+ if (order?.createdAt) {
64
+ clauses.push(`created_at ${order.createdAt.toUpperCase()}`);
65
+ }
66
+ if (order?.updatedAt) {
67
+ clauses.push(`updated_at ${order.updatedAt.toUpperCase()}`);
68
+ }
69
+ if (clauses.length === 0) {
70
+ return "created_at DESC";
71
+ }
72
+ return clauses.join(", ");
73
+ },
74
+ decode() {
75
+ throw new Error("SQL_ORDER.decode not implemented");
76
+ },
77
+ };
78
+ /**
79
+ * Encode ThreadUpdate to SQL SET clause with ? placeholders.
80
+ */
81
+ export const SQL_UPDATE = {
82
+ encode({ patch }) {
83
+ const sets = [];
84
+ const params = [];
85
+ if (patch.tick !== undefined) {
86
+ sets.push(`tick = ?`);
87
+ params.push(patch.tick);
88
+ }
89
+ if (patch.state !== undefined) {
90
+ sets.push(`state = ?`);
91
+ params.push(patch.state);
92
+ }
93
+ if (patch.context !== undefined) {
94
+ sets.push(`context = ?`);
95
+ params.push(JSON.stringify(patch.context.context));
96
+ }
97
+ if (patch.metadata !== undefined) {
98
+ sets.push(`metadata = ?`);
99
+ params.push(patch.metadata ? JSON.stringify(patch.metadata) : null);
100
+ }
101
+ // always update updated_at
102
+ sets.push(`updated_at = ?`);
103
+ params.push(Date.now());
104
+ return {
105
+ sql: sets.join(", "),
106
+ params,
107
+ };
108
+ },
109
+ decode() {
110
+ throw new Error("SQL_UPDATE.decode not implemented");
111
+ },
112
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * LibSQL Thread store implementation.
3
+ */
4
+ import type { Client } from "@libsql/client";
5
+ import { type IAgentRegistry, type IModelRegistry, type ThreadStore, type NewThread, type ThreadUpdate, type ThreadInclude, type ThreadListOptions, type ThreadHistoryOptions } from "kernl";
6
+ import { Thread, type ThreadEvent } from "kernl/internal";
7
+ import { type ThreadRecord } from "@kernl-sdk/storage";
8
+ /**
9
+ * LibSQL Thread store implementation.
10
+ *
11
+ * All async methods call `ensureInit()` before database operations
12
+ * to ensure schema/tables exist.
13
+ */
14
+ export declare class LibSQLThreadStore implements ThreadStore {
15
+ private db;
16
+ private registries;
17
+ private ensureInit;
18
+ constructor(db: Client, ensureInit: () => Promise<void>);
19
+ /**
20
+ * Bind runtime registries for hydrating Thread instances.
21
+ */
22
+ bind(registries: {
23
+ agents: IAgentRegistry;
24
+ models: IModelRegistry;
25
+ }): void;
26
+ /**
27
+ * Get a thread by id.
28
+ */
29
+ get(tid: string, include?: ThreadInclude): Promise<Thread | null>;
30
+ /**
31
+ * List threads matching the filter.
32
+ */
33
+ list(options?: ThreadListOptions): Promise<Thread[]>;
34
+ /**
35
+ * Insert a new thread into the store.
36
+ */
37
+ insert(thread: NewThread): Promise<Thread>;
38
+ /**
39
+ * Update thread runtime state.
40
+ */
41
+ update(tid: string, patch: ThreadUpdate): Promise<Thread>;
42
+ /**
43
+ * Delete a thread and cascade to thread_events.
44
+ */
45
+ delete(tid: string): Promise<void>;
46
+ /**
47
+ * Get the event history for a thread.
48
+ */
49
+ history(tid: string, opts?: ThreadHistoryOptions): Promise<ThreadEvent[]>;
50
+ /**
51
+ * Append events to the thread history.
52
+ *
53
+ * Semantics:
54
+ * - Guaranteed per-thread ordering via monotonically increasing `seq`
55
+ * - Idempotent on `(tid, event.id)`: duplicate ids MUST NOT create duplicate rows
56
+ * - Events maintain insertion order
57
+ */
58
+ append(events: ThreadEvent[]): Promise<void>;
59
+ /**
60
+ * Hydrate a Thread instance from a database record.
61
+ */
62
+ hydrate(thread: {
63
+ record: ThreadRecord;
64
+ events?: ThreadEvent[];
65
+ }): Thread;
66
+ }
67
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/thread/store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAW,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AAc5B;;;;;GAKG;AACH,qBAAa,iBAAkB,YAAW,WAAW;IACnD,OAAO,CAAC,EAAE,CAAS;IACnB,OAAO,CAAC,UAAU,CAA4D;IAC9E,OAAO,CAAC,UAAU,CAAsB;gBAE5B,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAMvD;;OAEG;IACH,IAAI,CAAC,UAAU,EAAE;QAAE,MAAM,EAAE,cAAc,CAAC;QAAC,MAAM,EAAE,cAAc,CAAA;KAAE,GAAG,IAAI;IAI1E;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAqFvE;;OAEG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAsC1D;;OAEG;IACG,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;IA4BhD;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAiB/D;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxC;;OAEG;IACG,OAAO,CACX,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,oBAAoB,GAC1B,OAAO,CAAC,WAAW,EAAE,CAAC;IAoCzB;;;;;;;OAOG;IACG,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAgClD;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,YAAY,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,EAAE,CAAA;KAAE,GAAG,MAAM;CAyC1E"}