kernl 0.2.1 → 0.6.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 (292) hide show
  1. package/.turbo/turbo-build.log +5 -4
  2. package/.turbo/turbo-check-types.log +4 -0
  3. package/CHANGELOG.md +147 -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 +35 -12
  14. package/dist/agent.d.ts.map +1 -1
  15. package/dist/agent.js +102 -15
  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/guardrail.d.ts +4 -4
  56. package/dist/index.d.ts +12 -4
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +10 -3
  59. package/dist/internal.d.ts +4 -0
  60. package/dist/internal.d.ts.map +1 -0
  61. package/dist/internal.js +2 -0
  62. package/dist/kernl/index.d.ts +3 -0
  63. package/dist/kernl/index.d.ts.map +1 -0
  64. package/dist/kernl/index.js +2 -0
  65. package/dist/kernl/kernl.d.ts +64 -0
  66. package/dist/kernl/kernl.d.ts.map +1 -0
  67. package/dist/kernl/kernl.js +116 -0
  68. package/dist/kernl/threads.d.ts +110 -0
  69. package/dist/kernl/threads.d.ts.map +1 -0
  70. package/dist/kernl/threads.js +126 -0
  71. package/dist/kernl.d.ts +27 -11
  72. package/dist/kernl.d.ts.map +1 -1
  73. package/dist/kernl.js +74 -11
  74. package/dist/lib/env.d.ts +3 -3
  75. package/dist/lib/env.js +1 -1
  76. package/dist/lib/error.d.ts +3 -3
  77. package/dist/lib/logger.js +1 -1
  78. package/dist/lifecycle.d.ts +5 -5
  79. package/dist/mcp/__tests__/base.test.js +2 -2
  80. package/dist/mcp/__tests__/fixtures/utils.d.ts +1 -1
  81. package/dist/mcp/__tests__/fixtures/utils.js +1 -1
  82. package/dist/mcp/__tests__/integration.test.js +16 -16
  83. package/dist/mcp/__tests__/stdio.test.js +2 -2
  84. package/dist/mcp/__tests__/utils.test.js +8 -8
  85. package/dist/mcp/base.d.ts +2 -2
  86. package/dist/mcp/http.d.ts +3 -3
  87. package/dist/mcp/http.d.ts.map +1 -1
  88. package/dist/mcp/http.js +11 -11
  89. package/dist/mcp/sse.d.ts +3 -3
  90. package/dist/mcp/sse.d.ts.map +1 -1
  91. package/dist/mcp/sse.js +9 -9
  92. package/dist/mcp/stdio.d.ts +2 -2
  93. package/dist/mcp/stdio.js +2 -2
  94. package/dist/mcp/types.d.ts +3 -3
  95. package/dist/mcp/utils.d.ts +4 -4
  96. package/dist/mcp/utils.d.ts.map +1 -1
  97. package/dist/mcp/utils.js +5 -6
  98. package/dist/storage/__tests__/in-memory.test.d.ts +2 -0
  99. package/dist/storage/__tests__/in-memory.test.d.ts.map +1 -0
  100. package/dist/storage/__tests__/in-memory.test.js +455 -0
  101. package/dist/storage/base.d.ts +64 -0
  102. package/dist/storage/base.d.ts.map +1 -0
  103. package/dist/storage/base.js +4 -0
  104. package/dist/storage/in-memory.d.ts +62 -0
  105. package/dist/storage/in-memory.d.ts.map +1 -0
  106. package/dist/storage/in-memory.js +283 -0
  107. package/dist/storage/index.d.ts +10 -0
  108. package/dist/storage/index.d.ts.map +1 -0
  109. package/dist/storage/index.js +7 -0
  110. package/dist/storage/thread.d.ts +123 -0
  111. package/dist/storage/thread.d.ts.map +1 -0
  112. package/dist/storage/thread.js +4 -0
  113. package/dist/task.d.ts +8 -6
  114. package/dist/task.d.ts.map +1 -1
  115. package/dist/task.js +10 -8
  116. package/dist/thread/__tests__/fixtures/mock-model.d.ts +1 -2
  117. package/dist/thread/__tests__/fixtures/mock-model.d.ts.map +1 -1
  118. package/dist/thread/__tests__/integration.test.js +10 -10
  119. package/dist/thread/__tests__/mock.d.ts +1 -1
  120. package/dist/thread/__tests__/namespace.test.d.ts +2 -0
  121. package/dist/thread/__tests__/namespace.test.d.ts.map +1 -0
  122. package/dist/thread/__tests__/namespace.test.js +131 -0
  123. package/dist/thread/__tests__/thread-persistence.test.d.ts +2 -0
  124. package/dist/thread/__tests__/thread-persistence.test.d.ts.map +1 -0
  125. package/dist/thread/__tests__/thread-persistence.test.js +351 -0
  126. package/dist/thread/__tests__/thread.test.js +55 -57
  127. package/dist/thread/index.d.ts +1 -1
  128. package/dist/thread/index.js +1 -1
  129. package/dist/thread/thread.d.ts +74 -22
  130. package/dist/thread/thread.d.ts.map +1 -1
  131. package/dist/thread/thread.js +212 -74
  132. package/dist/thread/utils.d.ts +38 -10
  133. package/dist/thread/utils.d.ts.map +1 -1
  134. package/dist/thread/utils.js +53 -9
  135. package/dist/tool/__tests__/fixtures.d.ts +8 -8
  136. package/dist/tool/__tests__/fixtures.js +3 -3
  137. package/dist/tool/__tests__/tool.test.js +2 -2
  138. package/dist/tool/__tests__/toolkit.test.js +17 -14
  139. package/dist/tool/index.d.ts +3 -3
  140. package/dist/tool/index.js +2 -2
  141. package/dist/tool/tool.d.ts +2 -2
  142. package/dist/tool/tool.js +5 -5
  143. package/dist/tool/toolkit.d.ts +4 -4
  144. package/dist/tool/toolkit.js +1 -1
  145. package/dist/tool/types.d.ts +4 -4
  146. package/dist/trace/traces.js +2 -2
  147. package/dist/types/agent.d.ts +4 -4
  148. package/dist/types/kernl.d.ts +42 -0
  149. package/dist/types/kernl.d.ts.map +1 -0
  150. package/dist/types/thread.d.ts +110 -24
  151. package/dist/types/thread.d.ts.map +1 -1
  152. package/dist/types/thread.js +12 -0
  153. package/package.json +12 -8
  154. package/src/agent/__tests__/concurrency.test.ts +194 -0
  155. package/src/agent/__tests__/run.test.ts +441 -0
  156. package/src/agent/index.ts +0 -0
  157. package/src/agent.ts +139 -24
  158. package/src/api/__tests__/cursor-page.test.ts +512 -0
  159. package/src/api/__tests__/offset-page.test.ts +624 -0
  160. package/src/api/__tests__/threads.test.ts +415 -0
  161. package/src/api/models/index.ts +6 -0
  162. package/src/api/models/thread.ts +138 -0
  163. package/src/api/pagination/base.ts +79 -0
  164. package/src/api/pagination/cursor.ts +86 -0
  165. package/src/api/pagination/offset.ts +89 -0
  166. package/src/api/resources/threads/events.ts +26 -0
  167. package/src/api/resources/threads/index.ts +9 -0
  168. package/src/api/resources/threads/threads.ts +256 -0
  169. package/src/api/resources/threads/types.ts +143 -0
  170. package/src/api/resources/threads/utils.ts +104 -0
  171. package/src/context.ts +10 -1
  172. package/src/index.ts +49 -1
  173. package/src/internal.ts +15 -0
  174. package/src/kernl.ts +86 -17
  175. package/src/mcp/__tests__/integration.test.ts +8 -9
  176. package/src/mcp/__tests__/utils.test.ts +6 -6
  177. package/src/mcp/http.ts +9 -9
  178. package/src/mcp/sse.ts +7 -7
  179. package/src/mcp/utils.ts +6 -5
  180. package/src/storage/__tests__/in-memory.test.ts +534 -0
  181. package/src/storage/base.ts +77 -0
  182. package/src/storage/in-memory.ts +372 -0
  183. package/src/storage/index.ts +21 -0
  184. package/src/storage/thread.ts +141 -0
  185. package/src/task.ts +12 -10
  186. package/src/thread/__tests__/fixtures/mock-model.ts +2 -4
  187. package/src/thread/__tests__/integration.test.ts +13 -12
  188. package/src/thread/__tests__/namespace.test.ts +158 -0
  189. package/src/thread/__tests__/thread-persistence.test.ts +367 -0
  190. package/src/thread/__tests__/thread.test.ts +52 -54
  191. package/src/thread/thread.ts +247 -96
  192. package/src/thread/utils.ts +76 -13
  193. package/src/tool/__tests__/fixtures.ts +1 -1
  194. package/src/tool/__tests__/toolkit.test.ts +15 -12
  195. package/src/tool/tool.ts +3 -3
  196. package/src/types/kernl.ts +51 -0
  197. package/src/types/thread.ts +139 -25
  198. package/vitest.config.ts +1 -0
  199. package/dist/env.d.ts +0 -45
  200. package/dist/env.d.ts.map +0 -1
  201. package/dist/env.js +0 -31
  202. package/dist/error.d.ts +0 -1
  203. package/dist/error.d.ts.map +0 -1
  204. package/dist/kernel.d.ts +0 -7
  205. package/dist/kernel.d.ts.map +0 -1
  206. package/dist/kernel.js +0 -7
  207. package/dist/lib/serde/__tests__/codec.test.d.ts +0 -2
  208. package/dist/lib/serde/__tests__/codec.test.d.ts.map +0 -1
  209. package/dist/lib/serde/__tests__/codec.test.js +0 -75
  210. package/dist/lib/serde/codec.d.ts +0 -12
  211. package/dist/lib/serde/codec.d.ts.map +0 -1
  212. package/dist/lib/serde/codec.js +0 -54
  213. package/dist/lib/serde/thread.d.ts +0 -1
  214. package/dist/lib/serde/thread.d.ts.map +0 -1
  215. package/dist/lib/serde/thread.js +0 -172
  216. package/dist/lib/serde/tool.d.ts +0 -36
  217. package/dist/lib/serde/tool.d.ts.map +0 -1
  218. package/dist/lib/utils.d.ts +0 -19
  219. package/dist/lib/utils.d.ts.map +0 -1
  220. package/dist/lib/utils.js +0 -41
  221. package/dist/logger.d.ts +0 -36
  222. package/dist/logger.d.ts.map +0 -1
  223. package/dist/logger.js +0 -43
  224. package/dist/mcp/__tests__/fixtures/echo-server.d.ts +0 -3
  225. package/dist/mcp/__tests__/fixtures/echo-server.d.ts.map +0 -1
  226. package/dist/mcp/__tests__/fixtures/echo-server.js +0 -92
  227. package/dist/mcp/__tests__/fixtures/math-server.d.ts +0 -3
  228. package/dist/mcp/__tests__/fixtures/math-server.d.ts.map +0 -1
  229. package/dist/mcp/__tests__/fixtures/math-server.js +0 -98
  230. package/dist/mcp/__tests__/fixtures/test-server.d.ts +0 -3
  231. package/dist/mcp/__tests__/fixtures/test-server.d.ts.map +0 -1
  232. package/dist/mcp/__tests__/fixtures/test-server.js +0 -163
  233. package/dist/mcp/__tests__/test-utils.d.ts +0 -17
  234. package/dist/mcp/__tests__/test-utils.d.ts.map +0 -1
  235. package/dist/mcp/__tests__/test-utils.js +0 -42
  236. package/dist/mcp/node.d.ts +0 -60
  237. package/dist/mcp/node.d.ts.map +0 -1
  238. package/dist/mcp/node.js +0 -297
  239. package/dist/model.d.ts +0 -175
  240. package/dist/model.d.ts.map +0 -1
  241. package/dist/providers/ai.d.ts +0 -1
  242. package/dist/providers/ai.d.ts.map +0 -1
  243. package/dist/providers/ai.js +0 -1
  244. package/dist/providers/default.d.ts +0 -16
  245. package/dist/providers/default.d.ts.map +0 -1
  246. package/dist/providers/default.js +0 -17
  247. package/dist/providers/registry.d.ts +0 -1
  248. package/dist/providers/registry.d.ts.map +0 -1
  249. package/dist/providers/registry.js +0 -1
  250. package/dist/sched/scheduler.d.ts +0 -20
  251. package/dist/sched/scheduler.d.ts.map +0 -1
  252. package/dist/sched/task.d.ts +0 -92
  253. package/dist/sched/task.d.ts.map +0 -1
  254. package/dist/sched/task.js +0 -102
  255. package/dist/serde/__tests__/codec.test.d.ts +0 -2
  256. package/dist/serde/__tests__/codec.test.d.ts.map +0 -1
  257. package/dist/serde/__tests__/codec.test.js +0 -75
  258. package/dist/serde/codec.d.ts +0 -12
  259. package/dist/serde/codec.d.ts.map +0 -1
  260. package/dist/serde/codec.js +0 -54
  261. package/dist/serde/json.d.ts +0 -8
  262. package/dist/serde/json.d.ts.map +0 -1
  263. package/dist/serde/json.js +0 -13
  264. package/dist/serde/thread.d.ts +0 -687
  265. package/dist/serde/thread.d.ts.map +0 -1
  266. package/dist/serde/thread.js +0 -158
  267. package/dist/serde/tool.d.ts +0 -36
  268. package/dist/serde/tool.d.ts.map +0 -1
  269. package/dist/session.d.ts +0 -1
  270. package/dist/session.d.ts.map +0 -1
  271. package/dist/session.js +0 -1
  272. package/dist/thread/__tests__/stream.test.d.ts +0 -2
  273. package/dist/thread/__tests__/stream.test.d.ts.map +0 -1
  274. package/dist/thread/__tests__/stream.test.js +0 -244
  275. package/dist/tool/mcp.d.ts +0 -75
  276. package/dist/tool/mcp.d.ts.map +0 -1
  277. package/dist/tool/mcp.js +0 -111
  278. package/dist/tools.d.ts +0 -362
  279. package/dist/tools.d.ts.map +0 -1
  280. package/dist/tools.js +0 -220
  281. package/dist/types/proto.d.ts +0 -1551
  282. package/dist/types/proto.d.ts.map +0 -1
  283. package/dist/types/proto.js +0 -531
  284. package/dist/usage.d.ts +0 -43
  285. package/dist/usage.d.ts.map +0 -1
  286. package/dist/usage.js +0 -61
  287. package/src/lib/serde/thread.ts +0 -188
  288. /package/dist/{error.js → agent/index.js} +0 -0
  289. /package/dist/{lib/serde/tool.js → api/models/index.js} +0 -0
  290. /package/dist/{model.js → api/models/thread.js} +0 -0
  291. /package/dist/{sched/scheduler.js → api/resources/threads/types.js} +0 -0
  292. /package/dist/{serde/tool.js → types/kernl.js} +0 -0
