kernl 0.1.4 → 0.2.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 (55) hide show
  1. package/.turbo/turbo-build.log +5 -4
  2. package/CHANGELOG.md +12 -0
  3. package/dist/agent.d.ts +20 -3
  4. package/dist/agent.d.ts.map +1 -1
  5. package/dist/agent.js +60 -41
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/kernl.d.ts +27 -1
  10. package/dist/kernl.d.ts.map +1 -1
  11. package/dist/kernl.js +36 -2
  12. package/dist/mcp/__tests__/integration.test.js +16 -0
  13. package/dist/thread/__tests__/fixtures/mock-model.d.ts +7 -0
  14. package/dist/thread/__tests__/fixtures/mock-model.d.ts.map +1 -0
  15. package/dist/thread/__tests__/fixtures/mock-model.js +59 -0
  16. package/dist/thread/__tests__/integration.test.d.ts +2 -0
  17. package/dist/thread/__tests__/integration.test.d.ts.map +1 -0
  18. package/dist/thread/__tests__/integration.test.js +247 -0
  19. package/dist/thread/__tests__/stream.test.d.ts +2 -0
  20. package/dist/thread/__tests__/stream.test.d.ts.map +1 -0
  21. package/dist/thread/__tests__/stream.test.js +244 -0
  22. package/dist/thread/__tests__/thread.test.js +612 -763
  23. package/dist/thread/thread.d.ts +30 -25
  24. package/dist/thread/thread.d.ts.map +1 -1
  25. package/dist/thread/thread.js +114 -314
  26. package/dist/thread/utils.d.ts +16 -1
  27. package/dist/thread/utils.d.ts.map +1 -1
  28. package/dist/thread/utils.js +30 -0
  29. package/dist/tool/index.d.ts +1 -1
  30. package/dist/tool/index.d.ts.map +1 -1
  31. package/dist/tool/index.js +1 -1
  32. package/dist/tool/tool.d.ts.map +1 -1
  33. package/dist/tool/tool.js +6 -2
  34. package/dist/tool/toolkit.d.ts +7 -3
  35. package/dist/tool/toolkit.d.ts.map +1 -1
  36. package/dist/tool/toolkit.js +7 -3
  37. package/dist/types/agent.d.ts +5 -5
  38. package/dist/types/agent.d.ts.map +1 -1
  39. package/dist/types/thread.d.ts +10 -16
  40. package/dist/types/thread.d.ts.map +1 -1
  41. package/package.json +7 -5
  42. package/src/agent.ts +97 -86
  43. package/src/index.ts +1 -1
  44. package/src/kernl.ts +51 -2
  45. package/src/mcp/__tests__/integration.test.ts +17 -0
  46. package/src/thread/__tests__/fixtures/mock-model.ts +71 -0
  47. package/src/thread/__tests__/integration.test.ts +349 -0
  48. package/src/thread/__tests__/thread.test.ts +625 -775
  49. package/src/thread/thread.ts +134 -381
  50. package/src/thread/utils.ts +36 -1
  51. package/src/tool/index.ts +1 -1
  52. package/src/tool/tool.ts +6 -2
  53. package/src/tool/toolkit.ts +10 -3
  54. package/src/types/agent.ts +9 -6
  55. package/src/types/thread.ts +25 -17
@@ -2,8 +2,8 @@ import { Kernl } from "../kernl";
2
2
  import { Agent } from "../agent";
3
3
  import { Context } from "../context";
4
4
  import type { Task } from "../task";
5
- import { LanguageModel, LanguageModelResponse } from "@kernl-sdk/protocol";
6
- import type { ThreadEvent, ThreadOptions, ThreadExecuteResult } from "../types/thread";
5
+ import { LanguageModel } from "@kernl-sdk/protocol";
6
+ import type { ThreadEvent, ThreadOptions, ThreadExecuteResult, ThreadState, ThreadStreamEvent } from "../types/thread";
7
7
  import type { AgentResponseType } from "../types/agent";
8
8
  import type { ResolvedAgentResponse } from "../guardrail";
