@openharness/core 0.3.3 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/agent-registry.test.d.ts +2 -0
- package/dist/__tests__/agent-registry.test.d.ts.map +1 -0
- package/dist/__tests__/agent-registry.test.js +429 -0
- package/dist/__tests__/agent-registry.test.js.map +1 -0
- package/dist/__tests__/conversation.test.d.ts +2 -0
- package/dist/__tests__/conversation.test.d.ts.map +1 -0
- package/dist/__tests__/conversation.test.js +127 -0
- package/dist/__tests__/conversation.test.js.map +1 -0
- package/dist/__tests__/messages.test.js +15 -2
- package/dist/__tests__/messages.test.js.map +1 -1
- package/dist/__tests__/middleware/compaction.test.d.ts +2 -0
- package/dist/__tests__/middleware/compaction.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/compaction.test.js +163 -0
- package/dist/__tests__/middleware/compaction.test.js.map +1 -0
- package/dist/__tests__/middleware/hooks.test.d.ts +2 -0
- package/dist/__tests__/middleware/hooks.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/hooks.test.js +97 -0
- package/dist/__tests__/middleware/hooks.test.js.map +1 -0
- package/dist/__tests__/middleware/persistence.test.d.ts +2 -0
- package/dist/__tests__/middleware/persistence.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/persistence.test.js +70 -0
- package/dist/__tests__/middleware/persistence.test.js.map +1 -0
- package/dist/__tests__/middleware/retry.test.d.ts +2 -0
- package/dist/__tests__/middleware/retry.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/retry.test.js +151 -0
- package/dist/__tests__/middleware/retry.test.js.map +1 -0
- package/dist/__tests__/middleware/turn-tracking.test.d.ts +2 -0
- package/dist/__tests__/middleware/turn-tracking.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/turn-tracking.test.js +70 -0
- package/dist/__tests__/middleware/turn-tracking.test.js.map +1 -0
- package/dist/__tests__/runner.test.d.ts +2 -0
- package/dist/__tests__/runner.test.d.ts.map +1 -0
- package/dist/__tests__/runner.test.js +111 -0
- package/dist/__tests__/runner.test.js.map +1 -0
- package/dist/__tests__/stream.test.d.ts +2 -0
- package/dist/__tests__/stream.test.d.ts.map +1 -0
- package/dist/__tests__/stream.test.js +100 -0
- package/dist/__tests__/stream.test.js.map +1 -0
- package/dist/__tests__/ui-stream.test.js +105 -0
- package/dist/__tests__/ui-stream.test.js.map +1 -1
- package/dist/agent-registry.d.ts +90 -0
- package/dist/agent-registry.d.ts.map +1 -0
- package/dist/agent-registry.js +217 -0
- package/dist/agent-registry.js.map +1 -0
- package/dist/agent.d.ts +13 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +186 -41
- package/dist/agent.js.map +1 -1
- package/dist/conversation.d.ts +46 -0
- package/dist/conversation.d.ts.map +1 -0
- package/dist/conversation.js +66 -0
- package/dist/conversation.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/middleware/compaction.d.ts +20 -0
- package/dist/middleware/compaction.d.ts.map +1 -0
- package/dist/middleware/compaction.js +73 -0
- package/dist/middleware/compaction.js.map +1 -0
- package/dist/middleware/hooks.d.ts +10 -0
- package/dist/middleware/hooks.d.ts.map +1 -0
- package/dist/middleware/hooks.js +36 -0
- package/dist/middleware/hooks.js.map +1 -0
- package/dist/middleware/index.d.ts +6 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +6 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/persistence.d.ts +12 -0
- package/dist/middleware/persistence.d.ts.map +1 -0
- package/dist/middleware/persistence.js +15 -0
- package/dist/middleware/persistence.js.map +1 -0
- package/dist/middleware/retry.d.ts +8 -0
- package/dist/middleware/retry.d.ts.map +1 -0
- package/dist/middleware/retry.js +70 -0
- package/dist/middleware/retry.js.map +1 -0
- package/dist/middleware/turn-tracking.d.ts +7 -0
- package/dist/middleware/turn-tracking.d.ts.map +1 -0
- package/dist/middleware/turn-tracking.js +25 -0
- package/dist/middleware/turn-tracking.js.map +1 -0
- package/dist/runner.d.ts +23 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +18 -0
- package/dist/runner.js.map +1 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +7 -40
- package/dist/session.js.map +1 -1
- package/dist/stream.d.ts +12 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +38 -0
- package/dist/stream.js.map +1 -0
- package/dist/ui-stream.d.ts.map +1 -1
- package/dist/ui-stream.js +13 -1
- package/dist/ui-stream.js.map +1 -1
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +40 -0
- package/dist/utils.js.map +1 -0
- package/package.json +1 -1
|
@@ -36,18 +36,31 @@ describe("extractUserInput", () => {
|
|
|
36
36
|
const result = await extractUserInput([msg]);
|
|
37
37
|
expect(result).toBe("hello world");
|
|
38
38
|
});
|
|
39
|
-
it("returns ModelMessage[] for
|
|
39
|
+
it("returns ModelMessage[] with stripped base64 for data URL files", async () => {
|
|
40
40
|
const msg = makeFileMessage("image/png", "data:image/png;base64,iVBOR", "test.png", "describe this image");
|
|
41
41
|
const result = await extractUserInput([msg]);
|
|
42
42
|
expect(Array.isArray(result)).toBe(true);
|
|
43
43
|
const messages = result;
|
|
44
|
-
expect(messages
|
|
44
|
+
expect(messages).toHaveLength(1);
|
|
45
45
|
expect(messages[0].role).toBe("user");
|
|
46
|
+
const content = messages[0].content;
|
|
47
|
+
expect(content).toHaveLength(2);
|
|
48
|
+
expect(content[0]).toEqual({ type: "text", text: "describe this image" });
|
|
49
|
+
expect(content[1]).toEqual({
|
|
50
|
+
type: "file",
|
|
51
|
+
data: "iVBOR",
|
|
52
|
+
mediaType: "image/png",
|
|
53
|
+
filename: "test.png",
|
|
54
|
+
});
|
|
46
55
|
});
|
|
47
56
|
it("returns ModelMessage[] for file-only (no text)", async () => {
|
|
48
57
|
const msg = makeFileMessage("image/jpeg", "data:image/jpeg;base64,/9j/4A");
|
|
49
58
|
const result = await extractUserInput([msg]);
|
|
50
59
|
expect(Array.isArray(result)).toBe(true);
|
|
60
|
+
const messages = result;
|
|
61
|
+
expect(messages[0].content).toHaveLength(1);
|
|
62
|
+
expect(messages[0].content[0].type).toBe("file");
|
|
63
|
+
expect(messages[0].content[0].data).toBe("/9j/4A");
|
|
51
64
|
});
|
|
52
65
|
it("uses only the last message", async () => {
|
|
53
66
|
const result = await extractUserInput([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.test.js","sourceRoot":"","sources":["../../src/__tests__/messages.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGlD,SAAS,eAAe,CAAC,IAAY,EAAE,OAA6B,MAAM;IACxE,OAAO;QACL,EAAE,EAAE,OAAO;QACX,IAAI;QACJ,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;KAChC,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CACtB,SAAiB,EACjB,GAAW,EACX,QAAiB,EACjB,IAAa;IAEb,MAAM,KAAK,GAAuB,EAAE,CAAC;IACrC,IAAI,IAAI,EAAE,CAAC;QACT,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;IACvD,OAAO;QACL,EAAE,EAAE,OAAO;QACX,IAAI,EAAE,MAAM;QACZ,KAAK;KACN,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QACxE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,GAAG,GAAc;YACrB,EAAE,EAAE,OAAO;YACX,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE;gBACL,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAChC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE;aAChC;SACA,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"messages.test.js","sourceRoot":"","sources":["../../src/__tests__/messages.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/middleware/compaction.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { withCompaction } from "../../middleware/compaction.js";
|
|
3
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
4
|
+
async function collect(gen) {
|
|
5
|
+
const result = [];
|
|
6
|
+
for await (const event of gen)
|
|
7
|
+
result.push(event);
|
|
8
|
+
return result;
|
|
9
|
+
}
|
|
10
|
+
const doneEvent = {
|
|
11
|
+
type: "done",
|
|
12
|
+
result: "complete",
|
|
13
|
+
messages: [],
|
|
14
|
+
totalUsage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
15
|
+
};
|
|
16
|
+
function createMockStrategy(result) {
|
|
17
|
+
return {
|
|
18
|
+
compact: vi.fn().mockResolvedValue({
|
|
19
|
+
messages: result?.messages ?? [{ role: "user", content: "compacted" }],
|
|
20
|
+
summary: result?.summary,
|
|
21
|
+
messagesRemoved: result?.messagesRemoved ?? 5,
|
|
22
|
+
tokensPruned: result?.tokensPruned ?? 10000,
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
// ── Tests ───────────────────────────────────────────────────────────
|
|
27
|
+
describe("withCompaction", () => {
|
|
28
|
+
it("does not compact when under threshold", async () => {
|
|
29
|
+
const strategy = createMockStrategy();
|
|
30
|
+
const runner = async function* () {
|
|
31
|
+
yield doneEvent;
|
|
32
|
+
};
|
|
33
|
+
const compacted = withCompaction({
|
|
34
|
+
contextWindow: 200_000,
|
|
35
|
+
model: {},
|
|
36
|
+
strategy,
|
|
37
|
+
})(runner);
|
|
38
|
+
// lastInputTokens starts at 0, threshold is 200_000 - 20_000 = 180_000
|
|
39
|
+
const events = await collect(compacted([], "test"));
|
|
40
|
+
expect(strategy.compact).not.toHaveBeenCalled();
|
|
41
|
+
expect(events.find((e) => e.type === "compaction.start")).toBeUndefined();
|
|
42
|
+
});
|
|
43
|
+
it("compacts when over threshold", async () => {
|
|
44
|
+
const strategy = createMockStrategy({ tokensPruned: 50000 });
|
|
45
|
+
let receivedHistory = [];
|
|
46
|
+
const runner = async function* (history) {
|
|
47
|
+
receivedHistory = [...history];
|
|
48
|
+
// Emit step.done with high inputTokens to trigger next compaction
|
|
49
|
+
yield {
|
|
50
|
+
type: "step.done",
|
|
51
|
+
stepNumber: 1,
|
|
52
|
+
usage: { inputTokens: 190_000, outputTokens: 100, totalTokens: 190_100 },
|
|
53
|
+
finishReason: "stop",
|
|
54
|
+
};
|
|
55
|
+
yield doneEvent;
|
|
56
|
+
};
|
|
57
|
+
const compacted = withCompaction({
|
|
58
|
+
contextWindow: 200_000,
|
|
59
|
+
model: {},
|
|
60
|
+
strategy,
|
|
61
|
+
})(runner);
|
|
62
|
+
// First call: no compaction (lastInputTokens = 0)
|
|
63
|
+
const history = [{ role: "user", content: "a long conversation" }];
|
|
64
|
+
await collect(compacted(history, "test"));
|
|
65
|
+
expect(strategy.compact).not.toHaveBeenCalled();
|
|
66
|
+
// Second call: should compact (lastInputTokens = 190_000 >= 180_000)
|
|
67
|
+
const events2 = await collect(compacted(history, "test2"));
|
|
68
|
+
expect(strategy.compact).toHaveBeenCalled();
|
|
69
|
+
const compactionStart = events2.find((e) => e.type === "compaction.start");
|
|
70
|
+
expect(compactionStart).toBeDefined();
|
|
71
|
+
expect(compactionStart.reason).toBe("overflow");
|
|
72
|
+
const compactionDone = events2.find((e) => e.type === "compaction.done");
|
|
73
|
+
expect(compactionDone).toBeDefined();
|
|
74
|
+
// Runner should receive compacted history
|
|
75
|
+
expect(receivedHistory).toEqual([{ role: "user", content: "compacted" }]);
|
|
76
|
+
});
|
|
77
|
+
it("yields compaction lifecycle events", async () => {
|
|
78
|
+
const strategy = createMockStrategy({
|
|
79
|
+
tokensPruned: 5000,
|
|
80
|
+
summary: "A summary",
|
|
81
|
+
messagesRemoved: 3,
|
|
82
|
+
});
|
|
83
|
+
const runner = async function* () {
|
|
84
|
+
yield {
|
|
85
|
+
type: "step.done",
|
|
86
|
+
stepNumber: 1,
|
|
87
|
+
usage: { inputTokens: 190_000, outputTokens: 100, totalTokens: 190_100 },
|
|
88
|
+
finishReason: "stop",
|
|
89
|
+
};
|
|
90
|
+
yield doneEvent;
|
|
91
|
+
};
|
|
92
|
+
const compacted = withCompaction({
|
|
93
|
+
contextWindow: 200_000,
|
|
94
|
+
model: {},
|
|
95
|
+
strategy,
|
|
96
|
+
})(runner);
|
|
97
|
+
// First call to set lastInputTokens
|
|
98
|
+
const history = [{ role: "user", content: "stuff" }];
|
|
99
|
+
await collect(compacted(history, "test"));
|
|
100
|
+
// Second call triggers compaction
|
|
101
|
+
const events = await collect(compacted(history, "test2"));
|
|
102
|
+
const types = events.map((e) => e.type);
|
|
103
|
+
expect(types).toContain("compaction.start");
|
|
104
|
+
expect(types).toContain("compaction.pruned");
|
|
105
|
+
expect(types).toContain("compaction.summary");
|
|
106
|
+
expect(types).toContain("compaction.done");
|
|
107
|
+
});
|
|
108
|
+
it("supports custom shouldCompact", async () => {
|
|
109
|
+
const strategy = createMockStrategy({ tokensPruned: 100 });
|
|
110
|
+
const runner = async function* () {
|
|
111
|
+
yield doneEvent;
|
|
112
|
+
};
|
|
113
|
+
const compacted = withCompaction({
|
|
114
|
+
contextWindow: 200_000,
|
|
115
|
+
model: {},
|
|
116
|
+
strategy,
|
|
117
|
+
shouldCompact: () => true, // Always compact
|
|
118
|
+
})(runner);
|
|
119
|
+
const history = [{ role: "user", content: "hi" }];
|
|
120
|
+
const events = await collect(compacted(history, "test"));
|
|
121
|
+
expect(strategy.compact).toHaveBeenCalled();
|
|
122
|
+
expect(events.find((e) => e.type === "compaction.start")).toBeDefined();
|
|
123
|
+
});
|
|
124
|
+
it("does not compact on empty history", async () => {
|
|
125
|
+
const strategy = createMockStrategy();
|
|
126
|
+
const runner = async function* () {
|
|
127
|
+
yield doneEvent;
|
|
128
|
+
};
|
|
129
|
+
const compacted = withCompaction({
|
|
130
|
+
contextWindow: 200_000,
|
|
131
|
+
model: {},
|
|
132
|
+
strategy,
|
|
133
|
+
shouldCompact: () => true,
|
|
134
|
+
})(runner);
|
|
135
|
+
const events = await collect(compacted([], "test"));
|
|
136
|
+
expect(strategy.compact).not.toHaveBeenCalled();
|
|
137
|
+
});
|
|
138
|
+
it("tracks lastInputTokens from step.done events across calls", async () => {
|
|
139
|
+
const strategy = createMockStrategy();
|
|
140
|
+
const runner = async function* () {
|
|
141
|
+
yield {
|
|
142
|
+
type: "step.done",
|
|
143
|
+
stepNumber: 1,
|
|
144
|
+
usage: { inputTokens: 50_000, outputTokens: 100, totalTokens: 50_100 },
|
|
145
|
+
finishReason: "stop",
|
|
146
|
+
};
|
|
147
|
+
yield doneEvent;
|
|
148
|
+
};
|
|
149
|
+
const shouldCompact = vi.fn().mockReturnValue(false);
|
|
150
|
+
const compacted = withCompaction({
|
|
151
|
+
contextWindow: 200_000,
|
|
152
|
+
model: {},
|
|
153
|
+
strategy,
|
|
154
|
+
shouldCompact,
|
|
155
|
+
})(runner);
|
|
156
|
+
await collect(compacted([], "first"));
|
|
157
|
+
await collect(compacted([], "second"));
|
|
158
|
+
// On second call, shouldCompact should see lastInputTokens from first call
|
|
159
|
+
expect(shouldCompact).toHaveBeenCalledTimes(2);
|
|
160
|
+
expect(shouldCompact.mock.calls[1][0].lastInputTokens).toBe(50_000);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
//# sourceMappingURL=compaction.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction.test.js","sourceRoot":"","sources":["../../../src/__tests__/middleware/compaction.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/middleware/hooks.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { withHooks } from "../../middleware/hooks.js";
|
|
3
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
4
|
+
async function collect(gen) {
|
|
5
|
+
const result = [];
|
|
6
|
+
for await (const event of gen)
|
|
7
|
+
result.push(event);
|
|
8
|
+
return result;
|
|
9
|
+
}
|
|
10
|
+
const doneEvent = {
|
|
11
|
+
type: "done",
|
|
12
|
+
result: "complete",
|
|
13
|
+
messages: [{ role: "user", content: "hi" }],
|
|
14
|
+
totalUsage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
15
|
+
};
|
|
16
|
+
// ── Tests ───────────────────────────────────────────────────────────
|
|
17
|
+
describe("withHooks", () => {
|
|
18
|
+
describe("onBeforeSend", () => {
|
|
19
|
+
it("modifies history before passing to inner runner", async () => {
|
|
20
|
+
const receivedHistory = [];
|
|
21
|
+
const runner = async function* (history) {
|
|
22
|
+
receivedHistory.push(...history);
|
|
23
|
+
yield doneEvent;
|
|
24
|
+
};
|
|
25
|
+
const hooked = withHooks({
|
|
26
|
+
onBeforeSend: (msgs) => [...msgs, { role: "user", content: "injected" }],
|
|
27
|
+
})(runner);
|
|
28
|
+
await collect(hooked([{ role: "user", content: "original" }], "test"));
|
|
29
|
+
expect(receivedHistory).toEqual([
|
|
30
|
+
{ role: "user", content: "original" },
|
|
31
|
+
{ role: "user", content: "injected" },
|
|
32
|
+
]);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe("onAfterResponse", () => {
|
|
36
|
+
it("is called after done event with turn info", async () => {
|
|
37
|
+
const onAfterResponse = vi.fn();
|
|
38
|
+
const runner = async function* () {
|
|
39
|
+
yield doneEvent;
|
|
40
|
+
};
|
|
41
|
+
const hooked = withHooks({ onAfterResponse })(runner);
|
|
42
|
+
await collect(hooked([], "test"));
|
|
43
|
+
expect(onAfterResponse).toHaveBeenCalledOnce();
|
|
44
|
+
expect(onAfterResponse).toHaveBeenCalledWith({
|
|
45
|
+
turnNumber: 0,
|
|
46
|
+
messages: doneEvent.messages,
|
|
47
|
+
usage: doneEvent.totalUsage,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
it("is not called when no done event", async () => {
|
|
51
|
+
const onAfterResponse = vi.fn();
|
|
52
|
+
const runner = async function* () {
|
|
53
|
+
yield { type: "text.delta", text: "hi" };
|
|
54
|
+
};
|
|
55
|
+
const hooked = withHooks({ onAfterResponse })(runner);
|
|
56
|
+
await collect(hooked([], "test"));
|
|
57
|
+
expect(onAfterResponse).not.toHaveBeenCalled();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe("onError", () => {
|
|
61
|
+
it("can suppress error events", async () => {
|
|
62
|
+
const runner = async function* () {
|
|
63
|
+
yield { type: "error", error: new Error("transient") };
|
|
64
|
+
yield doneEvent;
|
|
65
|
+
};
|
|
66
|
+
const hooked = withHooks({
|
|
67
|
+
onError: () => true,
|
|
68
|
+
})(runner);
|
|
69
|
+
const events = await collect(hooked([], "test"));
|
|
70
|
+
expect(events.find((e) => e.type === "error")).toBeUndefined();
|
|
71
|
+
expect(events.find((e) => e.type === "done")).toBeDefined();
|
|
72
|
+
});
|
|
73
|
+
it("passes through errors when onError returns false", async () => {
|
|
74
|
+
const runner = async function* () {
|
|
75
|
+
yield { type: "error", error: new Error("fatal") };
|
|
76
|
+
yield doneEvent;
|
|
77
|
+
};
|
|
78
|
+
const hooked = withHooks({
|
|
79
|
+
onError: () => false,
|
|
80
|
+
})(runner);
|
|
81
|
+
const events = await collect(hooked([], "test"));
|
|
82
|
+
expect(events.find((e) => e.type === "error")).toBeDefined();
|
|
83
|
+
});
|
|
84
|
+
it("passes through errors when onError returns undefined", async () => {
|
|
85
|
+
const runner = async function* () {
|
|
86
|
+
yield { type: "error", error: new Error("fatal") };
|
|
87
|
+
yield doneEvent;
|
|
88
|
+
};
|
|
89
|
+
const hooked = withHooks({
|
|
90
|
+
onError: () => { },
|
|
91
|
+
})(runner);
|
|
92
|
+
const events = await collect(hooked([], "test"));
|
|
93
|
+
expect(events.find((e) => e.type === "error")).toBeDefined();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
//# sourceMappingURL=hooks.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.test.js","sourceRoot":"","sources":["../../../src/__tests__/middleware/hooks.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/middleware/persistence.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { withPersistence } from "../../middleware/persistence.js";
|
|
3
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
4
|
+
function createMockRunner(events) {
|
|
5
|
+
return async function* () {
|
|
6
|
+
for (const event of events)
|
|
7
|
+
yield event;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
async function collect(gen) {
|
|
11
|
+
const result = [];
|
|
12
|
+
for await (const event of gen)
|
|
13
|
+
result.push(event);
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
const messages = [
|
|
17
|
+
{ role: "user", content: "hi" },
|
|
18
|
+
{ role: "assistant", content: "hello" },
|
|
19
|
+
];
|
|
20
|
+
const doneEvent = {
|
|
21
|
+
type: "done",
|
|
22
|
+
result: "complete",
|
|
23
|
+
messages,
|
|
24
|
+
totalUsage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
25
|
+
};
|
|
26
|
+
// ── Tests ───────────────────────────────────────────────────────────
|
|
27
|
+
describe("withPersistence", () => {
|
|
28
|
+
it("saves messages on done event", async () => {
|
|
29
|
+
const store = {
|
|
30
|
+
load: vi.fn(),
|
|
31
|
+
save: vi.fn().mockResolvedValue(undefined),
|
|
32
|
+
};
|
|
33
|
+
const runner = createMockRunner([
|
|
34
|
+
{ type: "text.delta", text: "hello" },
|
|
35
|
+
doneEvent,
|
|
36
|
+
]);
|
|
37
|
+
const persisted = withPersistence({ store, sessionId: "s1" })(runner);
|
|
38
|
+
await collect(persisted([], "test"));
|
|
39
|
+
expect(store.save).toHaveBeenCalledOnce();
|
|
40
|
+
expect(store.save).toHaveBeenCalledWith("s1", messages);
|
|
41
|
+
});
|
|
42
|
+
it("does not save when no done event", async () => {
|
|
43
|
+
const store = {
|
|
44
|
+
load: vi.fn(),
|
|
45
|
+
save: vi.fn(),
|
|
46
|
+
};
|
|
47
|
+
const runner = createMockRunner([
|
|
48
|
+
{ type: "text.delta", text: "hello" },
|
|
49
|
+
]);
|
|
50
|
+
const persisted = withPersistence({ store, sessionId: "s1" })(runner);
|
|
51
|
+
await collect(persisted([], "test"));
|
|
52
|
+
expect(store.save).not.toHaveBeenCalled();
|
|
53
|
+
});
|
|
54
|
+
it("passes all events through unchanged", async () => {
|
|
55
|
+
const store = {
|
|
56
|
+
load: vi.fn(),
|
|
57
|
+
save: vi.fn().mockResolvedValue(undefined),
|
|
58
|
+
};
|
|
59
|
+
const events = [
|
|
60
|
+
{ type: "text.delta", text: "hello" },
|
|
61
|
+
{ type: "text.done", text: "hello" },
|
|
62
|
+
doneEvent,
|
|
63
|
+
];
|
|
64
|
+
const runner = createMockRunner(events);
|
|
65
|
+
const persisted = withPersistence({ store, sessionId: "s1" })(runner);
|
|
66
|
+
const result = await collect(persisted([], "test"));
|
|
67
|
+
expect(result).toEqual(events);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=persistence.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.test.js","sourceRoot":"","sources":["../../../src/__tests__/middleware/persistence.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/middleware/retry.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { withRetry } from "../../middleware/retry.js";
|
|
3
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
4
|
+
async function collect(gen) {
|
|
5
|
+
const result = [];
|
|
6
|
+
for await (const event of gen)
|
|
7
|
+
result.push(event);
|
|
8
|
+
return result;
|
|
9
|
+
}
|
|
10
|
+
const doneEvent = {
|
|
11
|
+
type: "done",
|
|
12
|
+
result: "complete",
|
|
13
|
+
messages: [],
|
|
14
|
+
totalUsage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
|
|
15
|
+
};
|
|
16
|
+
// ── Tests ───────────────────────────────────────────────────────────
|
|
17
|
+
describe("withRetry", () => {
|
|
18
|
+
it("passes through events on success", async () => {
|
|
19
|
+
const runner = async function* () {
|
|
20
|
+
yield { type: "text.delta", text: "hello" };
|
|
21
|
+
yield doneEvent;
|
|
22
|
+
};
|
|
23
|
+
const retried = withRetry({ maxRetries: 3, initialDelayMs: 1 })(runner);
|
|
24
|
+
const events = await collect(retried([], "test"));
|
|
25
|
+
expect(events).toHaveLength(2);
|
|
26
|
+
expect(events[0].type).toBe("text.delta");
|
|
27
|
+
expect(events[1].type).toBe("done");
|
|
28
|
+
});
|
|
29
|
+
it("retries on retryable error before content", async () => {
|
|
30
|
+
let callCount = 0;
|
|
31
|
+
const runner = async function* () {
|
|
32
|
+
callCount++;
|
|
33
|
+
if (callCount === 1) {
|
|
34
|
+
yield { type: "error", error: new Error("429 rate limit") };
|
|
35
|
+
yield {
|
|
36
|
+
type: "done",
|
|
37
|
+
result: "error",
|
|
38
|
+
messages: [],
|
|
39
|
+
totalUsage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
40
|
+
};
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
yield { type: "text.delta", text: "success" };
|
|
44
|
+
yield doneEvent;
|
|
45
|
+
};
|
|
46
|
+
const retried = withRetry({ maxRetries: 3, initialDelayMs: 1 })(runner);
|
|
47
|
+
const events = await collect(retried([], "test"));
|
|
48
|
+
expect(callCount).toBe(2);
|
|
49
|
+
const retryEvents = events.filter((e) => e.type === "retry");
|
|
50
|
+
expect(retryEvents).toHaveLength(1);
|
|
51
|
+
expect(retryEvents[0].attempt).toBe(0);
|
|
52
|
+
});
|
|
53
|
+
it("does not retry after content has been yielded", async () => {
|
|
54
|
+
let callCount = 0;
|
|
55
|
+
const runner = async function* () {
|
|
56
|
+
callCount++;
|
|
57
|
+
yield { type: "text.delta", text: "content" };
|
|
58
|
+
yield { type: "error", error: new Error("429 rate limit") };
|
|
59
|
+
yield doneEvent;
|
|
60
|
+
};
|
|
61
|
+
const retried = withRetry({ maxRetries: 3, initialDelayMs: 1 })(runner);
|
|
62
|
+
const events = await collect(retried([], "test"));
|
|
63
|
+
expect(callCount).toBe(1);
|
|
64
|
+
expect(events.filter((e) => e.type === "retry")).toHaveLength(0);
|
|
65
|
+
// Error should pass through
|
|
66
|
+
expect(events.find((e) => e.type === "error")).toBeDefined();
|
|
67
|
+
});
|
|
68
|
+
it("does not retry non-retryable errors", async () => {
|
|
69
|
+
let callCount = 0;
|
|
70
|
+
const runner = async function* () {
|
|
71
|
+
callCount++;
|
|
72
|
+
yield { type: "error", error: new Error("invalid input") };
|
|
73
|
+
yield {
|
|
74
|
+
type: "done",
|
|
75
|
+
result: "error",
|
|
76
|
+
messages: [],
|
|
77
|
+
totalUsage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
const retried = withRetry({ maxRetries: 3, initialDelayMs: 1 })(runner);
|
|
81
|
+
const events = await collect(retried([], "test"));
|
|
82
|
+
expect(callCount).toBe(1);
|
|
83
|
+
expect(events.filter((e) => e.type === "retry")).toHaveLength(0);
|
|
84
|
+
});
|
|
85
|
+
it("retries on thrown retryable errors", async () => {
|
|
86
|
+
let callCount = 0;
|
|
87
|
+
const runner = async function* () {
|
|
88
|
+
callCount++;
|
|
89
|
+
if (callCount === 1) {
|
|
90
|
+
throw new Error("503 service unavailable");
|
|
91
|
+
}
|
|
92
|
+
yield doneEvent;
|
|
93
|
+
};
|
|
94
|
+
const retried = withRetry({ maxRetries: 3, initialDelayMs: 1 })(runner);
|
|
95
|
+
const events = await collect(retried([], "test"));
|
|
96
|
+
expect(callCount).toBe(2);
|
|
97
|
+
expect(events.filter((e) => e.type === "retry")).toHaveLength(1);
|
|
98
|
+
});
|
|
99
|
+
it("throws non-retryable thrown errors", async () => {
|
|
100
|
+
const runner = async function* () {
|
|
101
|
+
throw new Error("bug in code");
|
|
102
|
+
};
|
|
103
|
+
const retried = withRetry({ maxRetries: 3, initialDelayMs: 1 })(runner);
|
|
104
|
+
await expect(collect(retried([], "test"))).rejects.toThrow("bug in code");
|
|
105
|
+
});
|
|
106
|
+
it("gives up after maxRetries", async () => {
|
|
107
|
+
let callCount = 0;
|
|
108
|
+
const runner = async function* () {
|
|
109
|
+
callCount++;
|
|
110
|
+
yield { type: "error", error: new Error("429 rate limit") };
|
|
111
|
+
yield {
|
|
112
|
+
type: "done",
|
|
113
|
+
result: "error",
|
|
114
|
+
messages: [],
|
|
115
|
+
totalUsage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
const retried = withRetry({ maxRetries: 2, initialDelayMs: 1 })(runner);
|
|
119
|
+
const events = await collect(retried([], "test"));
|
|
120
|
+
// 1 initial + 2 retries = 3 calls
|
|
121
|
+
expect(callCount).toBe(3);
|
|
122
|
+
// Last attempt's error should pass through (not retried)
|
|
123
|
+
const errorEvents = events.filter((e) => e.type === "error");
|
|
124
|
+
expect(errorEvents).toHaveLength(1);
|
|
125
|
+
});
|
|
126
|
+
it("supports custom isRetryable", async () => {
|
|
127
|
+
let callCount = 0;
|
|
128
|
+
const runner = async function* () {
|
|
129
|
+
callCount++;
|
|
130
|
+
if (callCount === 1) {
|
|
131
|
+
yield { type: "error", error: new Error("custom-retryable") };
|
|
132
|
+
yield {
|
|
133
|
+
type: "done",
|
|
134
|
+
result: "error",
|
|
135
|
+
messages: [],
|
|
136
|
+
totalUsage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
137
|
+
};
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
yield doneEvent;
|
|
141
|
+
};
|
|
142
|
+
const retried = withRetry({
|
|
143
|
+
maxRetries: 3,
|
|
144
|
+
initialDelayMs: 1,
|
|
145
|
+
isRetryable: (e) => e.message.includes("custom-retryable"),
|
|
146
|
+
})(runner);
|
|
147
|
+
await collect(retried([], "test"));
|
|
148
|
+
expect(callCount).toBe(2);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
//# sourceMappingURL=retry.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.test.js","sourceRoot":"","sources":["../../../src/__tests__/middleware/retry.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-tracking.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/middleware/turn-tracking.test.ts"],"names":[],"mappings":""}
|