@@ -0,0 +1,372 @@
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
+
9
+ import { Thread } from "@/thread";
10
+ import { Context } from "@/context";
11
+ import { STOPPED } from "@kernl-sdk/protocol";
12
+ import { UnimplementedError } from "@kernl-sdk/shared/lib";
13
+
14
+ import type {
15
+ KernlStorage,
16
+ Transaction,
17
+ ThreadStore,
18
+ NewThread,
19
+ ThreadUpdate,
20
+ ThreadInclude,
21
+ ThreadListOptions,
22
+ ThreadHistoryOptions,
23
+ ThreadFilter,
24
+ SortOrder,
25
+ } from "@/storage";
26
+ import type { ThreadEvent, ThreadState } from "@/types/thread";
27
+ import type { AgentRegistry, ModelRegistry } from "@/types/kernl";
28
+
29
+ /**
30
+ * In-memory storage implementation.
31
+ */
32
+ export class InMemoryStorage implements KernlStorage {
33
+ threads: InMemoryThreadStore;
34
+
35
+ constructor() {
36
+ this.threads = new InMemoryThreadStore();
37
+ }
38
+
39
+ bind(registries: { agents: AgentRegistry; models: ModelRegistry }): void {
40
+ this.threads.bind(registries);
41
+ }
42
+
43
+ async transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T> {
44
+ throw new UnimplementedError(
45
+ "Transactions not supported in in-memory storage",
46
+ );
47
+ }
48
+
49
+ async init(): Promise<void> {
50
+ // no-op
51
+ }
52
+
53
+ async close(): Promise<void> {
54
+ // no-op
55
+ }
56
+
57
+ async migrate(): Promise<void> {
58
+ // no-op
59
+ }
60
+ }
61
+
62
+ /**
63
+ * In-memory thread data (minimal - just what can't be reconstructed from agent/model registries)
64
+ */
65
+ interface ThreadData {
66
+ tid: string;
67
+ namespace: string;
68
+ agentId: string;
69
+ model: string; // "provider/modelId"
70
+ context: unknown;
71
+ tick: number;
72
+ state: ThreadState;
73
+ parentTaskId: string | null;
74
+ metadata: Record<string, unknown> | null;
75
+ createdAt: Date;
76
+ updatedAt: Date;
77
+ }
78
+
79
+ /**
80
+ * In-memory thread store implementation.
81
+ */
82
+ export class InMemoryThreadStore implements ThreadStore {
83
+ private threads = new Map<string, ThreadData>();
84
+ private events = new Map<string, ThreadEvent[]>(); // tid -> events
85
+ private registries: { agents: AgentRegistry; models: ModelRegistry } | null =
86
+ null;
87
+
88
+ bind(registries: { agents: AgentRegistry; models: ModelRegistry }): void {
89
+ this.registries = registries;
90
+ }
91
+
92
+ async get(tid: string, include?: ThreadInclude): Promise<Thread | null> {
93
+ const data = this.threads.get(tid);
94
+ if (!data) return null;
95
+
96
+ const history = include?.history
97
+ ? this.filterHistory(tid, include.history)
98
+ : [];
99
+
100
+ try {
101
+ return this.hydrate(data, history);
102
+ } catch (error) {
103
+ return null;
104
+ }
105
+ }
106
+
107
+ async list(options?: ThreadListOptions): Promise<Thread[]> {
108
+ let threads = Array.from(this.threads.values());
109
+
110
+ // Apply filters
111
+ if (options?.filter) {
112
+ threads = this.applyFilters(threads, options.filter);
113
+ }
114
+
115
+ // Apply sorting
116
+ threads = this.applySorting(threads, options?.order);
117
+
118
+ // Apply pagination
119
+ if (options?.offset) threads = threads.slice(options.offset);
120
+ if (options?.limit) threads = threads.slice(0, options.limit);
121
+
122
+ return threads
123
+ .map((data) => {
124
+ try {
125
+ return this.hydrate(data, []);
126
+ } catch (error) {
127
+ // Skip threads with non-existent agent/model (graceful degradation)
128
+ return null;
129
+ }
130
+ })
131
+ .filter((thread): thread is Thread => thread !== null);
132
+ }
133
+
134
+ async insert(thread: NewThread): Promise<Thread> {
135
+ const now = new Date();
136
+ const data: ThreadData = {
137
+ tid: thread.id,
138
+ namespace: thread.namespace,
139
+ agentId: thread.agentId,
140
+ model: thread.model,
141
+ context: thread.context ?? {},
142
+ tick: thread.tick ?? 0,
143
+ state: thread.state ?? STOPPED,
144
+ parentTaskId: thread.parentTaskId ?? null,
145
+ metadata: thread.metadata ?? null,
146
+ createdAt: thread.createdAt ?? now,
147
+ updatedAt: thread.updatedAt ?? now,
148
+ };
149
+
150
+ this.threads.set(thread.id, data);
151
+ this.events.set(thread.id, []);
152
+
153
+ return this.hydrate(data, []);
154
+ }
155
+
156
+ async update(tid: string, patch: ThreadUpdate): Promise<Thread> {
157
+ const data = this.threads.get(tid);
158
+ if (!data) throw new Error(`Thread ${tid} not found`);
159
+
160
+ if (patch.tick !== undefined) data.tick = patch.tick;
161
+ if (patch.state !== undefined) data.state = patch.state;
162
+ if (patch.context !== undefined) {
163
+ // Store raw context value, not the Context wrapper.
164
+ data.context = patch.context.context;
165
+ }
166
+ if (patch.metadata !== undefined) data.metadata = patch.metadata;
167
+ data.updatedAt = new Date();
168
+
169
+ return this.hydrate(data, []);
170
+ }
171
+
172
+ async delete(tid: string): Promise<void> {
173
+ this.threads.delete(tid);
174
+ this.events.delete(tid);
175
+ }
176
+
177
+ async history(
178
+ tid: string,
179
+ opts?: ThreadHistoryOptions,
180
+ ): Promise<ThreadEvent[]> {
181
+ return this.filterHistory(tid, opts);
182
+ }
183
+
184
+ async append(events: ThreadEvent[]): Promise<void> {
185
+ if (events.length === 0) return;
186
+
187
+ // Group by tid
188
+ const byThread = new Map<string, ThreadEvent[]>();
189
+ for (const event of events) {
190
+ if (!byThread.has(event.tid)) byThread.set(event.tid, []);
191
+ byThread.get(event.tid)!.push(event);
192
+ }
193
+
194
+ // Append to each thread's event log (idempotent on event.id)
195
+ for (const [tid, newEvents] of Array.from(byThread.entries())) {
196
+ let existing = this.events.get(tid);
197
+ if (!existing) {
198
+ existing = [];
199
+ this.events.set(tid, existing);
200
+ }
201
+
202
+ const existingIds = new Set(existing.map((e) => e.id));
203
+
204
+ for (const event of newEvents) {
205
+ if (!existingIds.has(event.id)) {
206
+ existing.push(event);
207
+ }
208
+ }
209
+
210
+ // Keep sorted by seq
211
+ existing.sort((a, b) => a.seq - b.seq);
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Hydrate a Thread instance from in-memory data.
217
+ */
218
+ private hydrate(data: ThreadData, history: ThreadEvent[]): Thread {
219
+ if (!this.registries) {
220
+ throw new Error(
221
+ "Registries must be bound before hydrating threads (call bind() first)",
222
+ );
223
+ }
224
+
225
+ const agent = this.registries.agents.get(data.agentId);
226
+ const model = this.registries.models.get(data.model);
227
+
228
+ if (!agent || !model) {
229
+ throw new Error(
230
+ `Thread ${data.tid} references non-existent agent/model (agent: ${data.agentId}, model: ${data.model})`,
231
+ );
232
+ }
233
+
234
+ return new Thread({
235
+ agent,
236
+ tid: data.tid,
237
+ context: new Context(data.namespace, data.context),
238
+ model,
239
+ history,
240
+ tick: data.tick,
241
+ state: data.state,
242
+ namespace: data.namespace,
243
+ task: null, // TODO: load from TaskStore when it exists
244
+ metadata: data.metadata,
245
+ storage: this, // pass storage reference so resumed thread can persist
246
+ persisted: true,
247
+ });
248
+ }
249
+
250
+ /**
251
+ * Filter and sort event history based on options.
252
+ */
253
+ private filterHistory(
254
+ tid: string,
255
+ opts?: boolean | ThreadHistoryOptions,
256
+ ): ThreadEvent[] {
257
+ let events = this.events.get(tid) ?? [];
258
+
259
+ // Handle boolean flag: true = all events, false = no events
260
+ if (typeof opts === "boolean") {
261
+ return opts ? [...events] : [];
262
+ }
263
+
264
+ // No options provided = return all events
265
+ if (opts === undefined) {
266
+ return [...events];
267
+ }
268
+
269
+ // Apply filters
270
+ if (opts.after !== undefined) {
271
+ events = events.filter((e) => e.seq > opts.after!);
272
+ }
273
+ if (opts.kinds && opts.kinds.length > 0) {
274
+ const kinds = new Set(opts.kinds);
275
+ events = events.filter((e) => kinds.has(e.kind));
276
+ }
277
+
278
+ // Clone before sorting/slicing to avoid mutating the original
279
+ events = [...events];
280
+
281
+ // Apply ordering
282
+ if (opts.order === "desc") {
283
+ events.reverse();
284
+ }
285
+
286
+ // Apply limit
287
+ if (opts.limit !== undefined) {
288
+ events = events.slice(0, opts.limit);
289
+ }
290
+
291
+ return events;
292
+ }
293
+
294
+ /**
295
+ * Apply filters to thread list.
296
+ */
297
+ private applyFilters(
298
+ threads: ThreadData[],
299
+ filter: ThreadFilter,
300
+ ): ThreadData[] {
301
+ return threads.filter((thread) => {
302
+ // Filter by namespace
303
+ if (filter.namespace !== undefined) {
304
+ if (thread.namespace !== filter.namespace) return false;
305
+ }
306
+
307
+ // Filter by state
308
+ if (filter.state !== undefined) {
309
+ if (Array.isArray(filter.state)) {
310
+ if (!filter.state.includes(thread.state)) return false;
311
+ } else {
312
+ if (thread.state !== filter.state) return false;
313
+ }
314
+ }
315
+
316
+ // Filter by agentId
317
+ if (filter.agentId !== undefined) {
318
+ if (thread.agentId !== filter.agentId) return false;
319
+ }
320
+
321
+ // Filter by parentTaskId
322
+ if (filter.parentTaskId !== undefined) {
323
+ if (thread.parentTaskId !== filter.parentTaskId) return false;
324
+ }
325
+
326
+ // Filter by createdAfter
327
+ if (filter.createdAfter !== undefined) {
328
+ if (thread.createdAt <= filter.createdAfter) return false;
329
+ }
330
+
331
+ // Filter by createdBefore
332
+ if (filter.createdBefore !== undefined) {
333
+ if (thread.createdAt >= filter.createdBefore) return false;
334
+ }
335
+
336
+ return true;
337
+ });
338
+ }
339
+
340
+ /**
341
+ * Apply sorting to thread list.
342
+ */
343
+ private applySorting(
344
+ threads: ThreadData[],
345
+ order?: { createdAt?: SortOrder; updatedAt?: SortOrder },
346
+ ): ThreadData[] {
347
+ if (!order) {
348
+ // Default: most recent first
349
+ return threads.sort(
350
+ (a, b) => b.createdAt.getTime() - a.createdAt.getTime(),
351
+ );
352
+ }
353
+
354
+ return threads.sort((a, b) => {
355
+ // Sort by createdAt first if specified
356
+ if (order.createdAt) {
357
+ const diff = a.createdAt.getTime() - b.createdAt.getTime();
358
+ if (diff !== 0) {
359
+ return order.createdAt === "asc" ? diff : -diff;
360
+ }
361
+ }
362
+
363
+ // Then by updatedAt if specified
364
+ if (order.updatedAt) {
365
+ const diff = a.updatedAt.getTime() - b.updatedAt.getTime();
366
+ return order.updatedAt === "asc" ? diff : -diff;
367
+ }
368
+
369
+ return 0;
370
+ });
371
+ }
372
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Storage contracts for Kernl.
3
+ *
4
+ * Core owns these interfaces; storage packages implement them.
5
+ * (must be defined here to avoid circular deps)
6
+ */
7
+
8
+ export type {
9
+ NewThread,
10
+ ThreadUpdate,
11
+ ThreadFilter,
12
+ ThreadHistoryOptions,
13
+ ThreadInclude,
14
+ SortOrder,
15
+ ThreadListOptions,
16
+ ThreadStore,
17
+ } from "./thread";
18
+
19
+ export type { Transaction, KernlStorage } from "./base";
20
+
21
+ export { InMemoryStorage, InMemoryThreadStore } from "./in-memory";
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Thread storage contracts.
3
+ */
4
+
5
+ import type { Thread } from "@/thread";
6
+ import type { Context } from "@/context";
7
+ import type { ThreadEvent, ThreadState } from "@/types/thread";
8
+
9
+ /* ---- Store ---- */
10
+
11
+ /**
12
+ * Thread persistence store.
13
+ */
14
+ export interface ThreadStore {
15
+ /**
16
+ * Get a thread by id.
17
+ *
18
+ * Optionally include the thread_events.
19
+ */
20
+ get(tid: string, include?: ThreadInclude): Promise<Thread | null>;
21
+
22
+ /**
23
+ * List threads matching the filter.
24
+ */
25
+ list(options?: ThreadListOptions): Promise<Thread[]>;
26
+
27
+ /**
28
+ * Insert a new thread into the store.
29
+ */
30
+ insert(thread: NewThread): Promise<Thread>;
31
+
32
+ /**
33
+ * Update thread runtime state (tick, state, metadata).
34
+ *
35
+ * Does NOT mutate the event log, which is append-only.
36
+ */
37
+ update(tid: string, patch: ThreadUpdate): Promise<Thread>;
38
+
39
+ /**
40
+ * Delete a thread and cascade to thread_events.
41
+ */
42
+ delete(tid: string): Promise<void>;
43
+
44
+ /**
45
+ * Get the event history for a thread.
46
+ */
47
+ history(tid: string, options?: ThreadHistoryOptions): Promise<ThreadEvent[]>;
48
+
49
+ /**
50
+ * Append events to the thread history.
51
+ *
52
+ * Semantics:
53
+ * - Guaranteed per-thread ordering via a monotonically increasing `seq`.
54
+ * - Idempotent on `(tid, event.id)`: duplicate ids MUST NOT create duplicate rows.
55
+ * - Events maintain insertion order.
56
+ *
57
+ * Note:
58
+ * - Thread class manages monotonic seq and timestamp assignment.
59
+ */
60
+ append(events: ThreadEvent[]): Promise<void>;
61
+ }
62
+
63
+ /* ---- DTOs ---- */
64
+
65
+ /**
66
+ * Input for creating a new thread.
67
+ */
68
+ export interface NewThread {
69
+ id: string;
70
+ namespace: string;
71
+ agentId: string;
72
+ model: string; // composite key: '{provider}/{modelId}'
73
+ context?: unknown; // becomes a Context instance in a Thread
74
+ tick?: number;
75
+ state?: ThreadState;
76
+ parentTaskId?: string | null;
77
+ metadata?: Record<string, unknown> | null;
78
+ createdAt?: Date;
79
+ updatedAt?: Date;
80
+ }
81
+
82
+ /**
83
+ * Partial update for thread runtime state.
84
+ *
85
+ * Only mutable fields are exposed (tick, state, context, metadata).
86
+ */
87
+ export interface ThreadUpdate {
88
+ tick?: number;
89
+ state?: ThreadState;
90
+ context?: Context;
91
+ metadata?: Record<string, unknown> | null;
92
+ }
93
+
94
+ /**
95
+ * Filter for listing threads.
96
+ */
97
+ export interface ThreadFilter {
98
+ namespace?: string;
99
+ state?: ThreadState | ThreadState[];
100
+ agentId?: string;
101
+ parentTaskId?: string;
102
+ createdAfter?: Date;
103
+ createdBefore?: Date;
104
+ }
105
+
106
+ /**
107
+ * Options for querying thread history.
108
+ */
109
+ export interface ThreadHistoryOptions {
110
+ after?: number; // seq number
111
+ limit?: number;
112
+ order?: "asc" | "desc";
113
+ kinds?: string[]; // filter by event kind
114
+ }
115
+
116
+ /**
117
+ * Include options for eager loading related data.
118
+ */
119
+ export interface ThreadInclude {
120
+ history?: boolean | ThreadHistoryOptions;
121
+ }
122
+
123
+ /**
124
+ * Sort order direction.
125
+ */
126
+ export type SortOrder = "asc" | "desc";
127
+
128
+ /**
129
+ * Options for listing threads.
130
+ */
131
+ export interface ThreadListOptions {
132
+ filter?: ThreadFilter;
133
+ include?: ThreadInclude;
134
+ order?: {
135
+ createdAt?: SortOrder;
136
+ updatedAt?: SortOrder;
137
+ };
138
+ limit?: number;
139
+ offset?: number;
140
+ cursor?: string;
141
+ }
package/src/task.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import type { Agent } from "@/agent";
2
2
  import type { Context, UnknownContext } from "@/context";
3
-
4
3
  import type { Thread } from "@/thread";
5
4
 
6
5
  /**
@@ -8,7 +7,7 @@ import type { Thread } from "@/thread";
8
7
  * Analogous to task_struct in the Linux kernel.
9
8
  */
10
9
  export class Task<TContext = UnknownContext, TResult = unknown> {
11
- pid: string /* process ID - unique identifier for this task */;
10
+ id: string /* unique identifier for this task (pid) */;
12
11
  // tgid: string | null; /* task groupid */
13
12
  // prio: TaskPriority;
14
13
  instructions:
@@ -17,6 +16,7 @@ export class Task<TContext = UnknownContext, TResult = unknown> {
17
16
  // sched: TaskSched; /* scheduling (timers, timeouts, deadlines) */
18
17
  state: TaskState /* current state of the task */;
19
18
  owner: Agent<TContext> /* agent that currently owns this task */;
19
+ namespace: string;
20
20
 
21
21
  context: Context<TContext> /* execution context propagated throughout call graph */;
22
22
  // cred: Credentials; /* Effective (overridable) subjective task credentials (COW): */
@@ -35,20 +35,22 @@ export class Task<TContext = UnknownContext, TResult = unknown> {
35
35
  // TODO: Deferred fields for later implementation
36
36
  // tgid: string; // Thread group ID
37
37
  // limits: TaskLimits; // Resource limits (max ticks, tokens, timeout)
38
- // nsproxy: NamespaceProxy; // Namespace isolation
38
+ // nsproxy: NamespaceProxy; // Namespace isolation (partially implemented via namespace string)
39
39
 
40
- constructor(init: {
41
- pid: string;
40
+ constructor(options: {
41
+ id: string;
42
42
  instructions: string | ((context: Context<TContext>) => string);
43
43
  state: TaskState;
44
44
  owner: Agent<TContext>;
45
45
  context: Context<TContext>;
46
+ namespace?: string;
46
47
  }) {
47
- this.pid = init.pid;
48
- this.instructions = init.instructions;
49
- this.state = init.state;
50
- this.owner = init.owner;
51
- this.context = init.context;
48
+ this.id = options.id;
49
+ this.instructions = options.instructions;
50
+ this.state = options.state;
51
+ this.owner = options.owner;
52
+ this.context = options.context;
53
+ this.namespace = options.namespace ?? "kernl";
52
54
  this.threads = [];
53
55
  this.current = null;
54
56
  this.result = null;
@@ -55,15 +55,13 @@ async function* streamFromResponse(
55
55
  * Creates a mock LanguageModel that automatically implements streaming
56
56
  * based on the generate() implementation.
57
57
  */
58
- export function createMockModel(
59
- generateFn: (req: LanguageModelRequest) => Promise<LanguageModelResponse>,
60
- ): LanguageModel {
58
+ export function createMockModel(generateFn: any): any {
61
59
  return {
62
60
  spec: "1.0" as const,
63
61
  provider: "test",
64
62
  modelId: "test-model",
65
63
  generate: generateFn,
66
- stream: async function* (req: LanguageModelRequest) {
64
+ stream: async function* (req: any) {
67
65
  const response = await generateFn(req);
68
66
  yield* streamFromResponse(response);
69
67
  },