9
9
  /**
@@ -17,19 +17,40 @@ export declare class Thread<TContext = unknown, TResponse extends AgentResponseT
17
17
  readonly model: LanguageModel;
18
18
  readonly parent: Task<TContext> | null;
19
19
  readonly mode: "blocking" | "stream";
20
- readonly state: ThreadState;
21
- readonly input: ThreadEvent[] | string;
20
+ readonly input: ThreadEvent[];
21
+ _tick: number;
22
+ state: ThreadState;
22
23
  private history;
23
- constructor(kernl: Kernl, agent: Agent<TContext, TResponse>, input: ThreadEvent[] | string, options?: ThreadOptions<TContext>);
24
+ private abort?;
25
+ constructor(kernl: Kernl, agent: Agent<TContext, TResponse>, input: ThreadEvent[], options?: ThreadOptions<TContext>);
24
26
  /**
25
- * Main thread execution loop - runs until terminal state or interruption
27
+ * Blocking execution loop - runs until terminal state or interruption
26
28
  */
27
29
  execute(): Promise<ThreadExecuteResult<ResolvedAgentResponse<TResponse>>>;
28
30
  /**
29
- * A single tick of the thread's execution.
31
+ * Streaming execution - returns async iterator of events
32
+ */
33
+ stream(): AsyncIterable<ThreadStreamEvent>;
34
+ /**
35
+ * Cancel the running thread
36
+ */
37
+ cancel(): void;
38
+ /**
39
+ * Append a new event to the thread history
40
+ */
41
+ append(event: ThreadEvent): void;
42
+ /**
43
+ * Main execution loop - always yields events, callers can propagate or discard (as in execute())
44
+ *
45
+ * NOTE: Streaming structured output deferred for now. Prioritizing correctness + simplicity,
46
+ * and unclear what use cases there would actually be for streaming a structured output (other than maybe gen UI).
47
+ */
48
+ private _execute;
49
+ /**
50
+ * A single tick - calls model and yields events as they arrive
30
51
  *
31
- * Prepares the input for the model, gets the response, and then parses into a TickResult
32
- * with the events generated and the model's intentions (actions).
52
+ * NOTE: Streaming structured outputs deferred until concrete use cases emerge.
53
+ * For now, we stream text-delta and tool events, final validation happens in _execute().
33
54
  */
34
55
  private tick;
35
56
  /**
@@ -46,21 +67,5 @@ export declare class Thread<TContext = unknown, TResponse extends AgentResponseT
46
67
  * Applies call-level filters and prepares the model request for the language model
47
68
  */
48
69
  private prepareModelRequest;
49
- /**
50
- * @internal
51
- * Parses the model's response into events (for history) and actions (for execution).
52
- */
53
- private parseModelResponse;
54
- }
55
- /**
56
- * ThreadState tracks the execution state of a single thread.
57
- *
58
- * A thread is created each time a task is scheduled and executes
59
- * the main tick() loop until terminal state.
60
- */
61
- export declare class ThreadState {
62
- tick: number;
63
- modelResponses: LanguageModelResponse[];
64
- constructor();
65
70
  }
66
71
  //# sourceMappingURL=thread.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/thread/thread.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,EAEL,aAAa,EAEb,qBAAqB,EAGtB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAEV,WAAW,EACX,aAAa,EACb,mBAAmB,EAGpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAIzD;;GAEG;AACH,qBAAa,MAAM,CACjB,QAAQ,GAAG,OAAO,EAClB,SAAS,SAAS,iBAAiB,GAAG,MAAM;IAE5C,OAAO,CAAC,KAAK,CAAQ;IAErB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IAGrC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IACvC,OAAO,CAAC,OAAO,CAAsE;gBAGnF,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,EACjC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,EAC7B,OAAO,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC;IAgCnC;;OAEG;IACG,OAAO,IAAI,OAAO,CACtB,mBAAmB,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC,CACtD;IAmDD;;;;;OAKG;YACW,IAAI;IAmClB;;OAEG;YACW,cAAc;IAmC5B;;;;OAIG;YACW,YAAY;IA4C1B;;OAEG;YACW,mBAAmB;IA4CjC;;;OAGG;IACH,OAAO,CAAC,kBAAkB;CAqB3B;AAED;;;;;GAKG;AACH,qBAAa,WAAW;IACtB,IAAI,EAAE,MAAM,CAAwE;IACpF,cAAc,EAAE,qBAAqB,EAAE,CAAmE;;CAiB3G"}
