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,86 @@
1
+ import { AbstractPage, type PageParamsBase } from "./base";
2
+
3
+ export interface CursorPageParams extends PageParamsBase {
4
+ /**
5
+ * Pagination cursor returned from a previous page.
6
+ * If omitted, starts from the beginning of the collection.
7
+ */
8
+ cursor?: string;
9
+ }
10
+
11
+ export interface CursorPageResponse<T> {
12
+ data: T[];
13
+ /**
14
+ * Cursor for the next page, or null if there is no next page.
15
+ */
16
+ next: string | null;
17
+ /**
18
+ * True if this is the last page (no further pages).
19
+ */
20
+ last: boolean;
21
+ }
22
+
23
+ export class CursorPage<
24
+ T,
25
+ TParams extends CursorPageParams = CursorPageParams,
26
+ > extends AbstractPage<T, TParams, CursorPageResponse<T>> {
27
+ data: T[];
28
+ private readonly _next: string | null;
29
+ private readonly _last: boolean;
30
+
31
+ constructor(args: {
32
+ params: TParams;
33
+ response: CursorPageResponse<T>;
34
+ loader: (params: TParams) => Promise<CursorPageResponse<T>>;
35
+ }) {
36
+ super(args);
37
+ this.data = args.response.data ?? [];
38
+ this._next = args.response.next;
39
+ this._last = args.response.last;
40
+ }
41
+
42
+ /**
43
+ * All items contained in this page.
44
+ */
45
+ get items(): T[] {
46
+ return this.data;
47
+ }
48
+
49
+ /**
50
+ * True if this is the last page in the sequence.
51
+ *
52
+ * When `last` is true, `next()` will return null.
53
+ */
54
+ get last(): boolean {
55
+ if (this._last) return true;
56
+ if (!this._next) return true;
57
+ return this.data.length === 0;
58
+ }
59
+
60
+ /**
61
+ * Fetch the next page, or null if there is no next page.
62
+ */
63
+ async next(): Promise<this | null> {
64
+ if (this.last) {
65
+ return null;
66
+ }
67
+
68
+ if (!this._next) {
69
+ return null;
70
+ }
71
+
72
+ const nextParams: TParams = {
73
+ ...(this.params as TParams),
74
+ cursor: this._next,
75
+ };
76
+
77
+ const res = await this.loader(nextParams);
78
+ const page = new CursorPage<T, TParams>({
79
+ params: nextParams,
80
+ response: res,
81
+ loader: this.loader,
82
+ }) as this;
83
+
84
+ return page;
85
+ }
86
+ }
@@ -0,0 +1,89 @@
1
+ import { AbstractPage, type PageParamsBase } from "./base";
2
+
3
+ export interface OffsetPageParams extends PageParamsBase {
4
+ /**
5
+ * Number of items to skip from the beginning of the collection.
6
+ */
7
+ offset?: number;
8
+ }
9
+
10
+ export interface OffsetPageResponse<T> {
11
+ data: T[];
12
+ offset: number;
13
+ limit: number;
14
+ /**
15
+ * Optional total count when the underlying store can provide it.
16
+ */
17
+ total?: number;
18
+ }
19
+
20
+ export class OffsetPage<
21
+ T,
22
+ TParams extends OffsetPageParams = OffsetPageParams,
23
+ > extends AbstractPage<T, TParams, OffsetPageResponse<T>> {
24
+ data: T[];
25
+ offset: number;
26
+ limit: number;
27
+ total?: number;
28
+
29
+ constructor(args: {
30
+ params: TParams;
31
+ response: OffsetPageResponse<T>;
32
+ loader: (params: TParams) => Promise<OffsetPageResponse<T>>;
33
+ }) {
34
+ super(args);
35
+ this.data = args.response.data ?? [];
36
+ this.offset = args.response.offset;
37
+ this.limit = args.response.limit;
38
+ this.total = args.response.total;
39
+ }
40
+
41
+ /**
42
+ * All items contained in this page.
43
+ */
44
+ get items(): T[] {
45
+ return this.data;
46
+ }
47
+
48
+ /**
49
+ * True if this is the last page in the sequence.
50
+ *
51
+ * When `last` is true, `next()` will return null.
52
+ */
53
+ get last(): boolean {
54
+ if (this.data.length === 0) {
55
+ return true;
56
+ }
57
+
58
+ const next = (this.offset ?? 0) + (this.limit ?? 0);
59
+ if (this.total != null && next >= this.total) {
60
+ return true;
61
+ }
62
+
63
+ return false;
64
+ }
65
+
66
+ /**
67
+ * Fetch the next page, or null if there is no next page.
68
+ */
69
+ async next(): Promise<this | null> {
70
+ if (this.last) {
71
+ return null;
72
+ }
73
+
74
+ const next = (this.offset ?? 0) + (this.limit ?? 0);
75
+ const nextParams: TParams = {
76
+ ...(this.params as TParams),
77
+ offset: next,
78
+ };
79
+
80
+ const res = await this.loader(nextParams);
81
+ const page = new OffsetPage<T, TParams>({
82
+ params: nextParams,
83
+ response: res,
84
+ loader: this.loader,
85
+ }) as this;
86
+
87
+ return page;
88
+ }
89
+ }
@@ -0,0 +1,26 @@
1
+ import type { ThreadStore } from "@/storage";
2
+ import { isPublicEvent } from "@/thread/utils";
3
+ import type { MThreadEvent } from "@/api/models";
4
+ import type { ThreadEvent } from "@/types/thread";
5
+
6
+ /**
7
+ * Events subresource for threads.
8
+ *
9
+ * Provides access to the event log for an individual thread, filtered down to
10
+ * events that make sense to surface to callers (messages, tool calls/results, etc.).
11
+ */
12
+ export class RThreadEvents {
13
+ constructor(private readonly store: ThreadStore) {}
14
+
15
+ /**
16
+ * List events for a thread.
17
+ *
18
+ * Returns only public events – internal system events are filtered out.
19
+ *
20
+ * @param tid - Thread ID
21
+ */
22
+ async list(tid: string): Promise<MThreadEvent[]> {
23
+ const events: ThreadEvent[] = await this.store.history(tid);
24
+ return events.filter(isPublicEvent).map((e) => e as MThreadEvent);
25
+ }
26
+ }
@@ -0,0 +1,9 @@
1
+ export { RThreads } from "./threads";
2
+ export { RThreadEvents } from "./events";
3
+ export type {
4
+ RThreadsListParams,
5
+ RThreadGetOptions,
6
+ RThreadHistoryParams,
7
+ RThreadCreateParams,
8
+ RThreadUpdateParams,
9
+ } from "./types";
@@ -0,0 +1,256 @@
1
+ import type {
2
+ ThreadStore,
3
+ ThreadListOptions,
4
+ ThreadHistoryOptions,
5
+ } from "@/storage";
6
+ import type { ThreadEvent } from "@/types/thread";
7
+ import { isPublicEvent } from "@/thread/utils";
8
+ import { Context } from "@/context";
9
+ import { randomID } from "@kernl-sdk/shared/lib";
10
+ import { RUNNING } from "@kernl-sdk/protocol";
11
+
12
+ import type { MThread, MThreadEvent } from "@/api/models";
13
+ import { CursorPage, type CursorPageResponse } from "@/api/pagination/cursor";
14
+
15
+ import { RThreadEvents } from "./events";
16
+ import type {
17
+ RThreadCreateParams,
18
+ RThreadGetOptions,
19
+ RThreadHistoryParams,
20
+ RThreadsListParams,
21
+ RThreadUpdateParams,
22
+ } from "./types";
23
+ import { MThreadCodec, ThreadsFilterCodec, ThreadsOrderCodec } from "./utils";
24
+
25
+ /**
26
+ * Threads resource.
27
+ *
28
+ * Provides a structured API for listing, inspecting, and deleting threads.
29
+ */
30
+ export class RThreads {
31
+ readonly events: RThreadEvents;
32
+
33
+ constructor(private readonly store: ThreadStore) {
34
+ this.events = new RThreadEvents(store);
35
+ }
36
+
37
+ /**
38
+ * Get a single thread by ID.
39
+ *
40
+ * When `options.history` is set, the returned thread will include a
41
+ * `history` field containing public events for that thread.
42
+ */
43
+ async get(tid: string, options?: RThreadGetOptions): Promise<MThread | null> {
44
+ const thread = await this.store.get(tid);
45
+ if (!thread) return null;
46
+
47
+ const model = MThreadCodec.encode(thread);
48
+
49
+ if (options?.history) {
50
+ const historyParams =
51
+ options.history === true ? undefined : options.history;
52
+ const history = await this.history(tid, historyParams);
53
+ model.history = history; // attach history lazily so callers can opt-in.
54
+ }
55
+
56
+ return model;
57
+ }
58
+
59
+ /**
60
+ * Create a new thread.
61
+ */
62
+ async create(params: RThreadCreateParams): Promise<MThread> {
63
+ const now = new Date();
64
+ const tid = params.tid ?? `tid_${randomID()}`;
65
+ const namespace = params.namespace ?? "kernl";
66
+
67
+ // merge caller-supplied metadata with optional title
68
+ const baseMetadata: Record<string, unknown> = {
69
+ ...(params.metadata ?? {}),
70
+ };
71
+
72
+ if (params.title !== undefined) {
73
+ const trimmed = params.title.trim();
74
+ if (trimmed) {
75
+ baseMetadata.title = trimmed;
76
+ } else {
77
+ delete baseMetadata.title;
78
+ }
79
+ }
80
+
81
+ const metadata: Record<string, unknown> | null =
82
+ Object.keys(baseMetadata).length > 0 ? baseMetadata : null;
83
+
84
+ const thread = await this.store.insert({
85
+ id: tid,
86
+ namespace,
87
+ agentId: params.agentId,
88
+ model: `${params.model.provider}/${params.model.modelId}`,
89
+ context: params.context,
90
+ parentTaskId: params.parentTaskId ?? null,
91
+ metadata,
92
+ createdAt: now,
93
+ updatedAt: now,
94
+ });
95
+
96
+ return MThreadCodec.encode(thread);
97
+ }
98
+
99
+ /**
100
+ * List threads with optional filtering and cursor-based pagination.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * const threads = await kernl.threads.list({ limit: 20 });
105
+ *
106
+ * for await (const thread of threads) {
107
+ * console.log(thread.tid);
108
+ * }
109
+ * ```
110
+ */
111
+ async list(
112
+ params: RThreadsListParams = {},
113
+ ): Promise<CursorPage<MThread, RThreadsListParams>> {
114
+ const loader = async (
115
+ p: RThreadsListParams,
116
+ ): Promise<CursorPageResponse<MThread>> => {
117
+ const offset = p.cursor ? Number(p.cursor) : 0;
118
+ const limit = p.limit;
119
+ const effectiveLimit = limit !== undefined ? limit + 1 : undefined;
120
+
121
+ const filter = ThreadsFilterCodec.encode(p);
122
+ const order = ThreadsOrderCodec.encode(p.order);
123
+
124
+ const options: ThreadListOptions = {
125
+ filter,
126
+ order,
127
+ limit: effectiveLimit,
128
+ offset,
129
+ };
130
+
131
+ const threads = await this.store.list(options);
132
+
133
+ if (limit === undefined) {
134
+ // No limit requested → treat as a single, non-paginated page.
135
+ const data = threads.map((t) => MThreadCodec.encode(t));
136
+ return {
137
+ data,
138
+ next: null,
139
+ last: true,
140
+ };
141
+ }
142
+
143
+ const hasExtra = threads.length > limit;
144
+ const pageThreads = hasExtra ? threads.slice(0, limit) : threads;
145
+ const data = pageThreads.map((t) => MThreadCodec.encode(t));
146
+
147
+ const nextOffset = offset + data.length;
148
+ const last = !hasExtra || data.length === 0;
149
+ const next = last ? null : String(nextOffset);
150
+
151
+ return { data, next, last };
152
+ };
153
+
154
+ const response = await loader(params);
155
+
156
+ return new CursorPage<MThread, RThreadsListParams>({
157
+ params,
158
+ response,
159
+ loader,
160
+ });
161
+ }
162
+
163
+ /**
164
+ * Delete a thread and all associated events.
165
+ */
166
+ async delete(tid: string): Promise<void> {
167
+ await this.store.delete(tid);
168
+ }
169
+
170
+ /**
171
+ * Convenience wrapper around `events.list()` for fetching a thread's history.
172
+ *
173
+ * History is returned in descending sequence order by default (latest first).
174
+ */
175
+ async history(
176
+ tid: string,
177
+ params?: RThreadHistoryParams,
178
+ ): Promise<MThreadEvent[]> {
179
+ const opts: ThreadHistoryOptions = {
180
+ after: params?.after,
181
+ limit: params?.limit,
182
+ order: params?.order ?? "desc",
183
+ kinds: params?.kinds,
184
+ };
185
+
186
+ const events: ThreadEvent[] = await this.store.history(tid, opts);
187
+ return events.filter(isPublicEvent).map((e) => e as MThreadEvent);
188
+ }
189
+
190
+ /**
191
+ * Update mutable thread fields.
192
+ *
193
+ * Currently only supports updating the human-readable `title`, which is
194
+ * stored in thread metadata as `metadata.title`.
195
+ */
196
+ async update(
197
+ tid: string,
198
+ patch: RThreadUpdateParams,
199
+ ): Promise<MThread | null> {
200
+ const current = await this.store.get(tid);
201
+ if (!current) return null;
202
+
203
+ // Prevent context mutation while the thread is running.
204
+ if (patch.context !== undefined && current.state === RUNNING) {
205
+ throw new Error("Cannot update thread context while thread is running");
206
+ }
207
+
208
+ // build context patch
209
+ let contextPatch: Context<unknown> | undefined;
210
+ if ("context" in patch) {
211
+ const nextContextValue =
212
+ patch.context === null
213
+ ? {}
214
+ : (patch.context ?? current.context.context);
215
+ contextPatch = new Context(
216
+ current.namespace,
217
+ nextContextValue as unknown,
218
+ );
219
+ }
220
+
221
+ // build metadata base according to caller intent
222
+ let metadata: Record<string, unknown>;
223
+ if ("metadata" in patch) {
224
+ if (patch.metadata === null) {
225
+ metadata = {};
226
+ } else if (patch.metadata === undefined) {
227
+ metadata = current.metadata ? { ...current.metadata } : {};
228
+ } else {
229
+ metadata = { ...patch.metadata };
230
+ }
231
+ } else {
232
+ metadata = current.metadata ? { ...current.metadata } : {};
233
+ }
234
+
235
+ // apply title overlay on top of metadata
236
+ if ("title" in patch) {
237
+ const value = patch.title;
238
+ const trimmed =
239
+ typeof value === "string" ? value.trim() : (value ?? undefined);
240
+
241
+ if (!trimmed) {
242
+ delete metadata.title; // clear title
243
+ } else {
244
+ metadata.title = trimmed;
245
+ }
246
+ }
247
+
248
+ const nextMetadata = Object.keys(metadata).length > 0 ? metadata : null;
249
+
250
+ const updated = await this.store.update(tid, {
251
+ ...(contextPatch ? { context: contextPatch } : {}),
252
+ metadata: nextMetadata,
253
+ });
254
+ return MThreadCodec.encode(updated);
255
+ }
256
+ }
@@ -0,0 +1,143 @@
1
+ import type { ThreadState } from "@/types/thread";
2
+ import type { SortOrder } from "@/storage";
3
+ import type { CursorPageParams } from "@/api/pagination/cursor";
4
+
5
+ export interface RThreadHistoryParams {
6
+ /**
7
+ * Only return events with seq greater than this value.
8
+ */
9
+ after?: number;
10
+
11
+ /**
12
+ * Maximum number of events to return.
13
+ */
14
+ limit?: number;
15
+
16
+ /**
17
+ * Sort order by sequence number.
18
+ *
19
+ * Defaults to `"desc"` so callers see the latest events first.
20
+ */
21
+ order?: "asc" | "desc";
22
+
23
+ /**
24
+ * Restrict history to specific event kinds, e.g. `["message", "tool-result"]`.
25
+ */
26
+ kinds?: string[];
27
+ }
28
+
29
+ export interface RThreadsListParams extends CursorPageParams {
30
+ namespace?: string;
31
+ agentId?: string;
32
+ state?: ThreadState | ThreadState[];
33
+ parentTaskId?: string;
34
+
35
+ /**
36
+ * Only include threads created after this timestamp.
37
+ */
38
+ after?: Date;
39
+
40
+ /**
41
+ * Only include threads created before this timestamp.
42
+ */
43
+ before?: Date;
44
+
45
+ order?: {
46
+ createdAt?: SortOrder;
47
+ updatedAt?: SortOrder;
48
+ };
49
+ }
50
+
51
+ export interface RThreadGetOptions {
52
+ /**
53
+ * Include the thread's event history on the returned model.
54
+ *
55
+ * - `true` will fetch history with default options (latest-first).
56
+ * - An object lets you override history options (limit, kinds, order, etc.).
57
+ *
58
+ * This is equivalent to calling `kernl.threads.history(tid, opts)` and
59
+ * attaching the result to `thread.history`.
60
+ */
61
+ history?: true | RThreadHistoryParams;
62
+ }
63
+
64
+ /**
65
+ * Parameters for creating a new thread via the public Threads resource.
66
+ *
67
+ * Note: low-level API requires explicit agent + model. For most callers,
68
+ * prefer the agent-scoped helpers (agent.threads.create) which infer these.
69
+ */
70
+ export interface RThreadCreateParams {
71
+ /**
72
+ * Owning agent id for the new thread.
73
+ */
74
+ agentId: string;
75
+
76
+ /**
77
+ * Optional explicit thread id.
78
+ *
79
+ * If omitted, a new id will be generated.
80
+ */
81
+ tid?: string;
82
+
83
+ /**
84
+ * Logical namespace to create the thread in.
85
+ *
86
+ * Defaults to `"kernl"` when not provided.
87
+ */
88
+ namespace?: string;
89
+
90
+ /**
91
+ * Optional human-readable title for the thread.
92
+ */
93
+ title?: string;
94
+
95
+ /**
96
+ * Initial context object for the thread.
97
+ */
98
+ context?: Record<string, unknown>;
99
+
100
+ /**
101
+ * Optional parent task id that spawned this thread, if any.
102
+ */
103
+ parentTaskId?: string | null;
104
+ /**
105
+ * Language model backing this thread.
106
+ */
107
+ model: {
108
+ provider: string;
109
+ modelId: string;
110
+ };
111
+
112
+ /**
113
+ * Arbitrary JSON-serializable metadata to attach to the thread.
114
+ */
115
+ metadata?: Record<string, unknown>;
116
+ }
117
+
118
+ /**
119
+ * Patch for updating caller-owned thread fields.
120
+ *
121
+ * Semantics for all fields:
122
+ * - `undefined` → leave the field unchanged
123
+ * - value (`object` / `string` / etc.) → replace the field
124
+ * - `null` → clear the field (for `title`, clears `metadata.title`)
125
+ */
126
+ export interface RThreadUpdateParams {
127
+ /**
128
+ * Thread context object.
129
+ */
130
+ context?: Record<string, unknown> | null;
131
+
132
+ /**
133
+ * Arbitrary metadata bag attached to the thread.
134
+ */
135
+ metadata?: Record<string, unknown> | null;
136
+
137
+ /**
138
+ * Human-readable title (stored in `metadata.title`).
139
+ */
140
+ title?: string | null;
141
+ }
142
+
143
+
@@ -0,0 +1,104 @@
1
+ import { UnimplementedError, type Codec } from "@kernl-sdk/shared/lib";
2
+
3
+ import type { Thread } from "@/thread";
4
+ import type { ThreadFilter, ThreadListOptions } from "@/storage";
5
+ import type { MThread } from "@/api/models";
6
+ import type { RThreadsListParams } from "./types";
7
+
8
+ /**
9
+ * Converts an internal `Thread` runtime instance into an `MThread` model.
10
+ */
11
+ export const MThreadCodec: Codec<Thread, MThread> = {
12
+ encode(thread: Thread): MThread {
13
+ const rawTitle = thread.metadata?.title;
14
+ const title = typeof rawTitle === "string" ? rawTitle : null;
15
+
16
+ return {
17
+ tid: thread.tid,
18
+ namespace: thread.namespace,
19
+ agentId: thread.agent.id,
20
+ title,
21
+ model: {
22
+ provider: thread.model.provider,
23
+ modelId: thread.model.modelId,
24
+ },
25
+ context: thread.context.context as Record<string, unknown>,
26
+ parentTaskId: thread.parent?.id ?? null,
27
+ state: thread.state,
28
+ createdAt: thread.createdAt,
29
+ updatedAt: thread.updatedAt,
30
+ };
31
+ },
32
+
33
+ decode(_model: MThread): Thread {
34
+ throw new UnimplementedError();
35
+ },
36
+ };
37
+
38
+ /**
39
+ * Converts `RThreadsListParams` into a `ThreadFilter` for the store.
40
+ */
41
+ export const ThreadsFilterCodec: Codec<
42
+ RThreadsListParams,
43
+ ThreadFilter | undefined
44
+ > = {
45
+ encode(params: RThreadsListParams): ThreadFilter | undefined {
46
+ const filter: ThreadFilter = {};
47
+
48
+ if (params.namespace !== undefined) {
49
+ filter.namespace = params.namespace;
50
+ }
51
+ if (params.state !== undefined) {
52
+ filter.state = params.state;
53
+ }
54
+ if (params.agentId !== undefined) {
55
+ filter.agentId = params.agentId;
56
+ }
57
+ if (params.parentTaskId !== undefined) {
58
+ filter.parentTaskId = params.parentTaskId;
59
+ }
60
+ if (params.after !== undefined) {
61
+ filter.createdAfter = params.after;
62
+ }
63
+ if (params.before !== undefined) {
64
+ filter.createdBefore = params.before;
65
+ }
66
+
67
+ return Object.keys(filter).length > 0 ? filter : undefined;
68
+ },
69
+
70
+ decode(_filter: ThreadFilter | undefined): RThreadsListParams {
71
+ throw new UnimplementedError();
72
+ },
73
+ };
74
+
75
+ /**
76
+ * Converts list `order` params into `ThreadListOptions["order"]` for the store.
77
+ */
78
+ export const ThreadsOrderCodec: Codec<
79
+ RThreadsListParams["order"],
80
+ ThreadListOptions["order"] | undefined
81
+ > = {
82
+ encode(
83
+ order: RThreadsListParams["order"],
84
+ ): ThreadListOptions["order"] | undefined {
85
+ if (!order) return undefined;
86
+
87
+ const result: NonNullable<ThreadListOptions["order"]> = {};
88
+
89
+ if (order.createdAt !== undefined) {
90
+ result.createdAt = order.createdAt;
91
+ }
92
+ if (order.updatedAt !== undefined) {
93
+ result.updatedAt = order.updatedAt;
94
+ }
95
+
96
+ return Object.keys(result).length > 0 ? result : undefined;
97
+ },
98
+
99
+ decode(
100
+ _order: ThreadListOptions["order"] | undefined,
101
+ ): RThreadsListParams["order"] {
102
+ throw new UnimplementedError();
103
+ },
104
+ };