kernl 0.2.0 → 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 +4 -5
  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 +32 -9
  14. package/dist/agent.d.ts.map +1 -1
  15. package/dist/agent.js +102 -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 +73 -5
  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 +141 -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 +111 -10
  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,283 @@
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 { Context } from "../context";
10
+ import { STOPPED } from "@kernl-sdk/protocol";
11
+ import { UnimplementedError } from "@kernl-sdk/shared/lib";
12
+ /**
13
+ * In-memory storage implementation.
14
+ */
15
+ export class InMemoryStorage {
16
+ threads;
17
+ constructor() {
18
+ this.threads = new InMemoryThreadStore();
19
+ }
20
+ bind(registries) {
21
+ this.threads.bind(registries);
22
+ }
23
+ async transaction(fn) {
24
+ throw new UnimplementedError("Transactions not supported in in-memory storage");
25
+ }
26
+ async init() {
27
+ // no-op
28
+ }
29
+ async close() {
30
+ // no-op
31
+ }
32
+ async migrate() {
33
+ // no-op
34
+ }
35
+ }
36
+ /**
37
+ * In-memory thread store implementation.
38
+ */
39
+ export class InMemoryThreadStore {
40
+ threads = new Map();
41
+ events = new Map(); // tid -> events
42
+ registries = null;
43
+ bind(registries) {
44
+ this.registries = registries;
45
+ }
46
+ async get(tid, include) {
47
+ const data = this.threads.get(tid);
48
+ if (!data)
49
+ return null;
50
+ const history = include?.history
51
+ ? this.filterHistory(tid, include.history)
52
+ : [];
53
+ try {
54
+ return this.hydrate(data, history);
55
+ }
56
+ catch (error) {
57
+ return null;
58
+ }
59
+ }
60
+ async list(options) {
61
+ let threads = Array.from(this.threads.values());
62
+ // Apply filters
63
+ if (options?.filter) {
64
+ threads = this.applyFilters(threads, options.filter);
65
+ }
66
+ // Apply sorting
67
+ threads = this.applySorting(threads, options?.order);
68
+ // Apply pagination
69
+ if (options?.offset)
70
+ threads = threads.slice(options.offset);
71
+ if (options?.limit)
72
+ threads = threads.slice(0, options.limit);
73
+ return threads
74
+ .map((data) => {
75
+ try {
76
+ return this.hydrate(data, []);
77
+ }
78
+ catch (error) {
79
+ // Skip threads with non-existent agent/model (graceful degradation)
80
+ return null;
81
+ }
82
+ })
83
+ .filter((thread) => thread !== null);
84
+ }
85
+ async insert(thread) {
86
+ const now = new Date();
87
+ const data = {
88
+ tid: thread.id,
89
+ namespace: thread.namespace,
90
+ agentId: thread.agentId,
91
+ model: thread.model,
92
+ context: thread.context ?? {},
93
+ tick: thread.tick ?? 0,
94
+ state: thread.state ?? STOPPED,
95
+ parentTaskId: thread.parentTaskId ?? null,
96
+ metadata: thread.metadata ?? null,
97
+ createdAt: thread.createdAt ?? now,
98
+ updatedAt: thread.updatedAt ?? now,
99
+ };
100
+ this.threads.set(thread.id, data);
101
+ this.events.set(thread.id, []);
102
+ return this.hydrate(data, []);
103
+ }
104
+ async update(tid, patch) {
105
+ const data = this.threads.get(tid);
106
+ if (!data)
107
+ throw new Error(`Thread ${tid} not found`);
108
+ if (patch.tick !== undefined)
109
+ data.tick = patch.tick;
110
+ if (patch.state !== undefined)
111
+ data.state = patch.state;
112
+ if (patch.context !== undefined) {
113
+ // Store raw context value, not the Context wrapper.
114
+ data.context = patch.context.context;
115
+ }
116
+ if (patch.metadata !== undefined)
117
+ data.metadata = patch.metadata;
118
+ data.updatedAt = new Date();
119
+ return this.hydrate(data, []);
120
+ }
121
+ async delete(tid) {
122
+ this.threads.delete(tid);
123
+ this.events.delete(tid);
124
+ }
125
+ async history(tid, opts) {
126
+ return this.filterHistory(tid, opts);
127
+ }
128
+ async append(events) {
129
+ if (events.length === 0)
130
+ return;
131
+ // Group by tid
132
+ const byThread = new Map();
133
+ for (const event of events) {
134
+ if (!byThread.has(event.tid))
135
+ byThread.set(event.tid, []);
136
+ byThread.get(event.tid).push(event);
137
+ }
138
+ // Append to each thread's event log (idempotent on event.id)
139
+ for (const [tid, newEvents] of Array.from(byThread.entries())) {
140
+ let existing = this.events.get(tid);
141
+ if (!existing) {
142
+ existing = [];
143
+ this.events.set(tid, existing);
144
+ }
145
+ const existingIds = new Set(existing.map((e) => e.id));
146
+ for (const event of newEvents) {
147
+ if (!existingIds.has(event.id)) {
148
+ existing.push(event);
149
+ }
150
+ }
151
+ // Keep sorted by seq
152
+ existing.sort((a, b) => a.seq - b.seq);
153
+ }
154
+ }
155
+ /**
156
+ * Hydrate a Thread instance from in-memory data.
157
+ */
158
+ hydrate(data, history) {
159
+ if (!this.registries) {
160
+ throw new Error("Registries must be bound before hydrating threads (call bind() first)");
161
+ }
162
+ const agent = this.registries.agents.get(data.agentId);
163
+ const model = this.registries.models.get(data.model);
164
+ if (!agent || !model) {
165
+ throw new Error(`Thread ${data.tid} references non-existent agent/model (agent: ${data.agentId}, model: ${data.model})`);
166
+ }
167
+ return new Thread({
168
+ agent,
169
+ tid: data.tid,
170
+ context: new Context(data.namespace, data.context),
171
+ model,
172
+ history,
173
+ tick: data.tick,
174
+ state: data.state,
175
+ namespace: data.namespace,
176
+ task: null, // TODO: load from TaskStore when it exists
177
+ metadata: data.metadata,
178
+ storage: this, // pass storage reference so resumed thread can persist
179
+ persisted: true,
180
+ });
181
+ }
182
+ /**
183
+ * Filter and sort event history based on options.
184
+ */
185
+ filterHistory(tid, opts) {
186
+ let events = this.events.get(tid) ?? [];
187
+ // Handle boolean flag: true = all events, false = no events
188
+ if (typeof opts === "boolean") {
189
+ return opts ? [...events] : [];
190
+ }
191
+ // No options provided = return all events
192
+ if (opts === undefined) {
193
+ return [...events];
194
+ }
195
+ // Apply filters
196
+ if (opts.after !== undefined) {
197
+ events = events.filter((e) => e.seq > opts.after);
198
+ }
199
+ if (opts.kinds && opts.kinds.length > 0) {
200
+ const kinds = new Set(opts.kinds);
201
+ events = events.filter((e) => kinds.has(e.kind));
202
+ }
203
+ // Clone before sorting/slicing to avoid mutating the original
204
+ events = [...events];
205
+ // Apply ordering
206
+ if (opts.order === "desc") {
207
+ events.reverse();
208
+ }
209
+ // Apply limit
210
+ if (opts.limit !== undefined) {
211
+ events = events.slice(0, opts.limit);
212
+ }
213
+ return events;
214
+ }
215
+ /**
216
+ * Apply filters to thread list.
217
+ */
218
+ applyFilters(threads, filter) {
219
+ return threads.filter((thread) => {
220
+ // Filter by namespace
221
+ if (filter.namespace !== undefined) {
222
+ if (thread.namespace !== filter.namespace)
223
+ return false;
224
+ }
225
+ // Filter by state
226
+ if (filter.state !== undefined) {
227
+ if (Array.isArray(filter.state)) {
228
+ if (!filter.state.includes(thread.state))
229
+ return false;
230
+ }
231
+ else {
232
+ if (thread.state !== filter.state)
233
+ return false;
234
+ }
235
+ }
236
+ // Filter by agentId
237
+ if (filter.agentId !== undefined) {
238
+ if (thread.agentId !== filter.agentId)
239
+ return false;
240
+ }
241
+ // Filter by parentTaskId
242
+ if (filter.parentTaskId !== undefined) {
243
+ if (thread.parentTaskId !== filter.parentTaskId)
244
+ return false;
245
+ }
246
+ // Filter by createdAfter
247
+ if (filter.createdAfter !== undefined) {
248
+ if (thread.createdAt <= filter.createdAfter)
249
+ return false;
250
+ }
251
+ // Filter by createdBefore
252
+ if (filter.createdBefore !== undefined) {
253
+ if (thread.createdAt >= filter.createdBefore)
254
+ return false;
255
+ }
256
+ return true;
257
+ });
258
+ }
259
+ /**
260
+ * Apply sorting to thread list.
261
+ */
262
+ applySorting(threads, order) {
263
+ if (!order) {
264
+ // Default: most recent first
265
+ return threads.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
266
+ }
267
+ return threads.sort((a, b) => {
268
+ // Sort by createdAt first if specified
269
+ if (order.createdAt) {
270
+ const diff = a.createdAt.getTime() - b.createdAt.getTime();
271
+ if (diff !== 0) {
272
+ return order.createdAt === "asc" ? diff : -diff;
273
+ }
274
+ }
275
+ // Then by updatedAt if specified
276
+ if (order.updatedAt) {
277
+ const diff = a.updatedAt.getTime() - b.updatedAt.getTime();
278
+ return order.updatedAt === "asc" ? diff : -diff;
279
+ }
280
+ return 0;
281
+ });
282
+ }
283
+ }
@@ -0,0 +1,10 @@
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
+ export type { NewThread, ThreadUpdate, ThreadFilter, ThreadHistoryOptions, ThreadInclude, SortOrder, ThreadListOptions, ThreadStore, } from "./thread";
8
+ export type { Transaction, KernlStorage } from "./base";
9
+ export { InMemoryStorage, InMemoryThreadStore } from "./in-memory";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,aAAa,EACb,SAAS,EACT,iBAAiB,EACjB,WAAW,GACZ,MAAM,UAAU,CAAC;AAElB,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAExD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,7 @@
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
+ export { InMemoryStorage, InMemoryThreadStore } from "./in-memory";
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Thread storage contracts.
3
+ */
4
+ import type { Thread } from "../thread";
5
+ import type { Context } from "../context";
6
+ import type { ThreadEvent, ThreadState } from "../types/thread";
7
+ /**
8
+ * Thread persistence store.
9
+ */
10
+ export interface ThreadStore {
11
+ /**
12
+ * Get a thread by id.
13
+ *
14
+ * Optionally include the thread_events.
15
+ */
16
+ get(tid: string, include?: ThreadInclude): Promise<Thread | null>;
17
+ /**
18
+ * List threads matching the filter.
19
+ */
20
+ list(options?: ThreadListOptions): Promise<Thread[]>;
21
+ /**
22
+ * Insert a new thread into the store.
23
+ */
24
+ insert(thread: NewThread): Promise<Thread>;
25
+ /**
26
+ * Update thread runtime state (tick, state, metadata).
27
+ *
28
+ * Does NOT mutate the event log, which is append-only.
29
+ */
30
+ update(tid: string, patch: ThreadUpdate): Promise<Thread>;
31
+ /**
32
+ * Delete a thread and cascade to thread_events.
33
+ */
34
+ delete(tid: string): Promise<void>;
35
+ /**
36
+ * Get the event history for a thread.
37
+ */
38
+ history(tid: string, options?: ThreadHistoryOptions): Promise<ThreadEvent[]>;
39
+ /**
40
+ * Append events to the thread history.
41
+ *
42
+ * Semantics:
43
+ * - Guaranteed per-thread ordering via a monotonically increasing `seq`.
44
+ * - Idempotent on `(tid, event.id)`: duplicate ids MUST NOT create duplicate rows.
45
+ * - Events maintain insertion order.
46
+ *
47
+ * Note:
48
+ * - Thread class manages monotonic seq and timestamp assignment.
49
+ */
50
+ append(events: ThreadEvent[]): Promise<void>;
51
+ }
52
+ /**
53
+ * Input for creating a new thread.
54
+ */
55
+ export interface NewThread {
56
+ id: string;
57
+ namespace: string;
58
+ agentId: string;
59
+ model: string;
60
+ context?: unknown;
61
+ tick?: number;
62
+ state?: ThreadState;
63
+ parentTaskId?: string | null;
64
+ metadata?: Record<string, unknown> | null;
65
+ createdAt?: Date;
66
+ updatedAt?: Date;
67
+ }
68
+ /**
69
+ * Partial update for thread runtime state.
70
+ *
71
+ * Only mutable fields are exposed (tick, state, context, metadata).
72
+ */
73
+ export interface ThreadUpdate {
74
+ tick?: number;
75
+ state?: ThreadState;
76
+ context?: Context;
77
+ metadata?: Record<string, unknown> | null;
78
+ }
79
+ /**
80
+ * Filter for listing threads.
81
+ */
82
+ export interface ThreadFilter {
83
+ namespace?: string;
84
+ state?: ThreadState | ThreadState[];
85
+ agentId?: string;
86
+ parentTaskId?: string;
87
+ createdAfter?: Date;
88
+ createdBefore?: Date;
89
+ }
90
+ /**
91
+ * Options for querying thread history.
92
+ */
93
+ export interface ThreadHistoryOptions {
94
+ after?: number;
95
+ limit?: number;
96
+ order?: "asc" | "desc";
97
+ kinds?: string[];
98
+ }
99
+ /**
100
+ * Include options for eager loading related data.
101
+ */
102
+ export interface ThreadInclude {
103
+ history?: boolean | ThreadHistoryOptions;
104
+ }
105
+ /**
106
+ * Sort order direction.
107
+ */
108
+ export type SortOrder = "asc" | "desc";
109
+ /**
110
+ * Options for listing threads.
111
+ */
112
+ export interface ThreadListOptions {
113
+ filter?: ThreadFilter;
114
+ include?: ThreadInclude;
115
+ order?: {
116
+ createdAt?: SortOrder;
117
+ updatedAt?: SortOrder;
118
+ };
119
+ limit?: number;
120
+ offset?: number;
121
+ cursor?: string;
122
+ }
123
+ //# sourceMappingURL=thread.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/storage/thread.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Thread storage contracts.
3
+ */
4
+ export {};
package/dist/task.d.ts CHANGED
@@ -6,10 +6,11 @@ import type { Thread } from "./thread";
6
6
  * Analogous to task_struct in the Linux kernel.