1
+ {"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/thread/thread.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,EAEL,aAAa,EAMd,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAEV,WAAW,EACX,aAAa,EACb,mBAAmB,EAEnB,WAAW,EACX,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AASzD;;GAEG;AACH,qBAAa,MAAM,CACjB,QAAQ,GAAG,OAAO,EAClB,SAAS,SAAS,iBAAiB,GAAG,MAAM;IAE5C,OAAO,CAAC,KAAK,CAAQ;IAErB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC;IAI9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,CAAC,OAAO,CAAsE;IACrF,OAAO,CAAC,KAAK,CAAC,CAAkB;gBAG9B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,EACjC,KAAK,EAAE,WAAW,EAAE,EACpB,OAAO,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC;IAgBnC;;OAEG;IACG,OAAO,IAAI,OAAO,CACtB,mBAAmB,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC,CACtD;IAcD;;OAEG;IACI,MAAM,IAAI,aAAa,CAAC,iBAAiB,CAAC;IAkBjD;;OAEG;IACH,MAAM;IAIN;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAIhC;;;;;OAKG;YACY,QAAQ;IAiDvB;;;;;OAKG;YACY,IAAI;IAwBnB;;OAEG;YACW,cAAc;IA6C5B;;;;OAIG;YACW,YAAY;IA4C1B;;OAEG;YACW,mBAAmB;CA2ClC"}
@@ -1,8 +1,8 @@
1
1
  import assert from "assert";
2
2
  import { Context } from "../context";
3
- import { FAILED, } from "@kernl-sdk/protocol";
3
+ import { FAILED, RUNNING, STOPPED, } from "@kernl-sdk/protocol";
4
4
  import { randomID, filter } from "@kernl-sdk/shared/lib";
5
- import { getFinalResponse, parseFinalResponse } from "./utils";
5
+ import { notDelta, getFinalResponse, getIntentions, parseFinalResponse, } from "./utils";
6
6
  /**
7
7
  * A thread drives the execution loop for an agent.
8
8
  */
