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,357 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { Agent } from "../../agent.js";
3
+ import { Kernl } from "../../kernl.js";
4
+ import { createMockModel } from "../../thread/__tests__/fixtures/mock-model.js";
5
+ import { MisconfiguredError } from "../../lib/error.js";
6
+ import { message } from "@kernl-sdk/protocol";
7
+ import { InMemoryStorage } from "../../storage/in-memory.js";
8
+ describe("Agent.run() lifecycle", () => {
9
+ describe("Storage wiring", () => {
10
+ it("should pass storage to new Thread when creating", async () => {
11
+ const storage = new InMemoryStorage();
12
+ const model = createMockModel(async () => ({
13
+ content: [message({ role: "assistant", text: "Done" })],
14
+ finishReason: "stop",
15
+ usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
16
+ warnings: [],
17
+ }));
18
+ const agent = new Agent({
19
+ id: "test-agent",
20
+ name: "Test",
21
+ instructions: "Test",
22
+ model,
23
+ });
24
+ const kernl = new Kernl({ storage: { db: storage } });
25
+ kernl.register(agent);
26
+ await agent.run("Hello");
27
+ // Verify storage was used - check that events were appended
28
+ const threads = await storage.threads.list();
29
+ expect(threads).toHaveLength(1);
30
+ const history = await storage.threads.history(threads[0].tid);
31
+ expect(history.length).toBeGreaterThan(0);
32
+ });
33
+ it("should work without storage (persist is no-op)", async () => {
34
+ const model = createMockModel(async () => ({
35
+ content: [message({ role: "assistant", text: "Done" })],
36
+ finishReason: "stop",
37
+ usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
38
+ warnings: [],
39
+ }));
40
+ const agent = new Agent({
41
+ id: "test-agent",
42
+ name: "Test",
43
+ instructions: "Test",
44
+ model,
45
+ });
46
+ const kernl = new Kernl(); // No storage
47
+ kernl.register(agent);
48
+ const result = await agent.run("Hello");
49
+ // Should complete successfully
50
+ expect(result.response).toBe("Done");
51
+ expect(result.state).toBe("stopped");
52
+ });
53
+ it("should throw MisconfiguredError when agent not bound to kernl", async () => {
54
+ const model = createMockModel(async () => ({
55
+ content: [message({ role: "assistant", text: "Done" })],
56
+ finishReason: "stop",
57
+ usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
58
+ warnings: [],
59
+ }));
60
+ const agent = new Agent({
61
+ id: "test-agent",
62
+ name: "Test",
63
+ instructions: "Test",
64
+ model,
65
+ });
66
+ // Don't register with kernl
67
+ await expect(agent.run("Hello")).rejects.toThrow(MisconfiguredError);
68
+ await expect(agent.run("Hello")).rejects.toThrow(/not bound to kernl/);
69
+ });
70
+ });
71
+ describe("New thread path", () => {
72
+ it("should create new thread when no threadId provided", async () => {
73
+ const model = createMockModel(async () => ({
74
+ content: [message({ role: "assistant", text: "Done" })],
75
+ finishReason: "stop",
76
+ usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
77
+ warnings: [],
78
+ }));
79
+ const agent = new Agent({
80
+ id: "test-agent",
81
+ name: "Test",
82
+ instructions: "Test",
83
+ model,
84
+ });
85
+ const kernl = new Kernl();
86
+ kernl.register(agent);
87
+ const result = await agent.run("Hello");
88
+ expect(result.response).toBe("Done");
89
+ expect(result.state).toBe("stopped");
90
+ });
91
+ it("should create new thread when threadId not found in storage", async () => {
92
+ const storage = new InMemoryStorage();
93
+ const model = createMockModel(async () => ({
94
+ content: [message({ role: "assistant", text: "Done" })],
95
+ finishReason: "stop",
96
+ usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
97
+ warnings: [],
98
+ }));
99
+ const agent = new Agent({
100
+ id: "test-agent",
101
+ name: "Test",
102
+ instructions: "Test",
103
+ model,
104
+ });
105
+ const kernl = new Kernl({ storage: { db: storage } });
106
+ kernl.register(agent);
107
+ await agent.run("Hello", { threadId: "non-existent" });
108
+ // Should have created new thread with the specified tid
109
+ const threads = await storage.threads.list();
110
+ expect(threads).toHaveLength(1);
111
+ expect(threads[0].tid).toBe("non-existent");
112
+ });
113
+ it("should resume existing thread from storage", async () => {
114
+ const storage = new InMemoryStorage();
115
+ const model = createMockModel(async () => ({
116
+ content: [message({ role: "assistant", text: "Response" })],
117
+ finishReason: "stop",
118
+ usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
119
+ warnings: [],
120
+ }));
121
+ const agent = new Agent({
122
+ id: "test-agent",
123
+ name: "Test",
124
+ instructions: "Test",
125
+ model,
126
+ });
127
+ const kernl = new Kernl({ storage: { db: storage } });
128
+ kernl.register(agent);
129
+ const tid = "resume-thread";
130
+ // First run
131
+ await agent.run("First", { threadId: tid });
132
+ const firstHistory = await storage.threads.history(tid);
133
+ const firstEventCount = firstHistory.length;
134
+ expect(firstEventCount).toBeGreaterThanOrEqual(2); // user + assistant
135
+ // Second run (resume)
136
+ await agent.run("Second", { threadId: tid });
137
+ const secondHistory = await storage.threads.history(tid);
138
+ const secondEventCount = secondHistory.length;
139
+ // Should have more events (added user + assistant from second run)
140
+ expect(secondEventCount).toBeGreaterThanOrEqual(firstEventCount + 2);
141
+ });
142
+ });
143
+ describe("String vs array input", () => {
144
+ it("should handle string input (converted to message)", async () => {
145
+ const storage = new InMemoryStorage();
146
+ const model = createMockModel(async () => ({
147
+ content: [message({ role: "assistant", text: "Done" })],
148
+ finishReason: "stop",
149
+ usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
150
+ warnings: [],
151
+ }));
152
+ const agent = new Agent({
153
+ id: "test-agent",
154
+ name: "Test",
155
+ instructions: "Test",
156
+ model,
157
+ });
158
+ const kernl = new Kernl({ storage: { db: storage } });
159
+ kernl.register(agent);
160
+ await agent.run("Hello world");
161
+ const threads = await storage.threads.list();
162
+ const events = await storage.threads.history(threads[0].tid);
163
+ // Find the user message (first event should be user message)
164
+ const userMessage = events.find((e) => e.role === "user");
165
+ expect(userMessage).toMatchObject({
166
+ kind: "message",
167
+ role: "user",
168
+ content: [{ kind: "text", text: "Hello world" }],
169
+ });
170
+ });
171
+ it("should handle array input (used as-is)", async () => {
172
+ const storage = new InMemoryStorage();
173
+ const model = createMockModel(async () => ({
174
+ content: [message({ role: "assistant", text: "Done" })],
175
+ finishReason: "stop",
176
+ usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
177
+ warnings: [],
178
+ }));
179
+ const agent = new Agent({
180
+ id: "test-agent",
181
+ name: "Test",
182
+ instructions: "Test",
183
+ model,
184
+ });
185
+ const kernl = new Kernl({ storage: { db: storage } });
186
+ kernl.register(agent);
187
+ const input = [message({ role: "user", text: "Custom" })];
188
+ await agent.run(input);
189
+ const threads = await storage.threads.list();
190
+ const events = await storage.threads.history(threads[0].tid);
191
+ // Find the user message
192
+ const userMessage = events.find((e) => e.role === "user");
193
+ expect(userMessage).toMatchObject({
194
+ kind: "message",
195
+ role: "user",
196
+ content: [{ kind: "text", text: "Custom" }],
197
+ });
198
+ });
199
+ });
200
+ });
201
+ describe("Agent.stream() lifecycle", () => {
202
+ it("should yield stream-start event first", async () => {
203
+ const model = createMockModel(async () => ({
204
+ content: [message({ role: "assistant", text: "Done" })],
205
+ finishReason: "stop",
206
+ usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
207
+ warnings: [],
208
+ }));
209
+ const agent = new Agent({
210
+ id: "test-agent",
211
+ name: "Test",
212
+ instructions: "Test",
213
+ model,
214
+ });
215
+ const kernl = new Kernl();
216
+ kernl.register(agent);
217
+ const events = [];
218
+ for await (const event of agent.stream("Hello")) {
219
+ events.push(event);
220
+ }
221
+ expect(events[0]).toEqual({ kind: "stream-start" });
222
+ });
223
+ it("should have same persistence behavior as run()", async () => {
224
+ const storage = new InMemoryStorage();
225
+ const model = createMockModel(async () => ({
226
+ content: [message({ role: "assistant", text: "Done" })],
227
+ finishReason: "stop",
228
+ usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
229
+ warnings: [],
230
+ }));
231
+ const agent = new Agent({
232
+ id: "test-agent",
233
+ name: "Test",
234
+ instructions: "Test",
235
+ model,
236
+ });
237
+ const kernl = new Kernl({ storage: { db: storage } });
238
+ kernl.register(agent);
239
+ const events = [];
240
+ for await (const event of agent.stream("Hello")) {
241
+ events.push(event);
242
+ }
243
+ // Should have persisted like run()
244
+ const threads = await storage.threads.list();
245
+ expect(threads).toHaveLength(1);
246
+ const history = await storage.threads.history(threads[0].tid);
247
+ expect(history.length).toBeGreaterThan(0);
248
+ // Should have streamed events
249
+ expect(events).toEqual(expect.arrayContaining([
250
+ { kind: "stream-start" },
251
+ expect.objectContaining({ kind: "message" }),
252
+ ]));
253
+ });
254
+ });
255
+ describe("Agent.threads helper", () => {
256
+ it("should throw MisconfiguredError when agent is not bound to kernl", () => {
257
+ const model = createMockModel(async () => ({
258
+ content: [message({ role: "assistant", text: "Done" })],
259
+ finishReason: "stop",
260
+ usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
261
+ warnings: [],
262
+ }));
263
+ const agent = new Agent({
264
+ id: "test-agent",
265
+ name: "Test",
266
+ instructions: "Test",
267
+ model,
268
+ });
269
+ expect(() => agent.threads).toThrow(MisconfiguredError);
270
+ });
271
+ it("should list only threads for this agent", async () => {
272
+ const storage = new InMemoryStorage();
273
+ const model = createMockModel(async () => ({
274
+ content: [message({ role: "assistant", text: "Done" })],
275
+ finishReason: "stop",
276
+ usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
277
+ warnings: [],
278
+ }));
279
+ const agentA = new Agent({
280
+ id: "agent-a",
281
+ name: "Agent A",
282
+ instructions: "Test",
283
+ model,
284
+ });
285
+ const agentB = new Agent({
286
+ id: "agent-b",
287
+ name: "Agent B",
288
+ instructions: "Test",
289
+ model,
290
+ });
291
+ const kernl = new Kernl({ storage: { db: storage } });
292
+ kernl.register(agentA);
293
+ kernl.register(agentB);
294
+ await agentA.run("Hello from A");
295
+ await agentB.run("Hello from B");
296
+ await agentB.run("Another from B");
297
+ const threadsAPage = await agentA.threads.list();
298
+ const threadsBPage = await agentB.threads.list();
299
+ const threadsA = await threadsAPage.collect();
300
+ const threadsB = await threadsBPage.collect();
301
+ expect(threadsA).toHaveLength(1);
302
+ expect(threadsB.length).toBeGreaterThanOrEqual(2);
303
+ for (const thread of threadsA) {
304
+ expect(thread.agentId).toBe("agent-a");
305
+ }
306
+ for (const thread of threadsB) {
307
+ expect(thread.agentId).toBe("agent-b");
308
+ }
309
+ });
310
+ it("should expose thread history via threads.history()", async () => {
311
+ const storage = new InMemoryStorage();
312
+ const model = createMockModel(async () => ({
313
+ content: [message({ role: "assistant", text: "Done" })],
314
+ finishReason: "stop",
315
+ usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
316
+ warnings: [],
317
+ }));
318
+ const agent = new Agent({
319
+ id: "test-agent",
320
+ name: "Test",
321
+ instructions: "Test",
322
+ model,
323
+ });
324
+ const kernl = new Kernl({ storage: { db: storage } });
325
+ kernl.register(agent);
326
+ await agent.run("Hello");
327
+ const threadsPage = await agent.threads.list();
328
+ const threads = await threadsPage.collect();
329
+ expect(threads).toHaveLength(1);
330
+ const tid = threads[0].tid;
331
+ const events = await agent.threads.history(tid, { order: "asc" });
332
+ // Expect exactly two events: user message then assistant message
333
+ expect(events).toHaveLength(2);
334
+ const [userEvent, assistantEvent] = events;
335
+ // Common headers
336
+ expect(userEvent.tid).toBe(tid);
337
+ expect(assistantEvent.tid).toBe(tid);
338
+ expect(userEvent.seq).toBe(0);
339
+ expect(assistantEvent.seq).toBe(1);
340
+ // User message
341
+ expect(userEvent.kind).toBe("message");
342
+ // @ts-expect-error ThreadEvent extends LanguageModelItem at runtime
343
+ expect(userEvent.role).toBe("user");
344
+ // @ts-expect-error ThreadEvent extends LanguageModelItem at runtime
345
+ expect(userEvent.content).toEqual([
346
+ { kind: "text", text: "Hello" },
347
+ ]);
348
+ // Assistant message
349
+ expect(assistantEvent.kind).toBe("message");
350
+ // @ts-expect-error ThreadEvent extends LanguageModelItem at runtime
351
+ expect(assistantEvent.role).toBe("assistant");
352
+ // @ts-expect-error ThreadEvent extends LanguageModelItem at runtime
353
+ expect(assistantEvent.content).toEqual([
354
+ { kind: "text", text: "Done" },
355
+ ]);
356
+ });
357
+ });
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":""}
package/dist/agent.d.ts CHANGED
@@ -1,13 +1,13 @@
1
- import { LanguageModel, LanguageModelRequestSettings } from "@kernl-sdk/protocol";
2
- import type { Context, UnknownContext } from "./context";
3
- import { InputGuardrail, OutputGuardrail } from "./guardrail";
4
- import { AgentHooks } from "./lifecycle";
5
- import { BaseToolkit } from "./tool/toolkit";
6
- import { Tool } from "./tool";
7
- import type { AgentConfig, AgentResponseType } from "./types/agent";
8
- import type { TextResponse, ThreadOptions, ThreadExecuteResult, ThreadStreamEvent } from "./types/thread";
9
- import type { Kernl } from "./kernl";
10
- import type { ResolvedAgentResponse } from "./guardrail";
1
+ import { LanguageModel, LanguageModelItem, LanguageModelRequestSettings } from "@kernl-sdk/protocol";
2
+ import type { Kernl } from "./kernl.js";
3
+ import type { RThreadsListParams, RThreadCreateParams, RThreadGetOptions, RThreadHistoryParams, RThreadUpdateParams } from "./api/resources/threads/types.js";
4
+ import type { Context, UnknownContext } from "./context.js";
5
+ import { Tool } from "./tool/index.js";
6
+ import { BaseToolkit } from "./tool/toolkit.js";
7
+ import { InputGuardrail, OutputGuardrail, type ResolvedAgentResponse } from "./guardrail.js";
8
+ import { AgentHooks } from "./lifecycle.js";
9
+ import type { AgentConfig, AgentResponseType } from "./types/agent.js";
10
+ import type { TextResponse, ThreadExecuteOptions, ThreadExecuteResult, ThreadStreamEvent } from "./types/thread.js";
11
11
  export declare class Agent<TContext = UnknownContext, TResponse extends AgentResponseType = TextResponse> extends AgentHooks<TContext, TResponse> implements AgentConfig<TContext, TResponse> {
12
12
  private kernl?;
13
13
  id: string;
@@ -29,15 +29,23 @@ export declare class Agent<TContext = UnknownContext, TResponse extends AgentRes
29
29
  bind(kernl: Kernl): void;
30
30
  /**
31
31
  * Blocking execution - spawns or resumes thread and waits for completion
32
+ *
33
+ * @throws {RuntimeError} If the specified thread is already running (concurrent execution not allowed)
34
+ * @throws {MisconfiguredError} If the agent is not bound to a kernl instance
32
35
  */
33
- run(instructions: string, options?: ThreadOptions<TContext>): Promise<ThreadExecuteResult<ResolvedAgentResponse<TResponse>>>;
36
+ run(input: string | LanguageModelItem[], options?: ThreadExecuteOptions<TContext>): Promise<ThreadExecuteResult<ResolvedAgentResponse<TResponse>>>;
34
37
  /**
35
38
  * Streaming execution - spawns or resumes thread and returns async iterator
36
39
  *
37
40
  * NOTE: streaming probably won't make sense in scheduling contexts so spawnStream etc. won't make sense
41
+ *
42
+ * @throws {RuntimeError} If the specified thread is already running (concurrent execution not allowed)
43
+ * @throws {MisconfiguredError} If the agent is not bound to a kernl instance
38
44
  */
39
- stream(instructions: string, options?: ThreadOptions<TContext>): AsyncIterable<ThreadStreamEvent>;
45
+ stream(input: string | LanguageModelItem[], options?: ThreadExecuteOptions<TContext>): AsyncIterable<ThreadStreamEvent>;
40
46
  /**
47
+ * @internal
48
+ *
41
49
  * Get a specific tool by ID from all toolkits.
42
50
  *
43
51
  * @param id The tool ID to look up
@@ -45,6 +53,8 @@ export declare class Agent<TContext = UnknownContext, TResponse extends AgentRes
45
53
  */
46
54
  tool(id: string): Tool<TContext> | undefined;
47
55
  /**
56
+ * @internal
57
+ *
48
58
  * Get all tools available from all toolkits for the given context.
49
59
  * Checks for duplicate tool IDs across toolkits and throws an error if found.
50
60
  *
@@ -56,5 +66,18 @@ export declare class Agent<TContext = UnknownContext, TResponse extends AgentRes
56
66
  * @throws {MisconfiguredError} If duplicate tool IDs are found across toolkits
57
67
  */
58
68
  tools(context: Context<TContext>): Promise<Tool<TContext>[]>;
69
+ /**
70
+ * Thread management scoped to this agent.
71
+ *
72
+ * Convenience wrapper around kernl.threads that automatically filters to this agent's threads.
73
+ */
74
+ get threads(): {
75
+ get: (tid: string, options?: RThreadGetOptions) => Promise<import("./index.js").Thread | null>;
76
+ list: (params?: Omit<RThreadsListParams, "agentId">) => Promise<import("./api/pagination/cursor.js").CursorPage<import("./index.js").Thread, RThreadsListParams>>;
77
+ delete: (tid: string) => Promise<void>;
78
+ history: (tid: string, params?: RThreadHistoryParams) => Promise<import("./index.js").ThreadEvent[]>;
79
+ create: (params: Omit<RThreadCreateParams, "agentId" | "model">) => Promise<import("./index.js").Thread>;
80
+ update: (tid: string, patch: RThreadUpdateParams) => Promise<import("./index.js").Thread | null>;
81
+ };
59
82
  }
