kernl 0.12.2 → 0.12.4
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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +22 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +10 -4
- package/dist/lib/env.d.ts +2 -2
- package/dist/thread/__tests__/mock.d.ts.map +1 -1
- package/dist/thread/__tests__/mock.js +13 -4
- package/dist/thread/__tests__/thread.test.js +241 -0
- package/dist/thread/thread.js +7 -7
- package/dist/tool/tool.d.ts +2 -2
- package/dist/tool/tool.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/agent.ts +13 -7
- package/src/thread/__tests__/mock.ts +13 -4
- package/src/thread/__tests__/thread.test.ts +280 -0
- package/src/thread/thread.ts +7 -7
- package/src/tool/tool.ts +1 -1
package/.turbo/turbo-build.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @kernl/core
|
|
2
2
|
|
|
3
|
+
## 0.12.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 296c377: Make tool execute method public for direct invocation
|
|
8
|
+
|
|
9
|
+
## 0.12.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 884e513: Align with @ai-sdk/provider v3 stable release
|
|
14
|
+
- Update LanguageModelUsage to nested structure (inputTokens.total, outputTokens.total, etc.)
|
|
15
|
+
- Update LanguageModelFinishReason to object with unified and raw properties
|
|
16
|
+
- Rename LanguageModelWarning to SharedWarning with updated structure
|
|
17
|
+
- Update tool type from "provider-defined" to "provider"
|
|
18
|
+
- Bump @ai-sdk peer dependencies from beta to stable (^3.0.3)
|
|
19
|
+
|
|
20
|
+
- 0576a77: Fix model not being updated when resuming thread from storage
|
|
21
|
+
- Updated dependencies [884e513]
|
|
22
|
+
- @kernl-sdk/protocol@0.5.1
|
|
23
|
+
- @kernl-sdk/retrieval@0.1.10
|
|
24
|
+
|
|
3
25
|
## 0.12.2
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,EACV,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,qBAAqB,EAC3B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzC,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EACV,UAAU,EACV,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AAExB,qBAAa,KAAK,CACd,QAAQ,GAAG,cAAc,EACzB,OAAO,SAAS,eAAe,GAAG,UAAU,CAE9C,SAAQ,SAAS,CAAC,QAAQ,EAAE,OAAO,CACnC,YAAW,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC;IAEzC,QAAQ,CAAC,IAAI,SAAS;IACtB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,4BAA4B,CAAC;IAErD,QAAQ,CAAC,UAAU,EAAE;QACnB,KAAK,EAAE,cAAc,EAAE,CAAC;QACxB,MAAM,EAAE,eAAe,CAAC,eAAe,CAAC,EAAE,CAAC;KAC5C,CAAC;IACF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAqB;IAC7C,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;gBAEtB,MAAM,EAAE,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC;IAYlD;;;;;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,OAAO,CAAC,CAAC,CAAC;
|
|
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,EACV,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,qBAAqB,EAC3B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzC,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EACV,UAAU,EACV,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AAExB,qBAAa,KAAK,CACd,QAAQ,GAAG,cAAc,EACzB,OAAO,SAAS,eAAe,GAAG,UAAU,CAE9C,SAAQ,SAAS,CAAC,QAAQ,EAAE,OAAO,CACnC,YAAW,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC;IAEzC,QAAQ,CAAC,IAAI,SAAS;IACtB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,4BAA4B,CAAC;IAErD,QAAQ,CAAC,UAAU,EAAE;QACnB,KAAK,EAAE,cAAc,EAAE,CAAC;QACxB,MAAM,EAAE,eAAe,CAAC,eAAe,CAAC,EAAE,CAAC;KAC5C,CAAC;IACF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAqB;IAC7C,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;gBAEtB,MAAM,EAAE,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC;IAYlD;;;;;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,OAAO,CAAC,CAAC,CAAC;IA4D/D;;;;;;;OAOG;IACI,MAAM,CACX,KAAK,EAAE,MAAM,GAAG,iBAAiB,EAAE,EACnC,OAAO,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,GACvC,aAAa,CAAC,iBAAiB,CAAC;IA6DnC;;;;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
|
@@ -27,14 +27,11 @@ export class Agent extends BaseAgent {
|
|
|
27
27
|
* @throws {MisconfiguredError} If the agent is not bound to a kernl instance
|
|
28
28
|
*/
|
|
29
29
|
async run(input, options) {
|
|
30
|
+
let thread = null;
|
|
30
31
|
if (!this.kernl) {
|
|
31
32
|
throw new MisconfiguredError(`Agent ${this.id} not bound to kernl. Call kernl.register(agent) first.`);
|
|
32
33
|
}
|
|
33
|
-
const items = typeof input === "string"
|
|
34
|
-
? [message({ role: "user", text: input })]
|
|
35
|
-
: input;
|
|
36
34
|
const tid = options?.threadId;
|
|
37
|
-
let thread = null;
|
|
38
35
|
if (tid) {
|
|
39
36
|
// no concurrent execution of same thread - correctness contract
|
|
40
37
|
// TODO: race condition - need to check again after async storage.get()
|
|
@@ -48,6 +45,9 @@ export class Agent extends BaseAgent {
|
|
|
48
45
|
}));
|
|
49
46
|
}
|
|
50
47
|
}
|
|
48
|
+
const items = typeof input === "string"
|
|
49
|
+
? [message({ role: "user", text: input })]
|
|
50
|
+
: input;
|
|
51
51
|
// create new thread if not found in storage or no tid provided
|
|
52
52
|
if (!thread) {
|
|
53
53
|
const ctx = options?.context
|
|
@@ -69,6 +69,9 @@ export class Agent extends BaseAgent {
|
|
|
69
69
|
if (options?.context) {
|
|
70
70
|
thread.context.context = options.context;
|
|
71
71
|
}
|
|
72
|
+
if (options?.model) {
|
|
73
|
+
thread.model = options.model;
|
|
74
|
+
}
|
|
72
75
|
thread.append(...items);
|
|
73
76
|
return this.kernl.schedule(thread);
|
|
74
77
|
}
|
|
@@ -124,6 +127,9 @@ export class Agent extends BaseAgent {
|
|
|
124
127
|
if (options?.context) {
|
|
125
128
|
thread.context.context = options.context;
|
|
126
129
|
}
|
|
130
|
+
if (options?.model) {
|
|
131
|
+
thread.model = options.model;
|
|
132
|
+
}
|
|
127
133
|
thread.append(...items);
|
|
128
134
|
yield* this.kernl.scheduleStream(thread);
|
|
129
135
|
}
|
package/dist/lib/env.d.ts
CHANGED
|
@@ -7,11 +7,11 @@ import { z } from "zod";
|
|
|
7
7
|
*/
|
|
8
8
|
declare const envSchema: z.ZodObject<{
|
|
9
9
|
LOG_LEVEL: z.ZodDefault<z.ZodEnum<{
|
|
10
|
-
error: "error";
|
|
11
10
|
trace: "trace";
|
|
12
11
|
debug: "debug";
|
|
13
12
|
info: "info";
|
|
14
13
|
warn: "warn";
|
|
14
|
+
error: "error";
|
|
15
15
|
fatal: "fatal";
|
|
16
16
|
}>>;
|
|
17
17
|
KERNL_LOG_MODEL_DATA: z.ZodPipe<z.ZodOptional<z.ZodEnum<{
|
|
@@ -31,7 +31,7 @@ declare const envSchema: z.ZodObject<{
|
|
|
31
31
|
* console.log(env.LOG_LEVEL);
|
|
32
32
|
*/
|
|
33
33
|
export declare const env: {
|
|
34
|
-
LOG_LEVEL: "
|
|
34
|
+
LOG_LEVEL: "trace" | "debug" | "info" | "warn" | "error" | "fatal";
|
|
35
35
|
KERNL_LOG_MODEL_DATA: boolean;
|
|
36
36
|
KERNL_LOG_TOOL_DATA: boolean;
|
|
37
37
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../../src/thread/__tests__/mock.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EAGrB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAE7B;;;;;GAKG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IACrD,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,QAAQ,UAAU;IAC3B,QAAQ,CAAC,OAAO,gBAAgB;IAE1B,QAAQ,CACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../../src/thread/__tests__/mock.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EAGrB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAE7B;;;;;GAKG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IACrD,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,QAAQ,UAAU;IAC3B,QAAQ,CAAC,OAAO,gBAAgB;IAE1B,QAAQ,CACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAyC1B,MAAM,CACX,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAAC,wBAAwB,CAAC;IAK1C;;OAEG;IACH,OAAO,CAAC,eAAe;IAgBvB;;OAEG;IACH,OAAO,CAAC,WAAW;IAKnB;;OAEG;IACH,OAAO,CAAC,UAAU;CAGnB"}
|
|
@@ -26,13 +26,22 @@ export class MockLanguageModel {
|
|
|
26
26
|
},
|
|
27
27
|
];
|
|
28
28
|
// Return with fake usage stats
|
|
29
|
+
const tokens = this.countTokens(userText);
|
|
29
30
|
return {
|
|
30
31
|
content,
|
|
31
|
-
finishReason: "stop",
|
|
32
|
+
finishReason: { unified: "stop", raw: "stop" },
|
|
32
33
|
usage: {
|
|
33
|
-
inputTokens:
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
inputTokens: {
|
|
35
|
+
total: tokens,
|
|
36
|
+
noCache: tokens,
|
|
37
|
+
cacheRead: undefined,
|
|
38
|
+
cacheWrite: undefined,
|
|
39
|
+
},
|
|
40
|
+
outputTokens: {
|
|
41
|
+
total: tokens,
|
|
42
|
+
text: tokens,
|
|
43
|
+
reasoning: undefined,
|
|
44
|
+
},
|
|
36
45
|
},
|
|
37
46
|
warnings: [],
|
|
38
47
|
};
|
|
@@ -936,6 +936,247 @@ describe("Thread", () => {
|
|
|
936
936
|
expect(thread._tick).toBe(2);
|
|
937
937
|
});
|
|
938
938
|
});
|
|
939
|
+
describe("Event Sequencing", () => {
|
|
940
|
+
it("should assign incrementing seq values to all persisted events", async () => {
|
|
941
|
+
let callCount = 0;
|
|
942
|
+
const model = createMockModel(async (req) => {
|
|
943
|
+
callCount++;
|
|
944
|
+
if (callCount === 1) {
|
|
945
|
+
return {
|
|
946
|
+
content: [
|
|
947
|
+
{
|
|
948
|
+
kind: "message",
|
|
949
|
+
id: "msg_1",
|
|
950
|
+
role: "assistant",
|
|
951
|
+
content: [],
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
kind: "tool.call",
|
|
955
|
+
toolId: "echo",
|
|
956
|
+
state: IN_PROGRESS,
|
|
957
|
+
callId: "call_1",
|
|
958
|
+
arguments: JSON.stringify({ text: "test" }),
|
|
959
|
+
},
|
|
960
|
+
],
|
|
961
|
+
finishReason: "stop",
|
|
962
|
+
usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
|
|
963
|
+
warnings: [],
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
return {
|
|
967
|
+
content: [
|
|
968
|
+
{
|
|
969
|
+
kind: "message",
|
|
970
|
+
id: "msg_2",
|
|
971
|
+
role: "assistant",
|
|
972
|
+
content: [{ kind: "text", text: "Done!" }],
|
|
973
|
+
},
|
|
974
|
+
],
|
|
975
|
+
finishReason: "stop",
|
|
976
|
+
usage: { inputTokens: 4, outputTokens: 2, totalTokens: 6 },
|
|
977
|
+
warnings: [],
|
|
978
|
+
};
|
|
979
|
+
});
|
|
980
|
+
const echoTool = tool({
|
|
981
|
+
id: "echo",
|
|
982
|
+
description: "Echoes input",
|
|
983
|
+
parameters: z.object({ text: z.string() }),
|
|
984
|
+
execute: async (ctx, { text }) => `Echo: ${text}`,
|
|
985
|
+
});
|
|
986
|
+
const agent = new Agent({
|
|
987
|
+
id: "test",
|
|
988
|
+
name: "Test",
|
|
989
|
+
instructions: "Test agent",
|
|
990
|
+
model,
|
|
991
|
+
toolkits: [
|
|
992
|
+
new FunctionToolkit({ id: "test-tools", tools: [echoTool] }),
|
|
993
|
+
],
|
|
994
|
+
});
|
|
995
|
+
const thread = new Thread({ agent, input: userMessage("test") });
|
|
996
|
+
await thread.execute();
|
|
997
|
+
const history = thread.history;
|
|
998
|
+
// Verify all events have seq values
|
|
999
|
+
for (const event of history) {
|
|
1000
|
+
expect(event).toHaveProperty("seq");
|
|
1001
|
+
expect(typeof event.seq).toBe("number");
|
|
1002
|
+
}
|
|
1003
|
+
// Verify seq values increment: 0, 1, 2, 3, 4
|
|
1004
|
+
const seqValues = history.map((e) => e.seq);
|
|
1005
|
+
expect(seqValues).toEqual([0, 1, 2, 3, 4]);
|
|
1006
|
+
});
|
|
1007
|
+
it("should assign consistent tid to all events in a thread", async () => {
|
|
1008
|
+
const model = createMockModel(async (req) => {
|
|
1009
|
+
return {
|
|
1010
|
+
content: [
|
|
1011
|
+
{
|
|
1012
|
+
kind: "message",
|
|
1013
|
+
id: "msg_1",
|
|
1014
|
+
role: "assistant",
|
|
1015
|
+
content: [{ kind: "text", text: "Hello" }],
|
|
1016
|
+
},
|
|
1017
|
+
],
|
|
1018
|
+
finishReason: "stop",
|
|
1019
|
+
usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
|
|
1020
|
+
warnings: [],
|
|
1021
|
+
};
|
|
1022
|
+
});
|
|
1023
|
+
const agent = new Agent({
|
|
1024
|
+
id: "test",
|
|
1025
|
+
name: "Test",
|
|
1026
|
+
instructions: "Test agent",
|
|
1027
|
+
model,
|
|
1028
|
+
});
|
|
1029
|
+
const thread = new Thread({ agent, input: userMessage("test") });
|
|
1030
|
+
await thread.execute();
|
|
1031
|
+
const history = thread.history;
|
|
1032
|
+
const threadId = thread.tid;
|
|
1033
|
+
// All events should have the same tid as the thread
|
|
1034
|
+
for (const event of history) {
|
|
1035
|
+
expect(event).toHaveProperty("tid");
|
|
1036
|
+
expect(event.tid).toBe(threadId);
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1039
|
+
it("should assign timestamps to all events", async () => {
|
|
1040
|
+
const model = createMockModel(async (req) => {
|
|
1041
|
+
return {
|
|
1042
|
+
content: [
|
|
1043
|
+
{
|
|
1044
|
+
kind: "message",
|
|
1045
|
+
id: "msg_1",
|
|
1046
|
+
role: "assistant",
|
|
1047
|
+
content: [{ kind: "text", text: "Hello" }],
|
|
1048
|
+
},
|
|
1049
|
+
],
|
|
1050
|
+
finishReason: "stop",
|
|
1051
|
+
usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
|
|
1052
|
+
warnings: [],
|
|
1053
|
+
};
|
|
1054
|
+
});
|
|
1055
|
+
const agent = new Agent({
|
|
1056
|
+
id: "test",
|
|
1057
|
+
name: "Test",
|
|
1058
|
+
instructions: "Test agent",
|
|
1059
|
+
model,
|
|
1060
|
+
});
|
|
1061
|
+
const thread = new Thread({ agent, input: userMessage("test") });
|
|
1062
|
+
await thread.execute();
|
|
1063
|
+
const history = thread.history;
|
|
1064
|
+
for (const event of history) {
|
|
1065
|
+
expect(event).toHaveProperty("timestamp");
|
|
1066
|
+
expect(event.timestamp).toBeInstanceOf(Date);
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
it("should generate event ids in expected format", async () => {
|
|
1070
|
+
let callCount = 0;
|
|
1071
|
+
const model = createMockModel(async (req) => {
|
|
1072
|
+
callCount++;
|
|
1073
|
+
if (callCount === 1) {
|
|
1074
|
+
return {
|
|
1075
|
+
content: [
|
|
1076
|
+
{
|
|
1077
|
+
kind: "message",
|
|
1078
|
+
id: "msg_1",
|
|
1079
|
+
role: "assistant",
|
|
1080
|
+
content: [],
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
kind: "tool.call",
|
|
1084
|
+
toolId: "simple",
|
|
1085
|
+
state: IN_PROGRESS,
|
|
1086
|
+
callId: "call_1",
|
|
1087
|
+
arguments: "{}",
|
|
1088
|
+
},
|
|
1089
|
+
],
|
|
1090
|
+
finishReason: "stop",
|
|
1091
|
+
usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
|
|
1092
|
+
warnings: [],
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
return {
|
|
1096
|
+
content: [
|
|
1097
|
+
{
|
|
1098
|
+
kind: "message",
|
|
1099
|
+
id: "msg_2",
|
|
1100
|
+
role: "assistant",
|
|
1101
|
+
content: [{ kind: "text", text: "Done" }],
|
|
1102
|
+
},
|
|
1103
|
+
],
|
|
1104
|
+
finishReason: "stop",
|
|
1105
|
+
usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
|
|
1106
|
+
warnings: [],
|
|
1107
|
+
};
|
|
1108
|
+
});
|
|
1109
|
+
const simpleTool = tool({
|
|
1110
|
+
id: "simple",
|
|
1111
|
+
description: "Simple tool",
|
|
1112
|
+
parameters: undefined,
|
|
1113
|
+
execute: async () => "result",
|
|
1114
|
+
});
|
|
1115
|
+
const agent = new Agent({
|
|
1116
|
+
id: "test",
|
|
1117
|
+
name: "Test",
|
|
1118
|
+
instructions: "Test agent",
|
|
1119
|
+
model,
|
|
1120
|
+
toolkits: [
|
|
1121
|
+
new FunctionToolkit({ id: "test-tools", tools: [simpleTool] }),
|
|
1122
|
+
],
|
|
1123
|
+
});
|
|
1124
|
+
const thread = new Thread({ agent, input: userMessage("test") });
|
|
1125
|
+
await thread.execute();
|
|
1126
|
+
const history = thread.history;
|
|
1127
|
+
// Messages should have id format "message:{original_id}"
|
|
1128
|
+
const messages = history.filter((e) => e.kind === "message");
|
|
1129
|
+
for (const msg of messages) {
|
|
1130
|
+
expect(msg.id).toMatch(/^message:/);
|
|
1131
|
+
}
|
|
1132
|
+
// Tool calls use callId (not id), so tevent falls back to randomID()
|
|
1133
|
+
const toolCalls = history.filter((e) => e.kind === "tool.call");
|
|
1134
|
+
for (const tc of toolCalls) {
|
|
1135
|
+
expect(tc.id).toBeDefined();
|
|
1136
|
+
expect(typeof tc.id).toBe("string");
|
|
1137
|
+
}
|
|
1138
|
+
// Tool results also use callId, so tevent falls back to randomID()
|
|
1139
|
+
const toolResults = history.filter((e) => e.kind === "tool.result");
|
|
1140
|
+
for (const tr of toolResults) {
|
|
1141
|
+
expect(tr.id).toBeDefined();
|
|
1142
|
+
expect(typeof tr.id).toBe("string");
|
|
1143
|
+
}
|
|
1144
|
+
});
|
|
1145
|
+
it("should yield sequenced events during streaming", async () => {
|
|
1146
|
+
const model = createMockModel(async (req) => {
|
|
1147
|
+
return {
|
|
1148
|
+
content: [
|
|
1149
|
+
{
|
|
1150
|
+
kind: "message",
|
|
1151
|
+
id: "msg_1",
|
|
1152
|
+
role: "assistant",
|
|
1153
|
+
content: [{ kind: "text", text: "Hello" }],
|
|
1154
|
+
},
|
|
1155
|
+
],
|
|
1156
|
+
finishReason: "stop",
|
|
1157
|
+
usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
|
|
1158
|
+
warnings: [],
|
|
1159
|
+
};
|
|
1160
|
+
});
|
|
1161
|
+
const agent = new Agent({
|
|
1162
|
+
id: "test",
|
|
1163
|
+
name: "Test",
|
|
1164
|
+
instructions: "Test agent",
|
|
1165
|
+
model,
|
|
1166
|
+
});
|
|
1167
|
+
const thread = new Thread({ agent, input: userMessage("test") });
|
|
1168
|
+
const events = [];
|
|
1169
|
+
for await (const event of thread.stream()) {
|
|
1170
|
+
events.push(event);
|
|
1171
|
+
}
|
|
1172
|
+
// Find the complete message event (not deltas)
|
|
1173
|
+
const messageEvent = events.find((e) => e.kind === "message" && e.role === "assistant");
|
|
1174
|
+
expect(messageEvent).toBeDefined();
|
|
1175
|
+
expect(messageEvent).toHaveProperty("seq");
|
|
1176
|
+
expect(messageEvent).toHaveProperty("tid");
|
|
1177
|
+
expect(messageEvent).toHaveProperty("timestamp");
|
|
1178
|
+
});
|
|
1179
|
+
});
|
|
939
1180
|
describe("Final Output Parsing", () => {
|
|
940
1181
|
it("should return text output when output is 'text'", async () => {
|
|
941
1182
|
const model = createMockModel(async (req) => {
|
package/dist/thread/thread.js
CHANGED
|
@@ -155,13 +155,13 @@ export class Thread {
|
|
|
155
155
|
for await (const e of this.tick()) {
|
|
156
156
|
if (e.kind === "error") {
|
|
157
157
|
err = e.error;
|
|
158
|
-
logger.error(
|
|
158
|
+
logger.error(err.message); // (TODO): onError callback in options
|
|
159
159
|
}
|
|
160
160
|
// complete items get persisted with seq, deltas are ephemeral
|
|
161
161
|
if (notDelta(e)) {
|
|
162
|
-
const [
|
|
163
|
-
events.push(
|
|
164
|
-
yield
|
|
162
|
+
const [eseq] = this.append(e);
|
|
163
|
+
events.push(eseq);
|
|
164
|
+
yield eseq;
|
|
165
165
|
}
|
|
166
166
|
else {
|
|
167
167
|
yield e;
|
|
@@ -187,8 +187,8 @@ export class Thread {
|
|
|
187
187
|
const { actions, pendingApprovals } = await this.performActions(intentions);
|
|
188
188
|
// append + yield action events (sequenced)
|
|
189
189
|
for (const a of actions) {
|
|
190
|
-
const [
|
|
191
|
-
yield
|
|
190
|
+
const [eseq] = this.append(a);
|
|
191
|
+
yield eseq;
|
|
192
192
|
}
|
|
193
193
|
await this.checkpoint(); /* c3: tick complete */
|
|
194
194
|
if (pendingApprovals.length > 0) {
|
|
@@ -216,7 +216,7 @@ export class Thread {
|
|
|
216
216
|
const req = await this.prepareModelRequest(this.history);
|
|
217
217
|
this.emit("model.call.start", { settings: req.settings ?? {} });
|
|
218
218
|
let usage;
|
|
219
|
-
let finishReason = "
|
|
219
|
+
let finishReason = { unified: "other", raw: undefined };
|
|
220
220
|
try {
|
|
221
221
|
if (this.model.stream) {
|
|
222
222
|
for await (const event of this.model.stream(req)) {
|
package/dist/tool/tool.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Context, UnknownContext } from "../context.js";
|
|
2
2
|
import { type LanguageModelTool } from "@kernl-sdk/protocol";
|
|
3
|
-
import type { ToolConfig, ToolApprovalFunction, ToolEnabledFunction, ToolErrorFunction, ToolInputParameters, ToolResult } from "./types.js";
|
|
3
|
+
import type { ToolConfig, ToolApprovalFunction, ToolEnabledFunction, ToolErrorFunction, ToolExecuteFunction, ToolInputParameters, ToolResult } from "./types.js";
|
|
4
4
|
/**
|
|
5
5
|
* Exposes a function to the agent as a tool to be called
|
|
6
6
|
*
|
|
@@ -42,7 +42,7 @@ export declare class FunctionTool<TContext = UnknownContext, TParameters extends
|
|
|
42
42
|
readonly description: string;
|
|
43
43
|
readonly parameters?: TParameters;
|
|
44
44
|
readonly mode: "blocking" | "async";
|
|
45
|
-
|
|
45
|
+
execute: ToolExecuteFunction<TContext, TParameters, TResult>;
|
|
46
46
|
errorfn: ToolErrorFunction | null;
|
|
47
47
|
requiresApproval: ToolApprovalFunction<TParameters>;
|
|
48
48
|
isEnabled: ToolEnabledFunction<TContext>;
|
package/dist/tool/tool.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/tool/tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMpD,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EACV,UAAU,EACV,oBAAoB,EACpB,mBAAmB,EAEnB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/tool/tool.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kernl",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.4",
|
|
4
4
|
"description": "A modern AI agent framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kernl",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
36
36
|
"yaml": "^2.8.2",
|
|
37
|
-
"@kernl-sdk/protocol": "0.5.
|
|
38
|
-
"@kernl-sdk/
|
|
39
|
-
"@kernl-sdk/
|
|
37
|
+
"@kernl-sdk/protocol": "0.5.1",
|
|
38
|
+
"@kernl-sdk/retrieval": "0.1.10",
|
|
39
|
+
"@kernl-sdk/shared": "^0.4.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"zod": "^4.1.8"
|
package/src/agent.ts
CHANGED
|
@@ -71,20 +71,15 @@ export class Agent<
|
|
|
71
71
|
input: string | LanguageModelItem[],
|
|
72
72
|
options?: ThreadExecuteOptions<TContext>,
|
|
73
73
|
): Promise<ThreadExecuteResult<ResolvedAgentResponse<TOutput>>> {
|
|
74
|
+
let thread: Thread<TContext, TOutput> | null = null;
|
|
75
|
+
|
|
74
76
|
if (!this.kernl) {
|
|
75
77
|
throw new MisconfiguredError(
|
|
76
78
|
`Agent ${this.id} not bound to kernl. Call kernl.register(agent) first.`,
|
|
77
79
|
);
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
const items =
|
|
81
|
-
typeof input === "string"
|
|
82
|
-
? [message({ role: "user", text: input })]
|
|
83
|
-
: input;
|
|
84
82
|
const tid = options?.threadId;
|
|
85
|
-
|
|
86
|
-
let thread: Thread<TContext, TOutput> | null = null;
|
|
87
|
-
|
|
88
83
|
if (tid) {
|
|
89
84
|
// no concurrent execution of same thread - correctness contract
|
|
90
85
|
// TODO: race condition - need to check again after async storage.get()
|
|
@@ -100,6 +95,11 @@ export class Agent<
|
|
|
100
95
|
}
|
|
101
96
|
}
|
|
102
97
|
|
|
98
|
+
const items =
|
|
99
|
+
typeof input === "string"
|
|
100
|
+
? [message({ role: "user", text: input })]
|
|
101
|
+
: input;
|
|
102
|
+
|
|
103
103
|
// create new thread if not found in storage or no tid provided
|
|
104
104
|
if (!thread) {
|
|
105
105
|
const ctx = options?.context
|
|
@@ -123,6 +123,9 @@ export class Agent<
|
|
|
123
123
|
if (options?.context) {
|
|
124
124
|
thread.context.context = options.context;
|
|
125
125
|
}
|
|
126
|
+
if (options?.model) {
|
|
127
|
+
thread.model = options.model;
|
|
128
|
+
}
|
|
126
129
|
thread.append(...items);
|
|
127
130
|
return this.kernl.schedule(thread);
|
|
128
131
|
}
|
|
@@ -192,6 +195,9 @@ export class Agent<
|
|
|
192
195
|
if (options?.context) {
|
|
193
196
|
thread.context.context = options.context;
|
|
194
197
|
}
|
|
198
|
+
if (options?.model) {
|
|
199
|
+
thread.model = options.model;
|
|
200
|
+
}
|
|
195
201
|
thread.append(...items);
|
|
196
202
|
yield* this.kernl.scheduleStream(thread);
|
|
197
203
|
}
|
|
@@ -40,13 +40,22 @@ export class MockLanguageModel implements LanguageModel {
|
|
|
40
40
|
];
|
|
41
41
|
|
|
42
42
|
// Return with fake usage stats
|
|
43
|
+
const tokens = this.countTokens(userText);
|
|
43
44
|
return {
|
|
44
45
|
content,
|
|
45
|
-
finishReason: "stop",
|
|
46
|
+
finishReason: { unified: "stop", raw: "stop" },
|
|
46
47
|
usage: {
|
|
47
|
-
inputTokens:
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
inputTokens: {
|
|
49
|
+
total: tokens,
|
|
50
|
+
noCache: tokens,
|
|
51
|
+
cacheRead: undefined,
|
|
52
|
+
cacheWrite: undefined,
|
|
53
|
+
},
|
|
54
|
+
outputTokens: {
|
|
55
|
+
total: tokens,
|
|
56
|
+
text: tokens,
|
|
57
|
+
reasoning: undefined,
|
|
58
|
+
},
|
|
50
59
|
},
|
|
51
60
|
warnings: [],
|
|
52
61
|
};
|
|
@@ -1060,6 +1060,286 @@ describe("Thread", () => {
|
|
|
1060
1060
|
});
|
|
1061
1061
|
});
|
|
1062
1062
|
|
|
1063
|
+
describe("Event Sequencing", () => {
|
|
1064
|
+
it("should assign incrementing seq values to all persisted events", async () => {
|
|
1065
|
+
let callCount = 0;
|
|
1066
|
+
|
|
1067
|
+
const model = createMockModel(async (req: LanguageModelRequest) => {
|
|
1068
|
+
callCount++;
|
|
1069
|
+
|
|
1070
|
+
if (callCount === 1) {
|
|
1071
|
+
return {
|
|
1072
|
+
content: [
|
|
1073
|
+
{
|
|
1074
|
+
kind: "message" as const,
|
|
1075
|
+
id: "msg_1",
|
|
1076
|
+
role: "assistant" as const,
|
|
1077
|
+
content: [],
|
|
1078
|
+
},
|
|
1079
|
+
{
|
|
1080
|
+
kind: "tool.call" as const,
|
|
1081
|
+
toolId: "echo",
|
|
1082
|
+
state: IN_PROGRESS,
|
|
1083
|
+
callId: "call_1",
|
|
1084
|
+
arguments: JSON.stringify({ text: "test" }),
|
|
1085
|
+
},
|
|
1086
|
+
],
|
|
1087
|
+
finishReason: "stop",
|
|
1088
|
+
usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
|
|
1089
|
+
warnings: [],
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
return {
|
|
1094
|
+
content: [
|
|
1095
|
+
{
|
|
1096
|
+
kind: "message" as const,
|
|
1097
|
+
id: "msg_2",
|
|
1098
|
+
role: "assistant" as const,
|
|
1099
|
+
content: [{ kind: "text" as const, text: "Done!" }],
|
|
1100
|
+
},
|
|
1101
|
+
],
|
|
1102
|
+
finishReason: "stop",
|
|
1103
|
+
usage: { inputTokens: 4, outputTokens: 2, totalTokens: 6 },
|
|
1104
|
+
warnings: [],
|
|
1105
|
+
};
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
const echoTool = tool({
|
|
1109
|
+
id: "echo",
|
|
1110
|
+
description: "Echoes input",
|
|
1111
|
+
parameters: z.object({ text: z.string() }),
|
|
1112
|
+
execute: async (ctx, { text }) => `Echo: ${text}`,
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
const agent = new Agent({
|
|
1116
|
+
id: "test",
|
|
1117
|
+
name: "Test",
|
|
1118
|
+
instructions: "Test agent",
|
|
1119
|
+
model,
|
|
1120
|
+
toolkits: [
|
|
1121
|
+
new FunctionToolkit({ id: "test-tools", tools: [echoTool] }),
|
|
1122
|
+
],
|
|
1123
|
+
});
|
|
1124
|
+
|
|
1125
|
+
const thread = new Thread({ agent, input: userMessage("test") });
|
|
1126
|
+
await thread.execute();
|
|
1127
|
+
|
|
1128
|
+
const history = (thread as any).history as ThreadEvent[];
|
|
1129
|
+
|
|
1130
|
+
// Verify all events have seq values
|
|
1131
|
+
for (const event of history) {
|
|
1132
|
+
expect(event).toHaveProperty("seq");
|
|
1133
|
+
expect(typeof event.seq).toBe("number");
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// Verify seq values increment: 0, 1, 2, 3, 4
|
|
1137
|
+
const seqValues = history.map((e) => e.seq);
|
|
1138
|
+
expect(seqValues).toEqual([0, 1, 2, 3, 4]);
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
it("should assign consistent tid to all events in a thread", async () => {
|
|
1142
|
+
const model = createMockModel(async (req: LanguageModelRequest) => {
|
|
1143
|
+
return {
|
|
1144
|
+
content: [
|
|
1145
|
+
{
|
|
1146
|
+
kind: "message" as const,
|
|
1147
|
+
id: "msg_1",
|
|
1148
|
+
role: "assistant" as const,
|
|
1149
|
+
content: [{ kind: "text" as const, text: "Hello" }],
|
|
1150
|
+
},
|
|
1151
|
+
],
|
|
1152
|
+
finishReason: "stop",
|
|
1153
|
+
usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
|
|
1154
|
+
warnings: [],
|
|
1155
|
+
};
|
|
1156
|
+
});
|
|
1157
|
+
|
|
1158
|
+
const agent = new Agent({
|
|
1159
|
+
id: "test",
|
|
1160
|
+
name: "Test",
|
|
1161
|
+
instructions: "Test agent",
|
|
1162
|
+
model,
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
const thread = new Thread({ agent, input: userMessage("test") });
|
|
1166
|
+
await thread.execute();
|
|
1167
|
+
|
|
1168
|
+
const history = (thread as any).history as ThreadEvent[];
|
|
1169
|
+
const threadId = thread.tid;
|
|
1170
|
+
|
|
1171
|
+
// All events should have the same tid as the thread
|
|
1172
|
+
for (const event of history) {
|
|
1173
|
+
expect(event).toHaveProperty("tid");
|
|
1174
|
+
expect(event.tid).toBe(threadId);
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
it("should assign timestamps to all events", async () => {
|
|
1179
|
+
const model = createMockModel(async (req: LanguageModelRequest) => {
|
|
1180
|
+
return {
|
|
1181
|
+
content: [
|
|
1182
|
+
{
|
|
1183
|
+
kind: "message" as const,
|
|
1184
|
+
id: "msg_1",
|
|
1185
|
+
role: "assistant" as const,
|
|
1186
|
+
content: [{ kind: "text" as const, text: "Hello" }],
|
|
1187
|
+
},
|
|
1188
|
+
],
|
|
1189
|
+
finishReason: "stop",
|
|
1190
|
+
usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
|
|
1191
|
+
warnings: [],
|
|
1192
|
+
};
|
|
1193
|
+
});
|
|
1194
|
+
|
|
1195
|
+
const agent = new Agent({
|
|
1196
|
+
id: "test",
|
|
1197
|
+
name: "Test",
|
|
1198
|
+
instructions: "Test agent",
|
|
1199
|
+
model,
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
const thread = new Thread({ agent, input: userMessage("test") });
|
|
1203
|
+
await thread.execute();
|
|
1204
|
+
|
|
1205
|
+
const history = (thread as any).history as ThreadEvent[];
|
|
1206
|
+
|
|
1207
|
+
for (const event of history) {
|
|
1208
|
+
expect(event).toHaveProperty("timestamp");
|
|
1209
|
+
expect(event.timestamp).toBeInstanceOf(Date);
|
|
1210
|
+
}
|
|
1211
|
+
});
|
|
1212
|
+
|
|
1213
|
+
it("should generate event ids in expected format", async () => {
|
|
1214
|
+
let callCount = 0;
|
|
1215
|
+
|
|
1216
|
+
const model = createMockModel(async (req: LanguageModelRequest) => {
|
|
1217
|
+
callCount++;
|
|
1218
|
+
|
|
1219
|
+
if (callCount === 1) {
|
|
1220
|
+
return {
|
|
1221
|
+
content: [
|
|
1222
|
+
{
|
|
1223
|
+
kind: "message" as const,
|
|
1224
|
+
id: "msg_1",
|
|
1225
|
+
role: "assistant" as const,
|
|
1226
|
+
content: [],
|
|
1227
|
+
},
|
|
1228
|
+
{
|
|
1229
|
+
kind: "tool.call" as const,
|
|
1230
|
+
toolId: "simple",
|
|
1231
|
+
state: IN_PROGRESS,
|
|
1232
|
+
callId: "call_1",
|
|
1233
|
+
arguments: "{}",
|
|
1234
|
+
},
|
|
1235
|
+
],
|
|
1236
|
+
finishReason: "stop",
|
|
1237
|
+
usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
|
|
1238
|
+
warnings: [],
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
return {
|
|
1243
|
+
content: [
|
|
1244
|
+
{
|
|
1245
|
+
kind: "message" as const,
|
|
1246
|
+
id: "msg_2",
|
|
1247
|
+
role: "assistant" as const,
|
|
1248
|
+
content: [{ kind: "text" as const, text: "Done" }],
|
|
1249
|
+
},
|
|
1250
|
+
],
|
|
1251
|
+
finishReason: "stop",
|
|
1252
|
+
usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
|
|
1253
|
+
warnings: [],
|
|
1254
|
+
};
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1257
|
+
const simpleTool = tool({
|
|
1258
|
+
id: "simple",
|
|
1259
|
+
description: "Simple tool",
|
|
1260
|
+
parameters: undefined,
|
|
1261
|
+
execute: async () => "result",
|
|
1262
|
+
});
|
|
1263
|
+
|
|
1264
|
+
const agent = new Agent({
|
|
1265
|
+
id: "test",
|
|
1266
|
+
name: "Test",
|
|
1267
|
+
instructions: "Test agent",
|
|
1268
|
+
model,
|
|
1269
|
+
toolkits: [
|
|
1270
|
+
new FunctionToolkit({ id: "test-tools", tools: [simpleTool] }),
|
|
1271
|
+
],
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
const thread = new Thread({ agent, input: userMessage("test") });
|
|
1275
|
+
await thread.execute();
|
|
1276
|
+
|
|
1277
|
+
const history = (thread as any).history as ThreadEvent[];
|
|
1278
|
+
|
|
1279
|
+
// Messages should have id format "message:{original_id}"
|
|
1280
|
+
const messages = history.filter((e) => e.kind === "message");
|
|
1281
|
+
for (const msg of messages) {
|
|
1282
|
+
expect(msg.id).toMatch(/^message:/);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
// Tool calls use callId (not id), so tevent falls back to randomID()
|
|
1286
|
+
const toolCalls = history.filter((e) => e.kind === "tool.call");
|
|
1287
|
+
for (const tc of toolCalls) {
|
|
1288
|
+
expect(tc.id).toBeDefined();
|
|
1289
|
+
expect(typeof tc.id).toBe("string");
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
// Tool results also use callId, so tevent falls back to randomID()
|
|
1293
|
+
const toolResults = history.filter((e) => e.kind === "tool.result");
|
|
1294
|
+
for (const tr of toolResults) {
|
|
1295
|
+
expect(tr.id).toBeDefined();
|
|
1296
|
+
expect(typeof tr.id).toBe("string");
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
|
|
1300
|
+
it("should yield sequenced events during streaming", async () => {
|
|
1301
|
+
const model = createMockModel(async (req: LanguageModelRequest) => {
|
|
1302
|
+
return {
|
|
1303
|
+
content: [
|
|
1304
|
+
{
|
|
1305
|
+
kind: "message" as const,
|
|
1306
|
+
id: "msg_1",
|
|
1307
|
+
role: "assistant" as const,
|
|
1308
|
+
content: [{ kind: "text" as const, text: "Hello" }],
|
|
1309
|
+
},
|
|
1310
|
+
],
|
|
1311
|
+
finishReason: "stop",
|
|
1312
|
+
usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
|
|
1313
|
+
warnings: [],
|
|
1314
|
+
};
|
|
1315
|
+
});
|
|
1316
|
+
|
|
1317
|
+
const agent = new Agent({
|
|
1318
|
+
id: "test",
|
|
1319
|
+
name: "Test",
|
|
1320
|
+
instructions: "Test agent",
|
|
1321
|
+
model,
|
|
1322
|
+
});
|
|
1323
|
+
|
|
1324
|
+
const thread = new Thread({ agent, input: userMessage("test") });
|
|
1325
|
+
|
|
1326
|
+
const events: any[] = [];
|
|
1327
|
+
for await (const event of thread.stream()) {
|
|
1328
|
+
events.push(event);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
// Find the complete message event (not deltas)
|
|
1332
|
+
const messageEvent = events.find(
|
|
1333
|
+
(e) => e.kind === "message" && e.role === "assistant",
|
|
1334
|
+
);
|
|
1335
|
+
|
|
1336
|
+
expect(messageEvent).toBeDefined();
|
|
1337
|
+
expect(messageEvent).toHaveProperty("seq");
|
|
1338
|
+
expect(messageEvent).toHaveProperty("tid");
|
|
1339
|
+
expect(messageEvent).toHaveProperty("timestamp");
|
|
1340
|
+
});
|
|
1341
|
+
});
|
|
1342
|
+
|
|
1063
1343
|
describe("Final Output Parsing", () => {
|
|
1064
1344
|
it("should return text output when output is 'text'", async () => {
|
|
1065
1345
|
const model = createMockModel(async (req: LanguageModelRequest) => {
|
package/src/thread/thread.ts
CHANGED
|
@@ -216,13 +216,13 @@ export class Thread<
|
|
|
216
216
|
for await (const e of this.tick()) {
|
|
217
217
|
if (e.kind === "error") {
|
|
218
218
|
err = e.error;
|
|
219
|
-
logger.error(
|
|
219
|
+
logger.error(err.message); // (TODO): onError callback in options
|
|
220
220
|
}
|
|
221
221
|
// complete items get persisted with seq, deltas are ephemeral
|
|
222
222
|
if (notDelta(e)) {
|
|
223
|
-
const [
|
|
224
|
-
events.push(
|
|
225
|
-
yield
|
|
223
|
+
const [eseq] = this.append(e);
|
|
224
|
+
events.push(eseq);
|
|
225
|
+
yield eseq;
|
|
226
226
|
} else {
|
|
227
227
|
yield e;
|
|
228
228
|
}
|
|
@@ -253,8 +253,8 @@ export class Thread<
|
|
|
253
253
|
|
|
254
254
|
// append + yield action events (sequenced)
|
|
255
255
|
for (const a of actions) {
|
|
256
|
-
const [
|
|
257
|
-
yield
|
|
256
|
+
const [eseq] = this.append(a);
|
|
257
|
+
yield eseq;
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
await this.checkpoint(); /* c3: tick complete */
|
|
@@ -289,7 +289,7 @@ export class Thread<
|
|
|
289
289
|
this.emit("model.call.start", { settings: req.settings ?? {} });
|
|
290
290
|
|
|
291
291
|
let usage: LanguageModelUsage | undefined;
|
|
292
|
-
let finishReason: LanguageModelFinishReason = "
|
|
292
|
+
let finishReason: LanguageModelFinishReason = { unified: "other", raw: undefined };
|
|
293
293
|
|
|
294
294
|
try {
|
|
295
295
|
if (this.model.stream) {
|
package/src/tool/tool.ts
CHANGED
|
@@ -84,7 +84,7 @@ export class FunctionTool<
|
|
|
84
84
|
readonly description: string;
|
|
85
85
|
readonly parameters?: TParameters;
|
|
86
86
|
readonly mode: "blocking" | "async";
|
|
87
|
-
|
|
87
|
+
execute: ToolExecuteFunction<TContext, TParameters, TResult>;
|
|
88
88
|
|
|
89
89
|
errorfn: ToolErrorFunction | null;
|
|
90
90
|
requiresApproval: ToolApprovalFunction<TParameters>;
|