kernl 0.2.1 → 0.6.0

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 (267) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-check-types.log +4 -0
  3. package/CHANGELOG.md +138 -0
  4. package/LICENSE +1 -1
  5. package/dist/agent/__tests__/concurrency.test.d.ts +2 -0
  6. package/dist/agent/__tests__/concurrency.test.d.ts.map +1 -0
  7. package/dist/agent/__tests__/concurrency.test.js +152 -0
  8. package/dist/agent/__tests__/run.test.d.ts +2 -0
  9. package/dist/agent/__tests__/run.test.d.ts.map +1 -0
  10. package/dist/agent/__tests__/run.test.js +357 -0
  11. package/dist/agent/index.d.ts +1 -0
  12. package/dist/agent/index.d.ts.map +1 -0
  13. package/dist/agent.d.ts +32 -9
  14. package/dist/agent.d.ts.map +1 -1
  15. package/dist/agent.js +101 -14
  16. package/dist/api/__tests__/cursor-page.test.d.ts +2 -0
  17. package/dist/api/__tests__/cursor-page.test.d.ts.map +1 -0
  18. package/dist/api/__tests__/cursor-page.test.js +414 -0
  19. package/dist/api/__tests__/offset-page.test.d.ts +2 -0
  20. package/dist/api/__tests__/offset-page.test.d.ts.map +1 -0
  21. package/dist/api/__tests__/offset-page.test.js +510 -0
  22. package/dist/api/__tests__/threads.test.d.ts +2 -0
  23. package/dist/api/__tests__/threads.test.d.ts.map +1 -0
  24. package/dist/api/__tests__/threads.test.js +338 -0
  25. package/dist/api/models/index.d.ts +2 -0
  26. package/dist/api/models/index.d.ts.map +1 -0
  27. package/dist/api/models/thread.d.ts +120 -0
  28. package/dist/api/models/thread.d.ts.map +1 -0
  29. package/dist/api/pagination/base.d.ts +48 -0
  30. package/dist/api/pagination/base.d.ts.map +1 -0
  31. package/dist/api/pagination/base.js +45 -0
  32. package/dist/api/pagination/cursor.d.ts +44 -0
  33. package/dist/api/pagination/cursor.d.ts.map +1 -0
  34. package/dist/api/pagination/cursor.js +52 -0
  35. package/dist/api/pagination/offset.d.ts +42 -0
  36. package/dist/api/pagination/offset.d.ts.map +1 -0
  37. package/dist/api/pagination/offset.js +55 -0
  38. package/dist/api/resources/threads/events.d.ts +21 -0
  39. package/dist/api/resources/threads/events.d.ts.map +1 -0
  40. package/dist/api/resources/threads/events.js +24 -0
  41. package/dist/api/resources/threads/index.d.ts +4 -0
  42. package/dist/api/resources/threads/index.d.ts.map +1 -0
  43. package/dist/api/resources/threads/index.js +2 -0
  44. package/dist/api/resources/threads/threads.d.ts +57 -0
  45. package/dist/api/resources/threads/threads.d.ts.map +1 -0
  46. package/dist/api/resources/threads/threads.js +199 -0
  47. package/dist/api/resources/threads/types.d.ts +123 -0
  48. package/dist/api/resources/threads/types.d.ts.map +1 -0
  49. package/dist/api/resources/threads/utils.d.ts +18 -0
  50. package/dist/api/resources/threads/utils.d.ts.map +1 -0
  51. package/dist/api/resources/threads/utils.js +78 -0
  52. package/dist/context.d.ts +5 -1
  53. package/dist/context.d.ts.map +1 -1
  54. package/dist/context.js +6 -1
  55. package/dist/index.d.ts +9 -1
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +7 -0
  58. package/dist/internal.d.ts +4 -0
  59. package/dist/internal.d.ts.map +1 -0
  60. package/dist/internal.js +2 -0
  61. package/dist/kernl/index.d.ts +3 -0
  62. package/dist/kernl/index.d.ts.map +1 -0
  63. package/dist/kernl/index.js +2 -0
  64. package/dist/kernl/kernl.d.ts +64 -0
  65. package/dist/kernl/kernl.d.ts.map +1 -0
  66. package/dist/kernl/kernl.js +116 -0
  67. package/dist/kernl/threads.d.ts +110 -0
  68. package/dist/kernl/threads.d.ts.map +1 -0
  69. package/dist/kernl/threads.js +126 -0
  70. package/dist/kernl.d.ts +22 -6
  71. package/dist/kernl.d.ts.map +1 -1
  72. package/dist/kernl.js +73 -10
  73. package/dist/lib/env.d.ts +3 -3
  74. package/dist/lib/env.js +1 -1
  75. package/dist/mcp/__tests__/integration.test.js +8 -8
  76. package/dist/mcp/__tests__/utils.test.js +6 -6
  77. package/dist/mcp/http.d.ts +1 -1
  78. package/dist/mcp/http.d.ts.map +1 -1
  79. package/dist/mcp/http.js +9 -9
  80. package/dist/mcp/sse.d.ts +1 -1
  81. package/dist/mcp/sse.d.ts.map +1 -1
  82. package/dist/mcp/sse.js +7 -7
  83. package/dist/mcp/utils.d.ts +1 -1
  84. package/dist/mcp/utils.d.ts.map +1 -1
  85. package/dist/mcp/utils.js +4 -5
  86. package/dist/storage/__tests__/in-memory.test.d.ts +2 -0
  87. package/dist/storage/__tests__/in-memory.test.d.ts.map +1 -0
  88. package/dist/storage/__tests__/in-memory.test.js +455 -0
  89. package/dist/storage/base.d.ts +64 -0
  90. package/dist/storage/base.d.ts.map +1 -0
  91. package/dist/storage/base.js +4 -0
  92. package/dist/storage/in-memory.d.ts +62 -0
  93. package/dist/storage/in-memory.d.ts.map +1 -0
  94. package/dist/storage/in-memory.js +283 -0
  95. package/dist/storage/index.d.ts +10 -0
  96. package/dist/storage/index.d.ts.map +1 -0
  97. package/dist/storage/index.js +7 -0
  98. package/dist/storage/thread.d.ts +123 -0
  99. package/dist/storage/thread.d.ts.map +1 -0
  100. package/dist/storage/thread.js +4 -0
  101. package/dist/task.d.ts +5 -3
  102. package/dist/task.d.ts.map +1 -1
  103. package/dist/task.js +10 -8
  104. package/dist/thread/__tests__/fixtures/mock-model.d.ts +1 -2
  105. package/dist/thread/__tests__/fixtures/mock-model.d.ts.map +1 -1
  106. package/dist/thread/__tests__/integration.test.js +6 -6
  107. package/dist/thread/__tests__/namespace.test.d.ts +2 -0
  108. package/dist/thread/__tests__/namespace.test.d.ts.map +1 -0
  109. package/dist/thread/__tests__/namespace.test.js +131 -0
  110. package/dist/thread/__tests__/thread-persistence.test.d.ts +2 -0
  111. package/dist/thread/__tests__/thread-persistence.test.d.ts.map +1 -0
  112. package/dist/thread/__tests__/thread-persistence.test.js +351 -0
  113. package/dist/thread/__tests__/thread.test.js +49 -51
  114. package/dist/thread/thread.d.ts +70 -18
  115. package/dist/thread/thread.d.ts.map +1 -1
  116. package/dist/thread/thread.js +211 -73
  117. package/dist/thread/utils.d.ts +36 -8
  118. package/dist/thread/utils.d.ts.map +1 -1
  119. package/dist/thread/utils.js +52 -8
  120. package/dist/tool/__tests__/fixtures.js +1 -1
  121. package/dist/tool/__tests__/toolkit.test.js +15 -12
  122. package/dist/tool/tool.js +3 -3
  123. package/dist/types/kernl.d.ts +42 -0
  124. package/dist/types/kernl.d.ts.map +1 -0
  125. package/dist/types/thread.d.ts +108 -22
  126. package/dist/types/thread.d.ts.map +1 -1
  127. package/dist/types/thread.js +12 -0
  128. package/package.json +11 -7
  129. package/src/agent/__tests__/concurrency.test.ts +194 -0
  130. package/src/agent/__tests__/run.test.ts +441 -0
  131. package/src/agent/index.ts +0 -0
  132. package/src/agent.ts +139 -24
  133. package/src/api/__tests__/cursor-page.test.ts +512 -0
  134. package/src/api/__tests__/offset-page.test.ts +624 -0
  135. package/src/api/__tests__/threads.test.ts +415 -0
  136. package/src/api/models/index.ts +6 -0
  137. package/src/api/models/thread.ts +138 -0
  138. package/src/api/pagination/base.ts +79 -0
  139. package/src/api/pagination/cursor.ts +86 -0
  140. package/src/api/pagination/offset.ts +89 -0
  141. package/src/api/resources/threads/events.ts +26 -0
  142. package/src/api/resources/threads/index.ts +9 -0
  143. package/src/api/resources/threads/threads.ts +256 -0
  144. package/src/api/resources/threads/types.ts +143 -0
  145. package/src/api/resources/threads/utils.ts +104 -0
  146. package/src/context.ts +10 -1
  147. package/src/index.ts +49 -1
  148. package/src/internal.ts +15 -0
  149. package/src/kernl.ts +86 -17
  150. package/src/mcp/__tests__/integration.test.ts +8 -9
  151. package/src/mcp/__tests__/utils.test.ts +6 -6
  152. package/src/mcp/http.ts +9 -9
  153. package/src/mcp/sse.ts +7 -7
  154. package/src/mcp/utils.ts +6 -5
  155. package/src/storage/__tests__/in-memory.test.ts +534 -0
  156. package/src/storage/base.ts +77 -0
  157. package/src/storage/in-memory.ts +372 -0
  158. package/src/storage/index.ts +21 -0
  159. package/src/storage/thread.ts +141 -0
  160. package/src/task.ts +12 -10
  161. package/src/thread/__tests__/fixtures/mock-model.ts +2 -4
  162. package/src/thread/__tests__/integration.test.ts +13 -12
  163. package/src/thread/__tests__/namespace.test.ts +158 -0
  164. package/src/thread/__tests__/thread-persistence.test.ts +367 -0
  165. package/src/thread/__tests__/thread.test.ts +52 -54
  166. package/src/thread/thread.ts +247 -96
  167. package/src/thread/utils.ts +76 -13
  168. package/src/tool/__tests__/fixtures.ts +1 -1
  169. package/src/tool/__tests__/toolkit.test.ts +15 -12
  170. package/src/tool/tool.ts +3 -3
  171. package/src/types/kernl.ts +51 -0
  172. package/src/types/thread.ts +139 -25
  173. package/vitest.config.ts +1 -0
  174. package/dist/env.d.ts +0 -45
  175. package/dist/env.d.ts.map +0 -1
  176. package/dist/env.js +0 -31
  177. package/dist/error.d.ts +0 -1
  178. package/dist/error.d.ts.map +0 -1
  179. package/dist/kernel.d.ts +0 -7
  180. package/dist/kernel.d.ts.map +0 -1
  181. package/dist/kernel.js +0 -7
  182. package/dist/lib/serde/__tests__/codec.test.d.ts +0 -2
  183. package/dist/lib/serde/__tests__/codec.test.d.ts.map +0 -1
  184. package/dist/lib/serde/__tests__/codec.test.js +0 -75
  185. package/dist/lib/serde/codec.d.ts +0 -12
  186. package/dist/lib/serde/codec.d.ts.map +0 -1
  187. package/dist/lib/serde/codec.js +0 -54
  188. package/dist/lib/serde/thread.d.ts +0 -1
  189. package/dist/lib/serde/thread.d.ts.map +0 -1
  190. package/dist/lib/serde/thread.js +0 -172
  191. package/dist/lib/serde/tool.d.ts +0 -36
  192. package/dist/lib/serde/tool.d.ts.map +0 -1
  193. package/dist/lib/utils.d.ts +0 -19
  194. package/dist/lib/utils.d.ts.map +0 -1
  195. package/dist/lib/utils.js +0 -41
  196. package/dist/logger.d.ts +0 -36
  197. package/dist/logger.d.ts.map +0 -1
  198. package/dist/logger.js +0 -43
  199. package/dist/mcp/__tests__/fixtures/echo-server.d.ts +0 -3
  200. package/dist/mcp/__tests__/fixtures/echo-server.d.ts.map +0 -1
  201. package/dist/mcp/__tests__/fixtures/echo-server.js +0 -92
  202. package/dist/mcp/__tests__/fixtures/math-server.d.ts +0 -3
  203. package/dist/mcp/__tests__/fixtures/math-server.d.ts.map +0 -1
  204. package/dist/mcp/__tests__/fixtures/math-server.js +0 -98
  205. package/dist/mcp/__tests__/fixtures/test-server.d.ts +0 -3
  206. package/dist/mcp/__tests__/fixtures/test-server.d.ts.map +0 -1
  207. package/dist/mcp/__tests__/fixtures/test-server.js +0 -163
  208. package/dist/mcp/__tests__/test-utils.d.ts +0 -17
  209. package/dist/mcp/__tests__/test-utils.d.ts.map +0 -1
  210. package/dist/mcp/__tests__/test-utils.js +0 -42
  211. package/dist/mcp/node.d.ts +0 -60
  212. package/dist/mcp/node.d.ts.map +0 -1
  213. package/dist/mcp/node.js +0 -297
  214. package/dist/model.d.ts +0 -175
  215. package/dist/model.d.ts.map +0 -1
  216. package/dist/providers/ai.d.ts +0 -1
  217. package/dist/providers/ai.d.ts.map +0 -1
  218. package/dist/providers/ai.js +0 -1
  219. package/dist/providers/default.d.ts +0 -16
  220. package/dist/providers/default.d.ts.map +0 -1
  221. package/dist/providers/default.js +0 -17
  222. package/dist/providers/registry.d.ts +0 -1
  223. package/dist/providers/registry.d.ts.map +0 -1
  224. package/dist/providers/registry.js +0 -1
  225. package/dist/sched/scheduler.d.ts +0 -20
  226. package/dist/sched/scheduler.d.ts.map +0 -1
  227. package/dist/sched/task.d.ts +0 -92
  228. package/dist/sched/task.d.ts.map +0 -1
  229. package/dist/sched/task.js +0 -102
  230. package/dist/serde/__tests__/codec.test.d.ts +0 -2
  231. package/dist/serde/__tests__/codec.test.d.ts.map +0 -1
  232. package/dist/serde/__tests__/codec.test.js +0 -75
  233. package/dist/serde/codec.d.ts +0 -12
  234. package/dist/serde/codec.d.ts.map +0 -1
  235. package/dist/serde/codec.js +0 -54
  236. package/dist/serde/json.d.ts +0 -8
  237. package/dist/serde/json.d.ts.map +0 -1
  238. package/dist/serde/json.js +0 -13
  239. package/dist/serde/thread.d.ts +0 -687
  240. package/dist/serde/thread.d.ts.map +0 -1
  241. package/dist/serde/thread.js +0 -158
  242. package/dist/serde/tool.d.ts +0 -36
  243. package/dist/serde/tool.d.ts.map +0 -1
  244. package/dist/session.d.ts +0 -1
  245. package/dist/session.d.ts.map +0 -1
  246. package/dist/session.js +0 -1
  247. package/dist/thread/__tests__/stream.test.d.ts +0 -2
  248. package/dist/thread/__tests__/stream.test.d.ts.map +0 -1
  249. package/dist/thread/__tests__/stream.test.js +0 -244
  250. package/dist/tool/mcp.d.ts +0 -75
  251. package/dist/tool/mcp.d.ts.map +0 -1
  252. package/dist/tool/mcp.js +0 -111
  253. package/dist/tools.d.ts +0 -362
  254. package/dist/tools.d.ts.map +0 -1
  255. package/dist/tools.js +0 -220
  256. package/dist/types/proto.d.ts +0 -1551
  257. package/dist/types/proto.d.ts.map +0 -1
  258. package/dist/types/proto.js +0 -531
  259. package/dist/usage.d.ts +0 -43
  260. package/dist/usage.d.ts.map +0 -1
  261. package/dist/usage.js +0 -61
  262. package/src/lib/serde/thread.ts +0 -188
  263. /package/dist/{error.js → agent/index.js} +0 -0
  264. /package/dist/{lib/serde/tool.js → api/models/index.js} +0 -0
  265. /package/dist/{model.js → api/models/thread.js} +0 -0
  266. /package/dist/{sched/scheduler.js → api/resources/threads/types.js} +0 -0
  267. /package/dist/{serde/tool.js → types/kernl.js} +0 -0