60
83
  //# sourceMappingURL=agent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,4BAA4B,EAE7B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAK9B,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,qBAAa,KAAK,CACd,QAAQ,GAAG,cAAc,EACzB,SAAS,SAAS,iBAAiB,GAAG,YAAY,CAEpD,SAAQ,UAAU,CAAC,QAAQ,EAAE,SAAS,CACtC,YAAW,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC;IAE3C,OAAO,CAAC,KAAK,CAAC,CAAQ;IAEtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAEvE,KAAK,EAAE,aAAa,CAAC;IACrB,aAAa,EAAE,4BAA4B,CAAC;IAC5C,QAAQ,EAAE,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;IAGlC,UAAU,EAAE;QACV,KAAK,EAAE,cAAc,EAAE,CAAC;QACxB,MAAM,EAAE,eAAe,CAAC,iBAAiB,CAAC,EAAE,CAAC;KAC9C,CAAC;IACF,YAAY,EAAE,SAAS,CAAuB;IAC9C,eAAe,EAAE,OAAO,CAAC;gBAgBb,MAAM,EAAE,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC;IA0CpD;;OAEG;IACH,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAIxB;;OAEG;IACG,GAAG,CACP,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,GAChC,OAAO,CAAC,mBAAmB,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC,CAAC;IAqBjE;;;;OAIG;IACI,MAAM,CACX,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,GAChC,aAAa,CAAC,iBAAiB,CAAC;IAsBnC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,SAAS;IAQ5C;;;;;;;;;;OAUG;IACG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;CAoBnE"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,aAAa,EACb,iBAAiB,EACjB,4BAA4B,EAC7B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,qBAAqB,EAC3B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AAExB,qBAAa,KAAK,CACd,QAAQ,GAAG,cAAc,EACzB,SAAS,SAAS,iBAAiB,GAAG,YAAY,CAEpD,SAAQ,UAAU,CAAC,QAAQ,EAAE,SAAS,CACtC,YAAW,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC;IAE3C,OAAO,CAAC,KAAK,CAAC,CAAQ;IAEtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAEvE,KAAK,EAAE,aAAa,CAAC;IACrB,aAAa,EAAE,4BAA4B,CAAC;IAC5C,QAAQ,EAAE,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;IAGlC,UAAU,EAAE;QACV,KAAK,EAAE,cAAc,EAAE,CAAC;QACxB,MAAM,EAAE,eAAe,CAAC,iBAAiB,CAAC,EAAE,CAAC;KAC9C,CAAC;IACF,YAAY,EAAE,SAAS,CAAuB;IAC9C,eAAe,EAAE,OAAO,CAAC;gBAgBb,MAAM,EAAE,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC;IA0CpD;;OAEG;IACH,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAIxB;;;;;OAKG;IACG,GAAG,CACP,KAAK,EAAE,MAAM,GAAG,iBAAiB,EAAE,EACnC,OAAO,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,GACvC,OAAO,CAAC,mBAAmB,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC,CAAC;IAkDjE;;;;;;;OAOG;IACI,MAAM,CACX,KAAK,EAAE,MAAM,GAAG,iBAAiB,EAAE,EACnC,OAAO,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,GACvC,aAAa,CAAC,iBAAiB,CAAC;IAmDnC;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,SAAS;IAQ5C;;;;;;;;;;;;OAYG;IACG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;IAqBlE;;;;OAIG;IACH,IAAI,OAAO;mBAWI,MAAM,YAAY,iBAAiB;wBAE/B,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC;sBAEpC,MAAM;uBACL,MAAM,WAAW,oBAAoB;yBAEnC,IAAI,CAAC,mBAAmB,EAAE,SAAS,GAAG,OAAO,CAAC;sBASjD,MAAM,SAAS,mBAAmB;MAGnD;CACF"}