7
7
  */
8
8
  export declare class Task<TContext = UnknownContext, TResult = unknown> {
9
- pid: string;
9
+ id: string;
10
10
  instructions: string | ((context: Context<TContext>) => string);
11
11
  state: TaskState;
12
12
  owner: Agent<TContext>;
13
+ namespace: string;
13
14
  context: Context<TContext>;
14
15
  /**
15
16
  * Might want the ability to pick up a new thread of execution from a compressed task checkpoint instead of including
@@ -19,12 +20,13 @@ export declare class Task<TContext = UnknownContext, TResult = unknown> {
19
20
  threads: Thread<TContext>[];
20
21
  current: Thread<TContext> | null;
21
22
  result: TResult | null;
22
- constructor(init: {
23
- pid: string;
23
+ constructor(options: {
24
+ id: string;
24
25
  instructions: string | ((context: Context<TContext>) => string);
25
26
  state: TaskState;
26
27
  owner: Agent<TContext>;
27
28
  context: Context<TContext>;
29
+ namespace?: string;
28
30
  });
29
31
  }
30
32
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../src/task.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC;;;GAGG;AACH,qBAAa,IAAI,CAAC,QAAQ,GAAG,cAAc,EAAE,OAAO,GAAG,OAAO;IAC5D,GAAG,EAAE,MAAM,CAAoD;IAG/D,YAAY,EACR,MAAM,GACN,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,MAAM,CAAC,CAA4B;IAExE,KAAK,EAAE,SAAS,CAAiC;IACjD,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAA2C;IAEjE,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAA0D;IAIpF;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAA6C;IACxE,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAyC;IACzE,MAAM,EAAE,OAAO,GAAG,IAAI,CAAgC;gBAQ1C,IAAI,EAAE;QAChB,GAAG,EAAE,MAAM,CAAC;QACZ,YAAY,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,MAAM,CAAC,CAAC;QAChE,KAAK,EAAE,SAAS,CAAC;QACjB,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QACvB,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;KAC5B;CAUF;AAED;;GAEG;AACH,oBAAY,SAAS;IACnB;;;;OAIG;IACH,OAAO,YAAY;IAEnB;;;;;;;;;;OAUG;IACH,aAAa,kBAAkB;IAE/B;;;;;;;;;OASG;IACH,eAAe,oBAAoB;IAEnC;;;;;;;OAOG;IACH,OAAO,YAAY;IAEnB;;;;;;;OAOG;IACH,MAAM,WAAW;IAEjB;;;OAGG;IACH,IAAI,SAAS;CACd"}
1
+ {"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../src/task.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC;;;GAGG;AACH,qBAAa,IAAI,CAAC,QAAQ,GAAG,cAAc,EAAE,OAAO,GAAG,OAAO;IAC5D,EAAE,EAAE,MAAM,CAA6C;IAGvD,YAAY,EACR,MAAM,GACN,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,MAAM,CAAC,CAA4B;IAExE,KAAK,EAAE,SAAS,CAAiC;IACjD,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAA2C;IACjE,SAAS,EAAE,MAAM,CAAC;IAElB,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAA0D;IAIpF;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAA6C;IACxE,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAyC;IACzE,MAAM,EAAE,OAAO,GAAG,IAAI,CAAgC;gBAQ1C,OAAO,EAAE;QACnB,EAAE,EAAE,MAAM,CAAC;QACX,YAAY,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,MAAM,CAAC,CAAC;QAChE,KAAK,EAAE,SAAS,CAAC;QACjB,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QACvB,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB;CAWF;AAED;;GAEG;AACH,oBAAY,SAAS;IACnB;;;;OAIG;IACH,OAAO,YAAY;IAEnB;;;;;;;;;;OAUG;IACH,aAAa,kBAAkB;IAE/B;;;;;;;;;OASG;IACH,eAAe,oBAAoB;IAEnC;;;;;;;OAOG;IACH,OAAO,YAAY;IAEnB;;;;;;;OAOG;IACH,MAAM,WAAW;IAEjB;;;OAGG;IACH,IAAI,SAAS;CACd"}
package/dist/task.js CHANGED
@@ -3,13 +3,14 @@
3
3
  * Analogous to task_struct in the Linux kernel.
4
4
  */
5
5
  export class Task {
6
- pid;
6
+ id;
7
7
  // tgid: string | null; /* task groupid */
8
8
  // prio: TaskPriority;
9
9
  instructions;
10
10
  // sched: TaskSched; /* scheduling (timers, timeouts, deadlines) */
11
11
  state;
12
12
  owner;
13
+ namespace;
13
14
  context;
14
15
  // cred: Credentials; /* Effective (overridable) subjective task credentials (COW): */
15
16
  // realcred: Credentials; /* Objective and real subjective task credentials (COW): */
@@ -25,13 +26,14 @@ export class Task {
25
26
  // TODO: Deferred fields for later implementation
26
27
  // tgid: string; // Thread group ID
27
28
  // limits: TaskLimits; // Resource limits (max ticks, tokens, timeout)
28
- // nsproxy: NamespaceProxy; // Namespace isolation
29
- constructor(init) {
30
- this.pid = init.pid;
31
- this.instructions = init.instructions;
32
- this.state = init.state;
33
- this.owner = init.owner;
34
- this.context = init.context;
29
+ // nsproxy: NamespaceProxy; // Namespace isolation (partially implemented via namespace string)
30
+ constructor(options) {
31
+ this.id = options.id;
32
+ this.instructions = options.instructions;
33
+ this.state = options.state;
34
+ this.owner = options.owner;
35
+ this.context = options.context;
36
+ this.namespace = options.namespace ?? "kernl";
35
37
  this.threads = [];
36
38
  this.current = null;
37
39
  this.result = null;
@@ -1,7 +1,6 @@
1
- import type { LanguageModel, LanguageModelRequest, LanguageModelResponse } from "@kernl-sdk/protocol";
2
1
  /**
3
2
  * Creates a mock LanguageModel that automatically implements streaming
4
3
  * based on the generate() implementation.
5
4
  */
6
- export declare function createMockModel(generateFn: (req: LanguageModelRequest) => Promise<LanguageModelResponse>): LanguageModel;
5
+ export declare function createMockModel(generateFn: any): any;
7
6
  //# sourceMappingURL=mock-model.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mock-model.d.ts","sourceRoot":"","sources":["../../../../src/thread/__tests__/fixtures/mock-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EAGtB,MAAM,qBAAqB,CAAC;AA+C7B;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,CAAC,GAAG,EAAE,oBAAoB,KAAK,OAAO,CAAC,qBAAqB,CAAC,GACxE,aAAa,CAWf"}
1
+ {"version":3,"file":"mock-model.d.ts","sourceRoot":"","sources":["../../../../src/thread/__tests__/fixtures/mock-model.ts"],"names":[],"mappings":"AAqDA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,GAAG,GAAG,GAAG,CAWpD"}
@@ -40,7 +40,7 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
40
40
  ],
41
41
  },
42
42
  ];
43
- const thread = new Thread(kernl, agent, input);
43
+ const thread = new Thread({ agent, input });
44
44
  const events = [];
45
45
  for await (const event of thread.stream()) {
46
46
  events.push(event);
@@ -89,7 +89,7 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
89
89
  content: [{ kind: "text", text: "Count to 3" }],
90
90
  },
91
91
  ];
92
- const thread = new Thread(kernl, agent, input);
92
+ const thread = new Thread({ agent, input });
93
93
  const streamEvents = [];
94
94
  for await (const event of thread.stream()) {
95
95
  streamEvents.push(event);
@@ -150,7 +150,7 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
150
150
  content: [{ kind: "text", text: "What is 25 + 17?" }],
151
151
  },
152
152
  ];