@@ -14,10 +14,13 @@ export class Thread {
14
14
  model; /* inherited from the agent unless specified */
15
15
  parent; /* parent task which spawned this thread */
16
16
  mode; /* TODO */
17
+ input; /* the initial input for the thread */
18
+ // readonly stats: ThreadMetrics;
17
19
  /* state */
20
+ _tick;
18
21
  state;
19
- input; /* the initial input for the thread */
20
22
  history;
23
+ abort;
21
24
  constructor(kernl, agent, input, options) {
22
25
  this.id = `tid_${randomID()}`;
23
26
  this.agent = agent;
@@ -25,59 +28,94 @@ export class Thread {
25
28
  this.kernl = kernl;
26
29
  this.parent = options?.task ?? null;
27
30
  this.model = options?.model ?? agent.model;
28
- this.state = new ThreadState(); // (TODO): checkpoint ?? new ThreadState()
29
31
  this.mode = "blocking"; // (TODO): add streaming
30
32
  this.input = input;
31
- // Convert string input to user message and initialize history
32
- if (typeof input === "string") {
33
- this.history = [
34
- {
35
- kind: "message",
36
- id: `msg_${randomID()}`,
37
- role: "user",
38
- content: [
39
- {
40
- kind: "text",
41
- text: input,
42
- },
43
- ],
44
- },
45
- ];
33
+ this._tick = 0;
34
+ this.state = STOPPED;
35
+ this.history = input;
36
+ }
37
+ /**
38
+ * Blocking execution loop - runs until terminal state or interruption
39
+ */
40
+ async execute() {
41
+ for await (const _event of this.stream()) {
42
+ // just consume the stream (already in history in _execute())
46
43
  }
47
- else {
48
- this.history = input;
44
+ // extract final response from accumulated history
45
+ const text = getFinalResponse(this.history);
46
+ assert(text, "_execute continues until text !== null"); // (TODO): consider preventing infinite loops here
47
+ const parsed = parseFinalResponse(text, this.agent.responseType);
48
+ return { response: parsed, state: this.state };
49
+ }
50
+ /**
51
+ * Streaming execution - returns async iterator of events
52
+ */
53
+ async *stream() {
54
+ if (this.state === RUNNING && this.abort) {
55
+ throw new Error("thread already running");
56
+ }
57
+ this.state = RUNNING;
58
+ this.abort = new AbortController();
59
+ try {
60
+ yield* this._execute();
61
+ }
62
+ catch (err) {
63
+ throw err;
64
+ }
65
+ finally {
66
+ this.state = STOPPED;
67
+ this.abort = undefined;
49
68
  }
50
69
  }
51
70
  /**
52
- * Main thread execution loop - runs until terminal state or interruption
71
+ * Cancel the running thread
53
72
  */
54
- async execute() {
55
- while (true) {
56
- const { events, intentions } = await this.tick(); // actions: { syscalls, functions, mcpApprovalRequests }
57
- this.history.push(...events);
58
- // // priority 1: syscalls first - these override all other actions
59
- // if (actions.syscalls.length > 0) {
60
- // switch (actions.syscalls.kind) { // is it possible to have more than one?
61
- // case SYS_WAIT:
62
- // return this.state;
63
- // case SYS_EXIT:
64
- // return { state: this.state, output: this.output }
65
- // default:
66
- // }
67
- // }
68
- // if model returns a message with no actions intentions -> terminal state
73
+ cancel() {
74
+ this.abort?.abort();
75
+ }
76
+ /**
77
+ * Append a new event to the thread history
78
+ */
79
+ append(event) {
80
+ this.history.push(event);
81
+ }
82
+ /**
83
+ * Main execution loop - always yields events, callers can propagate or discard (as in execute())
84
+ *
85
+ * NOTE: Streaming structured output deferred for now. Prioritizing correctness + simplicity,
86
+ * and unclear what use cases there would actually be for streaming a structured output (other than maybe gen UI).
87
+ */
88
+ async *_execute() {
89
+ for (;;) {
90
+ if (this.abort?.signal.aborted) {
91
+ return;
92
+ }
93
+ const events = [];
94
+ for await (const e of this.tick()) {
95
+ // we don't want deltas in the history
96
+ if (notDelta(e)) {
97
+ events.push(e);
98
+ this.history.push(e);
99
+ }
100
+ yield e;
101
+ }
102
+ // if model returns a message with no action intentions -> terminal state
103
+ const intentions = getIntentions(events);
69
104
  if (!intentions) {
70
105
  const text = getFinalResponse(events);
71
106
  if (!text)
72
- continue; // run again, policy-dependent?
73
- const parsed = parseFinalResponse(text, this.agent.responseType);
107
+ continue; // run again, policy-dependent? (how to ensure no infinite loop here?)
74
108
  // await this.agent.runOutputGuardails(context, state);
75
109
  // this.kernl.emit("thread.terminated", context, output);
76
- return { response: parsed, state: this.state };
110
+ return;
77
111
  }
78
- // perform the actions intended by the model
112
+ // perform actions intended by the model
79
113
  const { actions, pendingApprovals } = await this.performActions(intentions);
80
- this.history.push(...actions);
114
+ // yield action events
115
+ for (const a of actions) {
116
+ this.history.push(a);
117
+ yield a;
118
+ }
81
119
  if (pendingApprovals.length > 0) {
82
120
  // publish a batch approval request containing all of them
83
121
  //
@@ -89,57 +127,57 @@ export class Thread {
89
127
  }
90
128
  }
91
129
  }
92
- // ----------------------
93
- // Internal helpers
94
- // ----------------------
95
130
  /**
96
- * A single tick of the thread's execution.
131
+ * A single tick - calls model and yields events as they arrive
97
132
  *
98
- * Prepares the input for the model, gets the response, and then parses into a TickResult
99
- * with the events generated and the model's intentions (actions).
133
+ * NOTE: Streaming structured outputs deferred until concrete use cases emerge.
134
+ * For now, we stream text-delta and tool events, final validation happens in _execute().
100
135
  */
101
- async tick() {
102
- this.state.tick++;
103
- // // check limits
104
- // if (this.state.tick > this.limits.maxTicks) {
105
- // throw new RuntimeError("resource_limit:max_ticks_exceeded");
106
- // }
107
- // run guardrails on the first tick
108
- if (this.state.tick === 1) {
109
- // await this.agent.runInputGuardrails(this.context, ...?);
136
+ async *tick() {
137
+ this._tick++;
138
+ // (TODO): check limits (if this._tick > this.limits.maxTicks)
139
+ // (TODO): run input guardrails on first tick (if this._tick === 1)
140
+ const req = await this.prepareModelRequest(this.history);
141
+ // try to stream if model supports it
142
+ if (this.model.stream) {
143
+ const stream = this.model.stream(req);
144
+ for await (const event of stream) {
145
+ yield event; // [text-delta, tool-call, message, reasoning, ...]
146
+ }
147
+ }
148
+ else {
149
+ // fallback: blocking generate, yield events as batch
150
+ const res = await this.model.generate(req);
151
+ for (const event of res.content) {
152
+ yield event;
153
+ }
154
+ // (TODO): track usage (this.stats.usage.add(res.usage))
110
155
  }
111
- const req = await this.prepareModelRequest(this.history); // (TODO): how to get input for this tick?
112
- // if (this.mode === "stream") {
113
- // const stream = this.model.stream(input, {
114
- // system: systemPrompt,
115
- // tools: this.agent.tools /* [systools, tools] */,
116
- // settings: this.agent.modelSettings,
117
- // responseSchema: this.agent.responseType,
118
- // });
119
- // for await (const event of stream) {
120
- // // handle streaming events
121
- // }
122
- // response = stream.collect(); // something like this
123
- // } else {
124
- const res = await this.model.generate(req);
125
- this.state.modelResponses.push(res);
126
- // this.stats.usage.add(response.usage);
127
- return this.parseModelResponse(res);
128
156
  }
129
157
  /**
130
158
  * Perform the actions returned by the model
131
159
  */
132
160
  async performActions(intentions) {
161
+ // // priority 1: syscalls first - these override all other actions
162
+ // if (actions.syscalls.length > 0) {
163
+ // switch (actions.syscalls.kind) { // is it possible to have more than one?
164
+ // case SYS_WAIT:
165
+ // return this.state;
166
+ // case SYS_EXIT:
167
+ // return { state: this.state, output: this.output }
168
+ // default:
169
+ // }
170
+ // }
133
171
  // (TODO): refactor into a general actions system - probably shouldn't be handled by Thread
134
172
  const toolEvents = await this.executeTools(intentions.toolCalls);
135
173
  // const mcpEvents = await this.executeMCPRequests(actions.mcpRequests);
136
- // Separate events and pending approvals
137
174
  const actions = [];
138
175
  const pendingApprovals = [];
139
176
  // (TODO): clean this - approval tracking should be handled differently
140
177
  for (const e of toolEvents) {
141
178
  if (e.kind === "tool-result" &&
142
- e.state === "requires_approval") {
179
+ e.state === "requires_approval" // (TODO): fix this
180
+ ) {
143
181
  // Find the original tool call for this pending approval
144
182
  const originalCall = intentions.toolCalls.find((call) => call.callId === e.callId);
145
183
  if (originalCall) {
@@ -231,242 +269,4 @@ export class Thread {
231
269
  tools,
232
270
  };
233
271
  }
234
- /**
235
- * @internal
236
- * Parses the model's response into events (for history) and actions (for execution).
237
- */
238
- parseModelResponse(res) {
239
- const events = [];
240
- const toolCalls = [];
241
- for (const event of res.content) {
242
- switch (event.kind) {
243
- case "tool-call":
244
- // Add to both actions (for execution) and events (for history)
245
- toolCalls.push(event);
246
- // fallthrough
247
- default:
248
- events.push(event);
249
- break;
250
- }
251
- }
252
- return {
253
- events,
254
- intentions: toolCalls.length > 0 ? { toolCalls } : null,
255
- };
256
- }
257
- }
258
- /**
259
- * ThreadState tracks the execution state of a single thread.
260
- *
261
- * A thread is created each time a task is scheduled and executes
262
- * the main tick() loop until terminal state.
263
- */
264
- export class ThreadState {
265
- tick;
266
- modelResponses;
267
- constructor() {
268
- this.tick = 0;
269
- this.modelResponses = [];
270
- }
271
272
  }
272
- // /**
273
- // * The result of an agent run in streaming mode.
274
- // */
275
- // export class StreamedRunResult<
276
- // TContext,
277
- // TAgent extends Agent<TContext, AgentResponseType>,
278
- // >
279
- // extends RunResultBase<TContext, TAgent>
280
- // implements AsyncIterable<ThreadStreamEvent>
281
- // {
282
- // /**
283
- // * The current agent that is running
284
- // */
285
- // public get currentAgent(): TAgent | undefined {
286
- // return this.lastAgent;
287
- // }
288
- // /**
289
- // * The current turn number
290
- // */
291
- // public currentTurn: number = 0;
292
- // /**
293
- // * The maximum number of turns that can be run
294
- // */
295
- // public maxTurns: number | undefined;
296
- // #error: unknown = null;
297
- // #signal?: AbortSignal;
298
- // #readableController:
299
- // | ReadableStreamDefaultController<ThreadStreamEvent>
300
- // | undefined;
301
- // #readableStream: ReadableStream<ThreadStreamEvent>;
302
- // #completedPromise: Promise<void>;
303
- // #completedPromiseResolve: (() => void) | undefined;
304
- // #completedPromiseReject: ((err: unknown) => void) | undefined;
305
- // #cancelled: boolean = false;
306
- // #streamLoopPromise: Promise<void> | undefined;
307
- // constructor(
308
- // result: {
309
- // state: ThreadState<TContext, TAgent>;
310
- // signal?: AbortSignal;
311
- // } = {} as any,
312
- // ) {
313
- // super(result.state);
314
- // this.#signal = result.signal;
315
- // this.#readableStream = new ReadableStream<ThreadStreamEvent>({
316
- // start: (controller) => {
317
- // this.#readableController = controller;
318
- // },
319
- // cancel: () => {
320
- // this.#cancelled = true;
321
- // },
322
- // });
323
- // this.#completedPromise = new Promise((resolve, reject) => {
324
- // this.#completedPromiseResolve = resolve;
325
- // this.#completedPromiseReject = reject;
326
- // });
327
- // if (this.#signal) {
328
- // const handleAbort = () => {
329
- // if (this.#cancelled) {
330
- // return;
331
- // }
332
- // this.#cancelled = true;
333
- // const controller = this.#readableController;
334
- // this.#readableController = undefined;
335
- // if (this.#readableStream.locked) {
336
- // if (controller) {
337
- // try {
338
- // controller.close();
339
- // } catch (err) {
340
- // logger.debug(`Failed to close readable stream on abort: ${err}`);
341
- // }
342
- // }
343
- // } else {
344
- // void this.#readableStream
345
- // .cancel(this.#signal?.reason)
346
- // .catch((err) => {
347
- // logger.debug(`Failed to cancel readable stream on abort: ${err}`);
348
- // });
349
- // }
350
- // this.#completedPromiseResolve?.();
351
- // };
352
- // if (this.#signal.aborted) {
353
- // handleAbort();
354
- // } else {
355
- // this.#signal.addEventListener("abort", handleAbort, { once: true });
356
- // }
357
- // }
358
- // }
359
- // /**
360
- // * @internal
361
- // * Adds an item to the stream of output items
362
- // */
363
- // _addItem(item: ThreadStreamEvent) {
364
- // if (!this.cancelled) {
365
- // this.#readableController?.enqueue(item);
366
- // }
367
- // }
368
- // /**
369
- // * @internal
370
- // * Indicates that the stream has been completed
371
- // */
372
- // _done() {
373
- // if (!this.cancelled && this.#readableController) {
374
- // this.#readableController.close();
375
- // this.#readableController = undefined;
376
- // this.#completedPromiseResolve?.();
377
- // }
378
- // }
379
- // /**
380
- // * @internal
381
- // * Handles an error in the stream loop.
382
- // */
383
- // _raiseError(err: unknown) {
384
- // if (!this.cancelled && this.#readableController) {
385
- // this.#readableController.error(err);
386
- // this.#readableController = undefined;
387
- // }
388
- // this.#error = err;
389
- // this.#completedPromiseReject?.(err);
390
- // this.#completedPromise.catch((e) => {
391
- // logger.debug(`Resulted in an error: ${e}`);
392
- // });
393
- // }
394
- // /**
395
- // * Returns true if the stream has been cancelled.
396
- // */
397
- // get cancelled(): boolean {
398
- // return this.#cancelled;
399
- // }
400
- // /**
401
- // * Returns the underlying readable stream.
402
- // * @returns A readable stream of the agent run.
403
- // */
404
- // toStream(): ReadableStream<ThreadStreamEvent> {
405
- // return this.#readableStream as ReadableStream<ThreadStreamEvent>;
406
- // }
407
- // /**
408
- // * Await this promise to ensure that the stream has been completed if you are not consuming the
409
- // * stream directly.
410
- // */
411
- // get completed() {
412
- // return this.#completedPromise;
413
- // }
414
- // /**
415
- // * Error thrown during the run, if any.
416
- // */
417
- // get error() {
418
- // return this.#error;
419
- // }
420
- // /**
421
- // * Returns a readable stream of the final text output of the agent run.
422
- // *
423
- // * @returns A readable stream of the final output of the agent run.
424
- // * @remarks Pass `{ compatibleWithNodeStreams: true }` to receive a Node.js compatible stream
425
- // * instance.
426
- // */
427
- // toTextStream(): ReadableStream<string>;
428
- // toTextStream(options?: { compatibleWithNodeStreams: true }): Readable;
429
- // toTextStream(options?: {
430
- // compatibleWithNodeStreams?: false;
431
- // }): ReadableStream<string>;
432
- // toTextStream(
433
- // options: { compatibleWithNodeStreams?: boolean } = {},
434
- // ): Readable | ReadableStream<string> {
435
- // const stream = this.#readableStream.pipeThrough(
436
- // new TransformStream<ThreadStreamEvent, string>({
437
- // transform(event, controller) {
438
- // if (
439
- // event.kind === "raw_model_stream_event" && // (TODO): what to do here?
440
- // event.data.kind === "text-delta"
441
- // ) {
442
- // const item = TextDeltaEvent.parse(event); // ??
443
- // controller.enqueue(item.text); // (TODO): is it just the text that we want to return here?
444
- // }
445
- // },
446
- // }),
447
- // );
448
- // if (options.compatibleWithNodeStreams) {
449
- // return Readable.fromWeb(stream);
450
- // }
451
- // return stream as ReadableStream<string>;
452
- // }
453
- // [Symbol.asyncIterator](): AsyncIterator<ThreadStreamEvent> {
454
- // return this.#readableStream[Symbol.asyncIterator]();
455
- // }
456
- // /**
457
- // * @internal
458
- // * Sets the stream loop promise that completes when the internal stream loop finishes.
459
- // * This is used to defer trace end until all agent work is complete.
460
- // */
461
- // _setStreamLoopPromise(promise: Promise<void>) {
462
- // this.#streamLoopPromise = promise;
463
- // }
464
- // /**
465
- // * @internal
466
- // * Returns a promise that resolves when the stream loop completes.
467
- // * This is used by the tracing system to wait for all agent work before ending the trace.
468
- // */
469
- // _getStreamLoopPromise(): Promise<void> | undefined {
470
- // return this.#streamLoopPromise;
471
- // }
472
- // }
@@ -1,6 +1,21 @@
1
1
  import type { ResolvedAgentResponse } from "../guardrail";
2
+ import { ToolCall } from "@kernl-sdk/protocol";
2
3
  import type { AgentResponseType } from "../types/agent";
3
- import type { ThreadEvent } from "../types/thread";
4
+ import type { ThreadEvent, ThreadStreamEvent, ActionSet } from "../types/thread";
5
+ /**
6
+ * Check if an event represents an intention (action to be performed)
7
+ */
8
+ export declare function isActionIntention(event: ThreadEvent): event is ToolCall;
9
+ /**
10
+ * Extract action intentions from a list of events.
11
+ * Returns ActionSet if there are any tool calls, null otherwise.
12
+ */
13
+ export declare function getIntentions(events: ThreadEvent[]): ActionSet | null;
14
+ /**
15
+ * Check if an event is NOT a delta/start/end event (i.e., a complete item).
16
+ * Returns true for complete items: Message, Reasoning, ToolCall, ToolResult
17
+ */
18
+ export declare function notDelta(event: ThreadStreamEvent): event is ThreadEvent;
4
19
  /**
5
20
  * Extract the final text response from a list of events.
6
21
  * Returns null if no assistant message with text content is found.
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/thread/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAOzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,GAAG,IAAI,CAcrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,SAAS,iBAAiB,EACpE,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,SAAS,GACtB,qBAAqB,CAAC,SAAS,CAAC,CAsBlC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/thread/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAIzD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAI/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,KAAK,IAAI,QAAQ,CAEvE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,GAAG,IAAI,CAGrE;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,KAAK,IAAI,WAAW,CAYvE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,GAAG,IAAI,CAcrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,SAAS,iBAAiB,EACpE,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,SAAS,GACtB,qBAAqB,CAAC,SAAS,CAAC,CAsBlC"}
@@ -1,6 +1,36 @@
1
1
  /* lib */
2
2
  import { json } from "@kernl-sdk/shared/lib";
3
3
  import { ModelBehaviorError } from "../lib/error";
4
+ /**
5
+ * Check if an event represents an intention (action to be performed)
6
+ */
7
+ export function isActionIntention(event) {
8
+ return event.kind === "tool-call";
9
+ }
10
+ /**
11
+ * Extract action intentions from a list of events.
12
+ * Returns ActionSet if there are any tool calls, null otherwise.
13
+ */
14
+ export function getIntentions(events) {
15
+ const toolCalls = events.filter(isActionIntention);
16
+ return toolCalls.length > 0 ? { toolCalls } : null;
17
+ }
18
+ /**
19
+ * Check if an event is NOT a delta/start/end event (i.e., a complete item).
20
+ * Returns true for complete items: Message, Reasoning, ToolCall, ToolResult
21
+ */
22
+ export function notDelta(event) {
23
+ switch (event.kind) {
24
+ case "message":
25
+ case "reasoning":
26
+ case "tool-call":
27
+ case "tool-result":
28
+ return true;
29
+ // all other events are streaming deltas/control events
30
+ default:
31
+ return false;
32
+ }
33
+ }
4
34
  /**
5
35
  * Extract the final text response from a list of events.
6
36
  * Returns null if no assistant message with text content is found.
@@ -1,4 +1,4 @@
1
1
  export { BaseTool, FunctionTool, HostedTool, tool } from "./tool";
2
- export { Toolkit, FunctionToolkit, MCPToolkit } from "./toolkit";
2
+ export { BaseToolkit, Toolkit, FunctionToolkit, MCPToolkit } from "./toolkit";
3
3
  export type { Tool, ToolResult, FunctionToolkitConfig, MCPToolkitConfig, ToolkitFilter, ToolkitFilterContext, } from "./types";
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tool/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACjE,YAAY,EACV,IAAI,EACJ,UAAU,EACV,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,oBAAoB,GACrB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tool/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC9E,YAAY,EACV,IAAI,EACJ,UAAU,EACV,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,oBAAoB,GACrB,MAAM,SAAS,CAAC"}