@@ -0,0 +1,455 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import { STOPPED, RUNNING, DEAD, IN_PROGRESS } from "@kernl-sdk/protocol";
3
+ import { InMemoryStorage, InMemoryThreadStore } from "../in-memory";
4
+ import { Agent } from "../../agent";
5
+ import { Thread } from "../../thread";
6
+ import { createMockModel } from "../../thread/__tests__/fixtures/mock-model";
7
+ describe("InMemoryThreadStore", () => {
8
+ let store;
9
+ let agent;
10
+ let model;
11
+ beforeEach(() => {
12
+ store = new InMemoryThreadStore();
13
+ model = createMockModel(async () => ({
14
+ content: [
15
+ {
16
+ kind: "message",
17
+ id: "msg_1",
18
+ role: "assistant",
19
+ content: [{ kind: "text", text: "Hello" }],
20
+ },
21
+ ],
22
+ finishReason: "stop",
23
+ usage: { inputTokens: 1, outputTokens: 1, totalTokens: 2 },
24
+ warnings: [],
25
+ }));
26
+ agent = new Agent({
27
+ id: "test-agent",
28
+ name: "Test Agent",
29
+ instructions: "Test instructions",
30
+ model,
31
+ });
32
+ // Bind registries
33
+ const agents = new Map([[agent.id, agent]]);
34
+ const models = new Map([[`${model.provider}/${model.modelId}`, model]]);
35
+ store.bind({ agents, models });
36
+ });
37
+ describe("insert", () => {
38
+ it("should insert a new thread", async () => {
39
+ const newThread = {
40
+ id: "thread-1",
41
+ namespace: "test",
42
+ agentId: agent.id,
43
+ model: `${model.provider}/${model.modelId}`,
44
+ context: { foo: "bar" },
45
+ };
46
+ const thread = await store.insert(newThread);
47
+ expect(thread).toBeInstanceOf(Thread);
48
+ expect(thread.tid).toBe("thread-1");
49
+ expect(thread._tick).toBe(0);
50
+ expect(thread.state).toBe(STOPPED);
51
+ });
52
+ it("should use provided defaults", async () => {
53
+ const createdAt = new Date("2024-01-01");
54
+ const newThread = {
55
+ id: "thread-2",
56
+ namespace: "test",
57
+ agentId: agent.id,
58
+ model: `${model.provider}/${model.modelId}`,
59
+ tick: 5,
60
+ state: RUNNING,
61
+ createdAt,
62
+ };
63
+ const thread = await store.insert(newThread);
64
+ expect(thread._tick).toBe(5);
65
+ expect(thread.state).toBe(RUNNING);
66
+ });
67
+ });
68
+ describe("get", () => {
69
+ it("should get a thread by id", async () => {
70
+ const newThread = {
71
+ id: "thread-1",
72
+ namespace: "test",
73
+ agentId: agent.id,
74
+ model: `${model.provider}/${model.modelId}`,
75
+ };
76
+ await store.insert(newThread);
77
+ const thread = await store.get("thread-1");
78
+ expect(thread).not.toBeNull();
79
+ expect(thread?.tid).toBe("thread-1");
80
+ });
81
+ it("should return null for non-existent thread", async () => {
82
+ const thread = await store.get("non-existent");
83
+ expect(thread).toBeNull();
84
+ });
85
+ it("should include history when requested", async () => {
86
+ const newThread = {
87
+ id: "thread-1",
88
+ namespace: "test",
89
+ agentId: agent.id,
90
+ model: `${model.provider}/${model.modelId}`,
91
+ };
92
+ await store.insert(newThread);
93
+ const events = [
94
+ {
95
+ kind: "message",
96
+ id: "msg-1",
97
+ tid: "thread-1",
98
+ seq: 0,
99
+ timestamp: new Date(),
100
+ role: "user",
101
+ content: [{ kind: "text", text: "Hello" }],
102
+ metadata: {},
103
+ },
104
+ ];
105
+ await store.append(events);
106
+ const thread = await store.get("thread-1", { history: true });
107
+ expect(thread).not.toBeNull();
108
+ // Access private history for testing
109
+ const history = thread.history;
110
+ expect(history).toHaveLength(1);
111
+ expect(history[0].id).toBe("msg-1");
112
+ });
113
+ });
114
+ describe("update", () => {
115
+ it("should update thread state", async () => {
116
+ const newThread = {
117
+ id: "thread-1",
118
+ namespace: "test",
119
+ agentId: agent.id,
120
+ model: `${model.provider}/${model.modelId}`,
121
+ };
122
+ await store.insert(newThread);
123
+ const updated = await store.update("thread-1", {
124
+ state: DEAD,
125
+ tick: 10,
126
+ });
127
+ expect(updated.state).toBe(DEAD);
128
+ expect(updated._tick).toBe(10);
129
+ });
130
+ it("should throw on non-existent thread", async () => {
131
+ await expect(store.update("non-existent", { state: DEAD })).rejects.toThrow("Thread non-existent not found");
132
+ });
133
+ });
134
+ describe("delete", () => {
135
+ it("should delete a thread and its events", async () => {
136
+ const newThread = {
137
+ id: "thread-1",
138
+ namespace: "test",
139
+ agentId: agent.id,
140
+ model: `${model.provider}/${model.modelId}`,
141
+ };
142
+ await store.insert(newThread);
143
+ await store.append([
144
+ {
145
+ kind: "message",
146
+ id: "msg-1",
147
+ tid: "thread-1",
148
+ seq: 0,
149
+ timestamp: new Date(),
150
+ role: "user",
151
+ content: [{ kind: "text", text: "Hello" }],
152
+ metadata: {},
153
+ },
154
+ ]);
155
+ await store.delete("thread-1");
156
+ const thread = await store.get("thread-1");
157
+ const history = await store.history("thread-1");
158
+ expect(thread).toBeNull();
159
+ expect(history).toHaveLength(0);
160
+ });
161
+ });
162
+ describe("append", () => {
163
+ it("should append events to thread history", async () => {
164
+ const newThread = {
165
+ id: "thread-1",
166
+ namespace: "test",
167
+ agentId: agent.id,
168
+ model: `${model.provider}/${model.modelId}`,
169
+ };
170
+ await store.insert(newThread);
171
+ const events = [
172
+ {
173
+ kind: "message",
174
+ id: "msg-1",
175
+ tid: "thread-1",
176
+ seq: 0,
177
+ timestamp: new Date(),
178
+ role: "user",
179
+ content: [{ kind: "text", text: "Hello" }],
180
+ metadata: {},
181
+ },
182
+ {
183
+ kind: "message",
184
+ id: "msg-2",
185
+ tid: "thread-1",
186
+ seq: 1,
187
+ timestamp: new Date(),
188
+ role: "assistant",
189
+ content: [{ kind: "text", text: "Hi" }],
190
+ metadata: {},
191
+ },
192
+ ];
193
+ await store.append(events);
194
+ const history = await store.history("thread-1");
195
+ expect(history).toHaveLength(2);
196
+ expect(history[0].seq).toBe(0);
197
+ expect(history[1].seq).toBe(1);
198
+ });
199
+ it("should be idempotent on event.id", async () => {
200
+ const newThread = {
201
+ id: "thread-1",
202
+ namespace: "test",
203
+ agentId: agent.id,
204
+ model: `${model.provider}/${model.modelId}`,
205
+ };
206
+ await store.insert(newThread);
207
+ const event = {
208
+ kind: "message",
209
+ id: "msg-1",
210
+ tid: "thread-1",
211
+ seq: 0,
212
+ timestamp: new Date(),
213
+ role: "user",
214
+ content: [{ kind: "text", text: "Hello" }],
215
+ metadata: {},
216
+ };
217
+ await store.append([event]);
218
+ await store.append([event]); // duplicate
219
+ const history = await store.history("thread-1");
220
+ expect(history).toHaveLength(1);
221
+ });
222
+ it("should maintain seq ordering", async () => {
223
+ const newThread = {
224
+ id: "thread-1",
225
+ namespace: "test",
226
+ agentId: agent.id,
227
+ model: `${model.provider}/${model.modelId}`,
228
+ };
229
+ await store.insert(newThread);
230
+ // Insert out of order
231
+ await store.append([
232
+ {
233
+ kind: "message",
234
+ id: "msg-2",
235
+ tid: "thread-1",
236
+ seq: 2,
237
+ timestamp: new Date(),
238
+ role: "user",
239
+ content: [{ kind: "text", text: "Second" }],
240
+ metadata: {},
241
+ },
242
+ ]);
243
+ await store.append([
244
+ {
245
+ kind: "message",
246
+ id: "msg-1",
247
+ tid: "thread-1",
248
+ seq: 1,
249
+ timestamp: new Date(),
250
+ role: "user",
251
+ content: [{ kind: "text", text: "First" }],
252
+ metadata: {},
253
+ },
254
+ ]);
255
+ const history = await store.history("thread-1");
256
+ expect(history).toHaveLength(2);
257
+ expect(history[0].seq).toBe(1);
258
+ expect(history[1].seq).toBe(2);
259
+ });
260
+ });
261
+ describe("history", () => {
262
+ beforeEach(async () => {
263
+ const newThread = {
264
+ id: "thread-1",
265
+ namespace: "test",
266
+ agentId: agent.id,
267
+ model: `${model.provider}/${model.modelId}`,
268
+ };
269
+ await store.insert(newThread);
270
+ await store.append([
271
+ {
272
+ kind: "message",
273
+ id: "msg-1",
274
+ tid: "thread-1",
275
+ seq: 0,
276
+ timestamp: new Date(),
277
+ role: "user",
278
+ content: [{ kind: "text", text: "First" }],
279
+ metadata: {},
280
+ },
281
+ {
282
+ kind: "message",
283
+ id: "msg-2",
284
+ tid: "thread-1",
285
+ seq: 1,
286
+ timestamp: new Date(),
287
+ role: "assistant",
288
+ content: [{ kind: "text", text: "Second" }],
289
+ metadata: {},
290
+ },
291
+ {
292
+ kind: "message",
293
+ id: "msg-3",
294
+ tid: "thread-1",
295
+ seq: 2,
296
+ timestamp: new Date(),
297
+ role: "user",
298
+ content: [{ kind: "text", text: "Third" }],
299
+ metadata: {},
300
+ },
301
+ ]);
302
+ });
303
+ it("should return all events by default", async () => {
304
+ const history = await store.history("thread-1");
305
+ expect(history).toHaveLength(3);
306
+ });
307
+ it("should filter by after seq", async () => {
308
+ const history = await store.history("thread-1", { after: 0 });
309
+ expect(history).toHaveLength(2);
310
+ expect(history[0].seq).toBe(1);
311
+ });
312
+ it("should filter by kinds", async () => {
313
+ await store.append([
314
+ {
315
+ kind: "tool-call",
316
+ id: "tc-1",
317
+ tid: "thread-1",
318
+ seq: 3,
319
+ timestamp: new Date(),
320
+ callId: "call-1",
321
+ toolId: "test-tool",
322
+ state: IN_PROGRESS,
323
+ arguments: "{}",
324
+ metadata: {},
325
+ },
326
+ ]);
327
+ const history = await store.history("thread-1", {
328
+ kinds: ["tool-call"],
329
+ });
330
+ expect(history).toHaveLength(1);
331
+ expect(history[0].kind).toBe("tool-call");
332
+ });
333
+ it("should apply limit", async () => {
334
+ const history = await store.history("thread-1", { limit: 2 });
335
+ expect(history).toHaveLength(2);
336
+ });
337
+ it("should support desc ordering", async () => {
338
+ const history = await store.history("thread-1", { order: "desc" });
339
+ expect(history).toHaveLength(3);
340
+ expect(history[0].seq).toBe(2);
341
+ expect(history[2].seq).toBe(0);
342
+ });
343
+ });
344
+ describe("list", () => {
345
+ beforeEach(async () => {
346
+ await store.insert({
347
+ id: "thread-1",
348
+ namespace: "test",
349
+ agentId: agent.id,
350
+ model: `${model.provider}/${model.modelId}`,
351
+ state: STOPPED,
352
+ createdAt: new Date("2024-01-01"),
353
+ });
354
+ await store.insert({
355
+ id: "thread-2",
356
+ namespace: "test",
357
+ agentId: agent.id,
358
+ model: `${model.provider}/${model.modelId}`,
359
+ state: RUNNING,
360
+ createdAt: new Date("2024-01-02"),
361
+ });
362
+ await store.insert({
363
+ id: "thread-3",
364
+ namespace: "test",
365
+ agentId: agent.id,
366
+ model: `${model.provider}/${model.modelId}`,
367
+ state: DEAD,
368
+ createdAt: new Date("2024-01-03"),
369
+ });
370
+ });
371
+ it("should list all threads", async () => {
372
+ const threads = await store.list();
373
+ expect(threads).toHaveLength(3);
374
+ });
375
+ it("should filter by state", async () => {
376
+ const threads = await store.list({
377
+ filter: { state: RUNNING },
378
+ });
379
+ expect(threads).toHaveLength(1);
380
+ expect(threads[0].tid).toBe("thread-2");
381
+ });
382
+ it("should filter by multiple states", async () => {
383
+ const threads = await store.list({
384
+ filter: { state: [STOPPED, DEAD] },
385
+ });
386
+ expect(threads).toHaveLength(2);
387
+ });
388
+ it("should filter by agentId", async () => {
389
+ const threads = await store.list({
390
+ filter: { agentId: agent.id },
391
+ });
392
+ expect(threads).toHaveLength(3);
393
+ });
394
+ it("should filter by createdAfter", async () => {
395
+ const threads = await store.list({
396
+ filter: { createdAfter: new Date("2024-01-01T12:00:00") },
397
+ });
398
+ expect(threads).toHaveLength(2);
399
+ });
400
+ it("should filter by createdBefore", async () => {
401
+ const threads = await store.list({
402
+ filter: { createdBefore: new Date("2024-01-02T12:00:00") },
403
+ });
404
+ expect(threads).toHaveLength(2);
405
+ });
406
+ it("should apply limit", async () => {
407
+ const threads = await store.list({ limit: 2 });
408
+ expect(threads).toHaveLength(2);
409
+ });
410
+ it("should apply offset", async () => {
411
+ const threads = await store.list({ offset: 1, limit: 2 });
412
+ expect(threads).toHaveLength(2);
413
+ });
414
+ it("should sort by createdAt asc", async () => {
415
+ const threads = await store.list({
416
+ order: { createdAt: "asc" },
417
+ });
418
+ expect(threads[0].tid).toBe("thread-1");
419
+ expect(threads[2].tid).toBe("thread-3");
420
+ });
421
+ it("should sort by createdAt desc (default)", async () => {
422
+ const threads = await store.list({
423
+ order: { createdAt: "desc" },
424
+ });
425
+ expect(threads[0].tid).toBe("thread-3");
426
+ expect(threads[2].tid).toBe("thread-1");
427
+ });
428
+ });
429
+ });
430
+ describe("InMemoryStorage", () => {
431
+ it("should create storage with thread store", () => {
432
+ const storage = new InMemoryStorage();
433
+ expect(storage.threads).toBeInstanceOf(InMemoryThreadStore);
434
+ });
435
+ it("should bind registries to thread store", () => {
436
+ const storage = new InMemoryStorage();
437
+ const agents = new Map();
438
+ const models = new Map();
439
+ storage.bind({ agents, models });
440
+ // Verify binding by checking thread store can hydrate (would throw if not bound)
441
+ expect(() => {
442
+ storage.threads.registries;
443
+ }).not.toThrow();
444
+ });
445
+ it("should have no-op lifecycle methods", async () => {
446
+ const storage = new InMemoryStorage();
447
+ await expect(storage.init()).resolves.toBeUndefined();
448
+ await expect(storage.close()).resolves.toBeUndefined();
449
+ await expect(storage.migrate()).resolves.toBeUndefined();
450
+ });
451
+ it("should throw on transaction", async () => {
452
+ const storage = new InMemoryStorage();
453
+ await expect(storage.transaction(async () => { })).rejects.toThrow("Transactions not supported");
454
+ });
455
+ });
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Core storage contracts.
3
+ */
4
+ import type { AgentRegistry, ModelRegistry } from "../types/kernl";
5
+ import type { ThreadStore } from "./thread";
6
+ /**
7
+ * The main storage interface for Kernl.
8
+ *
9
+ * Provides access to system stores (threads, tasks, traces) and transaction support.
10
+ */
11
+ export interface KernlStorage {
12
+ /**
13
+ * Thread store - manages thread execution records and event history.
14
+ */
15
+ threads: ThreadStore;
16
+ /**
17
+ * Bind runtime registries to storage.
18
+ *
19
+ * Called by Kernl after construction to wire up agent/model lookups.
20
+ */
21
+ bind(registries: {
22
+ agents: AgentRegistry;
23
+ models: ModelRegistry;
24
+ }): void;
25
+ /**
26
+ * Execute a function within a transaction.
27
+ *
28
+ * All operations performed using the transaction-scoped stores will be
29
+ * committed atomically or rolled back on error.
30
+ */
31
+ transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T>;
32
+ /**
33
+ * Initialize the storage backend.
34
+ *
35
+ * Connects to the database and ensures all required schemas/tables exist.
36
+ */
37
+ init(): Promise<void>;
38
+ /**
39
+ * Close the storage backend and cleanup resources.
40
+ */
41
+ close(): Promise<void>;
42
+ /**
43
+ * Runs the migrations in order to ensure all required tables exist.
44
+ */
45
+ migrate(): Promise<void>;
46
+ }
47
+ /**
48
+ * Transaction context providing transactional access to stores.
49
+ */
50
+ export interface Transaction {
51
+ /**
52
+ * Thread store within this transaction.
53
+ */
54
+ threads: ThreadStore;
55
+ /**
56
+ * Commit the transaction.
57
+ */
58
+ commit(): Promise<void>;
59
+ /**
60
+ * Rollback the transaction.
61
+ */
62
+ rollback(): Promise<void>;
63
+ }
64
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/storage/base.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,OAAO,EAAE,WAAW,CAAC;IAKrB;;;;OAIG;IACH,IAAI,CAAC,UAAU,EAAE;QAAE,MAAM,EAAE,aAAa,CAAC;QAAC,MAAM,EAAE,aAAa,CAAA;KAAE,GAAG,IAAI,CAAC;IAEzE;;;;;OAKG;IACH,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAEhE;;;;OAIG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtB;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,OAAO,EAAE,WAAW,CAAC;IAMrB;;OAEG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAExB;;OAEG;IACH,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Core storage contracts.
3
+ */
4
+ export {};
@@ -0,0 +1,62 @@
1
+ /**
2
+ * In-memory storage implementation for Kernl.
3
+ *
4
+ * Pure domain-level - no codecs, schemas, or DB records.
5
+ *
6
+ * Defined here so that it can be used as default and for testing.
7
+ */
8
+ import { Thread } from "../thread";
9
+ import type { KernlStorage, Transaction, ThreadStore, NewThread, ThreadUpdate, ThreadInclude, ThreadListOptions, ThreadHistoryOptions } from "../storage";
10
+ import type { ThreadEvent } from "../types/thread";
11
+ import type { AgentRegistry, ModelRegistry } from "../types/kernl";
12
+ /**
13
+ * In-memory storage implementation.
14
+ */
15
+ export declare class InMemoryStorage implements KernlStorage {
16
+ threads: InMemoryThreadStore;
17
+ constructor();
18
+ bind(registries: {
19
+ agents: AgentRegistry;
20
+ models: ModelRegistry;
21
+ }): void;
22
+ transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T>;
23
+ init(): Promise<void>;
24
+ close(): Promise<void>;
25
+ migrate(): Promise<void>;
26
+ }
27
+ /**
28
+ * In-memory thread store implementation.
29
+ */
30
+ export declare class InMemoryThreadStore implements ThreadStore {
31
+ private threads;
32
+ private events;
33
+ private registries;
34
+ bind(registries: {
35
+ agents: AgentRegistry;
36
+ models: ModelRegistry;
37
+ }): void;
38
+ get(tid: string, include?: ThreadInclude): Promise<Thread | null>;
39
+ list(options?: ThreadListOptions): Promise<Thread[]>;
40
+ insert(thread: NewThread): Promise<Thread>;
41
+ update(tid: string, patch: ThreadUpdate): Promise<Thread>;
42
+ delete(tid: string): Promise<void>;
43
+ history(tid: string, opts?: ThreadHistoryOptions): Promise<ThreadEvent[]>;
44
+ append(events: ThreadEvent[]): Promise<void>;
45
+ /**
46
+ * Hydrate a Thread instance from in-memory data.
47
+ */
48
+ private hydrate;
49
+ /**
50
+ * Filter and sort event history based on options.
51
+ */
52
+ private filterHistory;
53
+ /**
54
+ * Apply filters to thread list.
55
+ */
56
+ private applyFilters;
57
+ /**
58
+ * Apply sorting to thread list.
59
+ */
60
+ private applySorting;
61
+ }
62
+ //# sourceMappingURL=in-memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory.d.ts","sourceRoot":"","sources":["../../src/storage/in-memory.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAKlC,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,SAAS,EACT,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EAGrB,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,WAAW,EAAe,MAAM,gBAAgB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAElE;;GAEG;AACH,qBAAa,eAAgB,YAAW,YAAY;IAClD,OAAO,EAAE,mBAAmB,CAAC;;IAM7B,IAAI,CAAC,UAAU,EAAE;QAAE,MAAM,EAAE,aAAa,CAAC;QAAC,MAAM,EAAE,aAAa,CAAA;KAAE,GAAG,IAAI;IAIlE,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAM/D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B;AAmBD;;GAEG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACrD,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,MAAM,CAAoC;IAClD,OAAO,CAAC,UAAU,CACX;IAEP,IAAI,CAAC,UAAU,EAAE;QAAE,MAAM,EAAE,aAAa,CAAC;QAAC,MAAM,EAAE,aAAa,CAAA;KAAE,GAAG,IAAI;IAIlE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAejE,IAAI,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA2BpD,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;IAsB1C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBzD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlC,OAAO,CACX,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,oBAAoB,GAC1B,OAAO,CAAC,WAAW,EAAE,CAAC;IAInB,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BlD;;OAEG;IACH,OAAO,CAAC,OAAO;IAgCf;;OAEG;IACH,OAAO,CAAC,aAAa;IAyCrB;;OAEG;IACH,OAAO,CAAC,YAAY;IA2CpB;;OAEG;IACH,OAAO,CAAC,YAAY;CA6BrB"}