153
- const thread = new Thread(kernl, agent, input);
153
+ const thread = new Thread({ agent, input });
154
154
  const events = [];
155
155
  for await (const event of thread.stream()) {
156
156
  events.push(event);
@@ -184,6 +184,74 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
184
184
  expect(textContent).toBeDefined();
185
185
  expect(textContent.text).toContain("42");
186
186
  }, 30000);
187
+ it("should properly encode tool results with matching callIds for multi-turn", async () => {
188
+ const multiplyTool = tool({
189
+ id: "multiply",
190
+ name: "multiply",
191
+ description: "Multiply two numbers",
192
+ parameters: z.object({
193
+ a: z.number().describe("First number"),
194
+ b: z.number().describe("Second number"),
195
+ }),
196
+ execute: async (ctx, { a, b }) => {
197
+ return a * b;
198
+ },
199
+ });
200
+ const toolkit = new Toolkit({
201
+ id: "math",
202
+ tools: [multiplyTool],
203
+ });
204
+ const agent = new Agent({
205
+ id: "test-multi-turn",
206
+ name: "Test Multi-Turn Agent",
207
+ instructions: "You are a helpful assistant that can do math.",
208
+ model,
209
+ toolkits: [toolkit],
210
+ });
211
+ const input = [
212
+ {
213
+ kind: "message",
214
+ id: "msg-1",
215
+ role: "user",
216
+ content: [{ kind: "text", text: "What is 7 times 6?" }],
217
+ },
218
+ ];
219
+ const thread = new Thread({ agent, input });
220
+ const events = [];
221
+ // Collect all events from the stream
222
+ for await (const event of thread.stream()) {
223
+ events.push(event);
224
+ }
225
+ // Find the tool call and result
226
+ const toolCalls = events.filter((e) => e.kind === "tool-call");
227
+ const toolResults = events.filter((e) => e.kind === "tool-result");
228
+ expect(toolCalls.length).toBeGreaterThan(0);
229
+ expect(toolResults.length).toBeGreaterThan(0);
230
+ const multiplyCall = toolCalls[0];
231
+ const multiplyResult = toolResults[0];
232
+ // Verify callId matches between tool call and result
233
+ expect(multiplyCall.callId).toBe(multiplyResult.callId);
234
+ expect(multiplyCall.toolId).toBe("multiply");
235
+ expect(multiplyResult.toolId).toBe("multiply");
236
+ // Verify the tool result has the correct structure
237
+ expect(multiplyResult.callId).toBeDefined();
238
+ expect(typeof multiplyResult.callId).toBe("string");
239
+ expect(multiplyResult.callId.length).toBeGreaterThan(0);
240
+ // Verify history contains both with matching callIds
241
+ const history = thread.history;
242
+ const historyToolCall = history.find((e) => e.kind === "tool-call" && e.toolId === "multiply");
243
+ const historyToolResult = history.find((e) => e.kind === "tool-result" && e.toolId === "multiply");
244
+ expect(historyToolCall).toBeDefined();
245
+ expect(historyToolResult).toBeDefined();
246
+ expect(historyToolCall.callId).toBe(historyToolResult.callId);
247
+ // Verify final response uses the tool result
248
+ const messages = events.filter((e) => e.kind === "message");
249
+ const assistantMessage = messages.find((m) => m.role === "assistant");
250
+ expect(assistantMessage).toBeDefined();
251
+ const textContent = assistantMessage.content.find((c) => c.kind === "text");
252
+ expect(textContent).toBeDefined();
253
+ expect(textContent.text).toContain("42");
254
+ }, 30000);
187
255
  });
188
256
  describe("execute()", () => {
189
257
  it("should consume stream and return final response", async () => {
@@ -201,7 +269,7 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
201
269
  content: [{ kind: "text", text: "Say 'Testing' and nothing else." }],
202
270
  },
203
271
  ];
204
- const thread = new Thread(kernl, agent, input);
272
+ const thread = new Thread({ agent, input });
205
273
  const result = await thread.execute();
206
274
  // Should have a response
207
275
  expect(result.response).toBeDefined();
@@ -235,7 +303,7 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
235
303
  ],
236
304
  },
237
305
  ];
238
- const thread = new Thread(kernl, agent, input);
306
+ const thread = new Thread({ agent, input });
239
307
  const result = await thread.execute();
240
308
  // Response should be validated and parsed
241
309
  expect(result.response).toBeDefined();
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=namespace.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"namespace.test.d.ts","sourceRoot":"","sources":["../../../src/thread/__tests__/namespace.test.ts"],"names":[],"mappings":""}