package/dist/agent.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { message, } from "@kernl-sdk/protocol";
2
- import { AgentHooks } from "./lifecycle";
3
- import { Thread } from "./thread";
4
- import { MisconfiguredError } from "./lib/error";
2
+ import { Thread } from "./thread/index.js";
3
+ import { AgentHooks } from "./lifecycle.js";
4
+ import { MisconfiguredError, RuntimeError } from "./lib/error.js";
5
5
  export class Agent extends AgentHooks {
6
6
  kernl;
7
7
  id;
@@ -71,44 +71,102 @@ export class Agent extends AgentHooks {
71
71
  }
72
72
  /**
73
73
  * Blocking execution - spawns or resumes thread and waits for completion
74
+ *
75
+ * @throws {RuntimeError} If the specified thread is already running (concurrent execution not allowed)
76
+ * @throws {MisconfiguredError} If the agent is not bound to a kernl instance
74
77
  */
75
- async run(instructions, options) {
78
+ async run(input, options) {
76
79
  if (!this.kernl) {
77
80
  throw new MisconfiguredError(`Agent ${this.id} not bound to kernl. Call kernl.register(agent) first.`);
78
81
  }
79
- const m = message({ role: "user", text: instructions });
82
+ const items = typeof input === "string"
83
+ ? [message({ role: "user", text: input })]
84
+ : input;
80
85
  const tid = options?.threadId;
81
- // NOTE: may end up moving this to the kernl
82
- let thread = tid ? this.kernl.threads.get(tid) : null;
86
+ let thread = null;
87
+ if (tid) {
88
+ // no concurrent execution of same thread - correctness contract
89
+ // TODO: race condition - need to check again after async storage.get()
90
+ if (this.kernl.athreads.has(tid)) {
91
+ throw new RuntimeError(`Thread ${tid} is already running.`);
92
+ }
93
+ // try to resume from storage if available
94
+ if (this.kernl.storage?.threads) {
95
+ thread = (await this.kernl.storage.threads.get(tid, {
96
+ history: true,
97
+ }));
98
+ }
99
+ }
100
+ // create new thread if not found in storage or no tid provided
83
101
  if (!thread) {
84
- thread = new Thread(this.kernl, this, [m], options);
102
+ thread = new Thread({
103
+ agent: this,
104
+ input: items,
105
+ context: options?.context,
106
+ model: options?.model,
107
+ task: options?.task,
108
+ tid: options?.threadId,
109
+ namespace: options?.namespace,
110
+ storage: this.kernl.storage?.threads,
111
+ });
85
112
  return this.kernl.spawn(thread);
86
113
  }
87
- thread.append(m);
114
+ // resume existing thread from storage
115
+ thread.append(...items);
88
116
  return this.kernl.schedule(thread);
89
117
  }
90
118
  /**
91
119
  * Streaming execution - spawns or resumes thread and returns async iterator
92
120
  *
93
121
  * NOTE: streaming probably won't make sense in scheduling contexts so spawnStream etc. won't make sense
122
+ *
123
+ * @throws {RuntimeError} If the specified thread is already running (concurrent execution not allowed)
124
+ * @throws {MisconfiguredError} If the agent is not bound to a kernl instance
94
125
  */
95
- async *stream(instructions, options) {
126
+ async *stream(input, options) {
96
127
  if (!this.kernl) {
97
128
  throw new MisconfiguredError(`Agent ${this.id} not bound to kernl. Call kernl.register(agent) first.`);
98
129
  }
99
- const m = message({ role: "user", text: instructions });
130
+ const items = typeof input === "string"
131
+ ? [message({ role: "user", text: input })]
132
+ : input;
100
133
  const tid = options?.threadId;
101
- // NOTE: may end up moving this to the kernl
102
- let thread = tid ? this.kernl.threads.get(tid) : null;
134
+ let thread = null;
135
+ if (tid) {
136
+ // no concurrent execution of same thread - correctness contract
137
+ // TODO: race condition - need to check again after async storage.get()
138
+ if (this.kernl.athreads.has(tid)) {
139
+ throw new RuntimeError(`Thread ${tid} is already running.`);
140
+ }
141
+ // try to resume from storage if available
142
+ if (this.kernl.storage?.threads) {
143
+ thread = (await this.kernl.storage.threads.get(tid, {
144
+ history: true,
145
+ }));
146
+ }
147
+ }
148
+ // create new thread if not found in storage or no tid provided
103
149
  if (!thread) {
104
- thread = new Thread(this.kernl, this, [m], options);
150
+ thread = new Thread({
151
+ agent: this,
152
+ input: items,
153
+ context: options?.context,
154
+ model: options?.model,
155
+ task: options?.task,
156
+ tid: options?.threadId,
157
+ namespace: options?.namespace,
158
+ storage: this.kernl.storage?.threads,
159
+ });
105
160
  yield* this.kernl.spawnStream(thread);
106
161
  return;
107
162
  }
108
- thread.append(m);
163
+ // resume existing thread from storage
164
+ thread.append(...items);
109
165
  yield* this.kernl.scheduleStream(thread);
110
166
  }
111
167
  /**
168
+ * @internal
169
+ *
112
170
  * Get a specific tool by ID from all toolkits.
113
171
  *
114
172
  * @param id The tool ID to look up
@@ -123,6 +181,8 @@ export class Agent extends AgentHooks {
123
181
  return undefined;
124
182
  }
125
183
  /**
184
+ * @internal
185
+ *
126
186
  * Get all tools available from all toolkits for the given context.
127
187
  * Checks for duplicate tool IDs across toolkits and throws an error if found.
128
188
  *
@@ -147,4 +207,31 @@ export class Agent extends AgentHooks {
147
207
  }
148
208
  return allTools;
149
209
  }
210
+ /**
211
+ * Thread management scoped to this agent.
212
+ *
213
+ * Convenience wrapper around kernl.threads that automatically filters to this agent's threads.
214
+ */
215
+ get threads() {
216
+ if (!this.kernl) {
217
+ throw new MisconfiguredError(`Agent ${this.id} not bound to kernl. Call kernl.register(agent) first.`);
218
+ }
219
+ const agentId = this.id;
220
+ const kthreads = this.kernl.threads;
221
+ return {
222
+ get: (tid, options) => kthreads.get(tid, options),
223
+ list: (params = {}) => kthreads.list({ ...params, agentId }),
224
+ delete: (tid) => kthreads.delete(tid),
225
+ history: (tid, params) => kthreads.history(tid, params),
226
+ create: (params) => kthreads.create({
227
+ ...params,
228
+ agentId,
229
+ model: {
230
+ provider: this.model.provider,
231
+ modelId: this.model.modelId,
232
+ },
233
+ }),
234
+ update: (tid, patch) => kthreads.update(tid, patch),
235
+ };
236
+ }
150
237
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cursor-page.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-page.test.d.ts","sourceRoot":"","sources":["../../../src/api/__tests__/cursor-page.test.ts"],"names":[],"mappings":""}