kernl 0.7.4 → 0.8.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 (63) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +19 -1
  3. package/dist/agent/types.d.ts +20 -12
  4. package/dist/agent/types.d.ts.map +1 -1
  5. package/dist/agent.d.ts +7 -7
  6. package/dist/agent.d.ts.map +1 -1
  7. package/dist/agent.js +3 -14
  8. package/dist/api/resources/agents/agents.d.ts +5 -5
  9. package/dist/api/resources/agents/agents.d.ts.map +1 -1
  10. package/dist/api/resources/agents/agents.js +1 -1
  11. package/dist/guardrail.d.ts +19 -19
  12. package/dist/guardrail.d.ts.map +1 -1
  13. package/dist/kernl/kernl.d.ts +6 -6
  14. package/dist/kernl/kernl.d.ts.map +1 -1
  15. package/dist/lib/error.d.ts +3 -3
  16. package/dist/lib/error.d.ts.map +1 -1
  17. package/dist/lifecycle.d.ts +6 -6
  18. package/dist/lifecycle.d.ts.map +1 -1
  19. package/dist/memory/__tests__/encoder.test.d.ts +2 -0
  20. package/dist/memory/__tests__/encoder.test.d.ts.map +1 -0
  21. package/dist/memory/__tests__/encoder.test.js +120 -0
  22. package/dist/memory/codecs/domain.d.ts +5 -0
  23. package/dist/memory/codecs/domain.d.ts.map +1 -1
  24. package/dist/memory/codecs/domain.js +6 -0
  25. package/dist/memory/encoder.d.ts +25 -2
  26. package/dist/memory/encoder.d.ts.map +1 -1
  27. package/dist/memory/encoder.js +46 -5
  28. package/dist/memory/index.d.ts +1 -1
  29. package/dist/memory/index.d.ts.map +1 -1
  30. package/dist/memory/index.js +1 -1
  31. package/dist/memory/schema.d.ts.map +1 -1
  32. package/dist/memory/schema.js +5 -0
  33. package/dist/memory/types.d.ts +1 -0
  34. package/dist/memory/types.d.ts.map +1 -1
  35. package/dist/thread/__tests__/integration.test.js +1 -1
  36. package/dist/thread/__tests__/thread.test.js +8 -8
  37. package/dist/thread/thread.d.ts +5 -5
  38. package/dist/thread/thread.d.ts.map +1 -1
  39. package/dist/thread/thread.js +13 -2
  40. package/dist/thread/types.d.ts +9 -6
  41. package/dist/thread/types.d.ts.map +1 -1
  42. package/dist/thread/utils.d.ts +7 -6
  43. package/dist/thread/utils.d.ts.map +1 -1
  44. package/dist/thread/utils.js +9 -8
  45. package/package.json +5 -4
  46. package/src/agent/types.ts +25 -29
  47. package/src/agent.ts +15 -28
  48. package/src/api/resources/agents/agents.ts +8 -8
  49. package/src/guardrail.ts +28 -28
  50. package/src/kernl/kernl.ts +12 -12
  51. package/src/lib/error.ts +3 -3
  52. package/src/lifecycle.ts +6 -6
  53. package/src/memory/__tests__/encoder.test.ts +153 -0
  54. package/src/memory/codecs/domain.ts +6 -0
  55. package/src/memory/encoder.ts +51 -6
  56. package/src/memory/index.ts +1 -1
  57. package/src/memory/schema.ts +5 -0
  58. package/src/memory/types.ts +1 -0
  59. package/src/thread/__tests__/integration.test.ts +130 -146
  60. package/src/thread/__tests__/thread.test.ts +8 -8
  61. package/src/thread/thread.ts +21 -7
  62. package/src/thread/types.ts +9 -6
  63. package/src/thread/utils.ts +15 -14
@@ -1061,7 +1061,7 @@ describe("Thread", () => {
1061
1061
  });
1062
1062
 
1063
1063
  describe("Final Output Parsing", () => {
1064
- it("should return text output when responseType is 'text'", async () => {
1064
+ it("should return text output when output is 'text'", async () => {
1065
1065
  const model = createMockModel(async (req: LanguageModelRequest) => {
1066
1066
  return {
1067
1067
  content: [
@@ -1087,7 +1087,7 @@ describe("Thread", () => {
1087
1087
  name: "Test",
1088
1088
  instructions: "Test agent",
1089
1089
  model,
1090
- responseType: "text",
1090
+ output: "text",
1091
1091
  });
1092
1092
 
1093
1093
  const kernl = new Kernl();
@@ -1136,7 +1136,7 @@ describe("Thread", () => {
1136
1136
  name: "Test",
1137
1137
  instructions: "Test agent",
1138
1138
  model,
1139
- responseType: responseSchema,
1139
+ output: responseSchema,
1140
1140
  });
1141
1141
 
1142
1142
  const kernl = new Kernl();
@@ -1186,7 +1186,7 @@ describe("Thread", () => {
1186
1186
  name: "Test",
1187
1187
  instructions: "Test agent",
1188
1188
  model,
1189
- responseType: responseSchema,
1189
+ output: responseSchema,
1190
1190
  });
1191
1191
 
1192
1192
  const kernl = new Kernl();
@@ -1231,7 +1231,7 @@ describe("Thread", () => {
1231
1231
  name: "Test",
1232
1232
  instructions: "Test agent",
1233
1233
  model,
1234
- responseType: responseSchema,
1234
+ output: responseSchema,
1235
1235
  });
1236
1236
 
1237
1237
  const kernl = new Kernl();
@@ -1277,7 +1277,7 @@ describe("Thread", () => {
1277
1277
  name: "Test",
1278
1278
  instructions: "Test agent",
1279
1279
  model,
1280
- responseType: responseSchema,
1280
+ output: responseSchema,
1281
1281
  });
1282
1282
 
1283
1283
  const kernl = new Kernl();
@@ -1336,7 +1336,7 @@ describe("Thread", () => {
1336
1336
  name: "Test",
1337
1337
  instructions: "Test agent",
1338
1338
  model,
1339
- responseType: responseSchema,
1339
+ output: responseSchema,
1340
1340
  });
1341
1341
 
1342
1342
  const kernl = new Kernl();
@@ -1405,7 +1405,7 @@ describe("Thread", () => {
1405
1405
  name: "Test",
1406
1406
  instructions: "Test agent",
1407
1407
  model,
1408
- responseType: "text",
1408
+ output: "text",
1409
1409
  });
1410
1410
 
1411
1411
  const kernl = new Kernl();
@@ -1,4 +1,6 @@
1
1
  import assert from "assert";
2
+ import { ZodType } from "zod";
3
+ import * as z from "zod";
2
4
 
3
5
  import { Agent } from "@/agent";
4
6
  import { Context } from "@/context";
@@ -30,7 +32,8 @@ import type {
30
32
  ThreadExecuteResult,
31
33
  PerformActionsResult,
32
34
  } from "./types";
33
- import type { AgentResponseType } from "@/agent/types";
35
+ import type { AgentOutputType } from "@/agent/types";
36
+ import type { LanguageModelResponseType } from "@kernl-sdk/protocol";
34
37
 
35
38
  import {
36
39
  tevent,
@@ -82,11 +85,11 @@ import {
82
85
  */
83
86
  export class Thread<
84
87
  TContext = unknown,
85
- TResponse extends AgentResponseType = "text",
88
+ TOutput extends AgentOutputType = "text",
86
89
  > {
87
90
  readonly tid: string;
88
91
  readonly namespace: string;
89
- readonly agent: Agent<TContext, TResponse>;
92
+ readonly agent: Agent<TContext, TOutput>;
90
93
  readonly context: Context<TContext>;
91
94
  readonly model: LanguageModel; /* inherited from the agent unless specified */
92
95
  readonly parent: Task<TContext> | null; /* parent task which spawned this thread */
@@ -106,7 +109,7 @@ export class Thread<
106
109
  private abort?: AbortController;
107
110
  private storage?: ThreadStore;
108
111
 
109
- constructor(options: ThreadOptions<TContext, TResponse>) {
112
+ constructor(options: ThreadOptions<TContext, TOutput>) {
110
113
  this.tid = options.tid ?? `tid_${randomID()}`;
111
114
  this.namespace = options.namespace ?? "kernl";
112
115
  this.agent = options.agent;
@@ -142,7 +145,7 @@ export class Thread<
142
145
  * Blocking execution - runs until terminal state or interruption
143
146
  */
144
147
  async execute(): Promise<
145
- ThreadExecuteResult<ResolvedAgentResponse<TResponse>>
148
+ ThreadExecuteResult<ResolvedAgentResponse<TOutput>>
146
149
  > {
147
150
  for await (const _event of this.stream()) {
148
151
  // just consume the stream (already in history in _execute())
@@ -158,7 +161,7 @@ export class Thread<
158
161
 
159
162
  const text = getFinalResponse(items);
160
163
  assert(text, "_execute continues until text !== null"); // (TODO): consider preventing infinite loops here
161
- const parsed = parseFinalResponse(text, this.agent.responseType);
164
+ const parsed = parseFinalResponse(text, this.agent.output);
162
165
 
163
166
  return { response: parsed, state: this.state };
164
167
  }
@@ -405,7 +408,7 @@ export class Thread<
405
408
  e.kind === "tool-result" &&
406
409
  (e.state as any) === "requires_approval" // (TODO): fix this
407
410
  ) {
408
- // Find the original tool call for this pending approval
411
+ // find the original tool call for this pending approval
409
412
  const call = intentions.toolCalls.find((c) => c.callId === e.callId);
410
413
  call && pendingApprovals.push(call);
411
414
  } else {
@@ -508,10 +511,21 @@ export class Thread<
508
511
  );
509
512
  const tools = enabled.map((tool) => tool.serialize());
510
513
 
514
+ // derive responseType from agent.output
515
+ let responseType: LanguageModelResponseType | undefined;
516
+ if (this.agent.output && this.agent.output !== "text") {
517
+ const schema = this.agent.output as ZodType;
518
+ responseType = {
519
+ kind: "json",
520
+ schema: z.toJSONSchema(schema, { target: "draft-7" }) as any,
521
+ };
522
+ }
523
+
511
524
  return {
512
525
  input: filtered,
513
526
  settings,
514
527
  tools,
528
+ responseType,
515
529
  };
516
530
  }
517
531
  }
@@ -15,7 +15,7 @@ import { Task } from "@/task";
15
15
  import { Context } from "@/context";
16
16
  import { Agent } from "@/agent";
17
17
 
18
- import type { AgentResponseType } from "@/agent/types";
18
+ import type { AgentOutputType } from "@/agent/types";
19
19
  import type { ThreadStore } from "@/storage";
20
20
 
21
21
  /**
@@ -23,7 +23,10 @@ import type { ThreadStore } from "@/storage";
23
23
  */
24
24
  export type PublicThreadEvent = LanguageModelItem & ThreadEventBase;
25
25
 
26
- export type TextResponse = "text";
26
+ /**
27
+ * Text output type - indicates the agent returns a plain string.
28
+ */
29
+ export type TextOutput = "text";
27
30
 
28
31
  /**
29
32
  * Thread state values as a const array (for zod schemas).
@@ -61,10 +64,10 @@ export const REQUIRES_APPROVAL = "requires_approval";
61
64
  */
62
65
  export interface IThread<
63
66
  TContext = unknown,
64
- TResponse extends AgentResponseType = "text",
67
+ TOutput extends AgentOutputType = "text",
65
68
  > {
66
69
  tid: string;
67
- agent: Agent<TContext, TResponse>;
70
+ agent: Agent<TContext, TOutput>;
68
71
  model: LanguageModel;
69
72
 
70
73
  context: Context<TContext>;
@@ -148,9 +151,9 @@ export interface ThreadExecuteResult<TResponse = unknown> {
148
151
  */
149
152
  export interface ThreadOptions<
150
153
  TContext = unknown,
151
- TResponse extends AgentResponseType = "text",
154
+ TOutput extends AgentOutputType = "text",
152
155
  > {
153
- agent: Agent<TContext, TResponse>;
156
+ agent: Agent<TContext, TOutput>;
154
157
  input?: LanguageModelItem[];
155
158
  history?: ThreadEvent[];
156
159
  context?: Context<TContext>;
@@ -8,7 +8,7 @@ import { ToolCall, LanguageModelItem } from "@kernl-sdk/protocol";
8
8
  import { ModelBehaviorError } from "@/lib/error";
9
9
 
10
10
  /* types */
11
- import type { AgentResponseType } from "@/agent/types";
11
+ import type { AgentOutputType } from "@/agent/types";
12
12
  import type {
13
13
  ThreadEvent,
14
14
  ThreadEventBase,
@@ -129,30 +129,31 @@ export function getFinalResponse(items: LanguageModelItem[]): string | null {
129
129
  }
130
130
 
131
131
  /**
132
- * (TODO): This should run through the language model's native structured output (if avail)
132
+ * Parse the final response according to the output type schema.
133
133
  *
134
- * Parse the final response according to the response type schema.
135
- * - If responseType is "text", returns the text as-is
136
- * - If responseType is a ZodType, parses and validates the text as JSON
134
+ * This serves as a safety net validation after native structured output from the provider.
135
+ *
136
+ * - If output is "text", returns the text as-is
137
+ * - If output is a ZodType, parses and validates the text as JSON
137
138
  *
138
139
  * @throws {ModelBehaviorError} if structured output validation fails
139
140
  */
140
- export function parseFinalResponse<TResponse extends AgentResponseType>(
141
+ export function parseFinalResponse<TOutput extends AgentOutputType>(
141
142
  text: string,
142
- responseType: TResponse,
143
- ): ResolvedAgentResponse<TResponse> {
144
- if (responseType === "text") {
145
- return text as ResolvedAgentResponse<TResponse>; // text output - return as-is
143
+ output: TOutput,
144
+ ): ResolvedAgentResponse<TOutput> {
145
+ if (output === "text") {
146
+ return text as ResolvedAgentResponse<TOutput>; // text output - return as-is
146
147
  }
147
148
 
148
149
  // structured output - decode JSON and validate with schema
149
- if (responseType && typeof responseType === "object") {
150
+ if (output && typeof output === "object") {
150
151
  // (TODO): prob better way of checking this here
151
- const schema = responseType as ZodType;
152
+ const schema = output as ZodType;
152
153
 
153
154
  try {
154
155
  const validated = json(schema).decode(text); // (TODO): it would be nice if we could use `decodeSafe` here
155
- return validated as ResolvedAgentResponse<TResponse>;
156
+ return validated as ResolvedAgentResponse<TOutput>;
156
157
  } catch (error) {
157
158
  throw new ModelBehaviorError(
158
159
  `Failed to parse structured output: ${error instanceof Error ? error.message : String(error)}`,
@@ -161,5 +162,5 @@ export function parseFinalResponse<TResponse extends AgentResponseType>(
161
162
  }
162
163
 
163
164
  // Fallback - should not reach here
164
- return text as ResolvedAgentResponse<TResponse>;
165
+ return text as ResolvedAgentResponse<TOutput>;
165
166
  }