@openharness/core 0.4.0 → 0.4.2

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 (100) hide show
  1. package/README.md +116 -0
  2. package/dist/__tests__/agent-registry.test.d.ts +2 -0
  3. package/dist/__tests__/agent-registry.test.d.ts.map +1 -0
  4. package/dist/__tests__/agent-registry.test.js +429 -0
  5. package/dist/__tests__/agent-registry.test.js.map +1 -0
  6. package/dist/__tests__/conversation.test.d.ts +2 -0
  7. package/dist/__tests__/conversation.test.d.ts.map +1 -0
  8. package/dist/__tests__/conversation.test.js +127 -0
  9. package/dist/__tests__/conversation.test.js.map +1 -0
  10. package/dist/__tests__/messages.test.js +15 -2
  11. package/dist/__tests__/messages.test.js.map +1 -1
  12. package/dist/__tests__/middleware/compaction.test.d.ts +2 -0
  13. package/dist/__tests__/middleware/compaction.test.d.ts.map +1 -0
  14. package/dist/__tests__/middleware/compaction.test.js +163 -0
  15. package/dist/__tests__/middleware/compaction.test.js.map +1 -0
  16. package/dist/__tests__/middleware/hooks.test.d.ts +2 -0
  17. package/dist/__tests__/middleware/hooks.test.d.ts.map +1 -0
  18. package/dist/__tests__/middleware/hooks.test.js +97 -0
  19. package/dist/__tests__/middleware/hooks.test.js.map +1 -0
  20. package/dist/__tests__/middleware/persistence.test.d.ts +2 -0
  21. package/dist/__tests__/middleware/persistence.test.d.ts.map +1 -0
  22. package/dist/__tests__/middleware/persistence.test.js +70 -0
  23. package/dist/__tests__/middleware/persistence.test.js.map +1 -0
  24. package/dist/__tests__/middleware/retry.test.d.ts +2 -0
  25. package/dist/__tests__/middleware/retry.test.d.ts.map +1 -0
  26. package/dist/__tests__/middleware/retry.test.js +151 -0
  27. package/dist/__tests__/middleware/retry.test.js.map +1 -0
  28. package/dist/__tests__/middleware/turn-tracking.test.d.ts +2 -0
  29. package/dist/__tests__/middleware/turn-tracking.test.d.ts.map +1 -0
  30. package/dist/__tests__/middleware/turn-tracking.test.js +70 -0
  31. package/dist/__tests__/middleware/turn-tracking.test.js.map +1 -0
  32. package/dist/__tests__/runner.test.d.ts +2 -0
  33. package/dist/__tests__/runner.test.d.ts.map +1 -0
  34. package/dist/__tests__/runner.test.js +111 -0
  35. package/dist/__tests__/runner.test.js.map +1 -0
  36. package/dist/__tests__/stream.test.d.ts +2 -0
  37. package/dist/__tests__/stream.test.d.ts.map +1 -0
  38. package/dist/__tests__/stream.test.js +100 -0
  39. package/dist/__tests__/stream.test.js.map +1 -0
  40. package/dist/__tests__/ui-stream.test.js +105 -0
  41. package/dist/__tests__/ui-stream.test.js.map +1 -1
  42. package/dist/agent-registry.d.ts +90 -0
  43. package/dist/agent-registry.d.ts.map +1 -0
  44. package/dist/agent-registry.js +217 -0
  45. package/dist/agent-registry.js.map +1 -0
  46. package/dist/agent.d.ts +13 -0
  47. package/dist/agent.d.ts.map +1 -1
  48. package/dist/agent.js +186 -41
  49. package/dist/agent.js.map +1 -1
  50. package/dist/conversation.d.ts +46 -0
  51. package/dist/conversation.d.ts.map +1 -0
  52. package/dist/conversation.js +66 -0
  53. package/dist/conversation.js.map +1 -0
  54. package/dist/index.d.ts +9 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +14 -0
  57. package/dist/index.js.map +1 -1
  58. package/dist/middleware/compaction.d.ts +20 -0
  59. package/dist/middleware/compaction.d.ts.map +1 -0
  60. package/dist/middleware/compaction.js +73 -0
  61. package/dist/middleware/compaction.js.map +1 -0
  62. package/dist/middleware/hooks.d.ts +10 -0
  63. package/dist/middleware/hooks.d.ts.map +1 -0
  64. package/dist/middleware/hooks.js +36 -0
  65. package/dist/middleware/hooks.js.map +1 -0
  66. package/dist/middleware/index.d.ts +6 -0
  67. package/dist/middleware/index.d.ts.map +1 -0
  68. package/dist/middleware/index.js +6 -0
  69. package/dist/middleware/index.js.map +1 -0
  70. package/dist/middleware/persistence.d.ts +12 -0
  71. package/dist/middleware/persistence.d.ts.map +1 -0
  72. package/dist/middleware/persistence.js +15 -0
  73. package/dist/middleware/persistence.js.map +1 -0
  74. package/dist/middleware/retry.d.ts +8 -0
  75. package/dist/middleware/retry.d.ts.map +1 -0
  76. package/dist/middleware/retry.js +70 -0
  77. package/dist/middleware/retry.js.map +1 -0
  78. package/dist/middleware/turn-tracking.d.ts +7 -0
  79. package/dist/middleware/turn-tracking.d.ts.map +1 -0
  80. package/dist/middleware/turn-tracking.js +25 -0
  81. package/dist/middleware/turn-tracking.js.map +1 -0
  82. package/dist/runner.d.ts +23 -0
  83. package/dist/runner.d.ts.map +1 -0
  84. package/dist/runner.js +18 -0
  85. package/dist/runner.js.map +1 -0
  86. package/dist/session.d.ts.map +1 -1
  87. package/dist/session.js +7 -40
  88. package/dist/session.js.map +1 -1
  89. package/dist/stream.d.ts +12 -0
  90. package/dist/stream.d.ts.map +1 -0
  91. package/dist/stream.js +38 -0
  92. package/dist/stream.js.map +1 -0
  93. package/dist/ui-stream.d.ts.map +1 -1
  94. package/dist/ui-stream.js +13 -1
  95. package/dist/ui-stream.js.map +1 -1
  96. package/dist/utils.d.ts +8 -0
  97. package/dist/utils.d.ts.map +1 -0
  98. package/dist/utils.js +40 -0
  99. package/dist/utils.js.map +1 -0
  100. package/package.json +13 -2
@@ -0,0 +1,127 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { Conversation } from "../conversation.js";
3
+ // ── Helpers ─────────────────────────────────────────────────────────
4
+ const makeMessages = () => [
5
+ { role: "user", content: "hi" },
6
+ { role: "assistant", content: "hello" },
7
+ ];
8
+ function createMockRunner(events) {
9
+ return async function* () {
10
+ for (const event of events)
11
+ yield event;
12
+ };
13
+ }
14
+ async function collectEvents(gen) {
15
+ const result = [];
16
+ for await (const event of gen)
17
+ result.push(event);
18
+ return result;
19
+ }
20
+ const doneEvent = {
21
+ type: "done",
22
+ result: "complete",
23
+ messages: makeMessages(),
24
+ totalUsage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
25
+ };
26
+ // ── Tests ───────────────────────────────────────────────────────────
27
+ describe("Conversation", () => {
28
+ it("updates messages from done events", async () => {
29
+ const runner = createMockRunner([
30
+ { type: "text.delta", text: "hello" },
31
+ doneEvent,
32
+ ]);
33
+ const conv = new Conversation({ runner });
34
+ expect(conv.messages).toEqual([]);
35
+ await collectEvents(conv.send("hi"));
36
+ expect(conv.messages).toEqual(makeMessages());
37
+ });
38
+ it("yields all events from the runner", async () => {
39
+ const events = [
40
+ { type: "text.delta", text: "hello" },
41
+ { type: "text.done", text: "hello" },
42
+ doneEvent,
43
+ ];
44
+ const runner = createMockRunner(events);
45
+ const conv = new Conversation({ runner });
46
+ const collected = await collectEvents(conv.send("hi"));
47
+ expect(collected).toEqual(events);
48
+ });
49
+ it("passes messages as history to subsequent calls", async () => {
50
+ const histories = [];
51
+ const runner = async function* (history, input) {
52
+ histories.push([...history]);
53
+ yield {
54
+ type: "done",
55
+ result: "complete",
56
+ messages: [...history, { role: "user", content: input }, { role: "assistant", content: "ok" }],
57
+ totalUsage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
58
+ };
59
+ };
60
+ const conv = new Conversation({ runner });
61
+ await collectEvents(conv.send("first"));
62
+ await collectEvents(conv.send("second"));
63
+ expect(histories[0]).toEqual([]);
64
+ expect(histories[1]).toHaveLength(2);
65
+ });
66
+ describe("load/save", () => {
67
+ it("load returns false without store", async () => {
68
+ const conv = new Conversation({ runner: createMockRunner([]) });
69
+ expect(await conv.load()).toBe(false);
70
+ });
71
+ it("save is a no-op without store", async () => {
72
+ const conv = new Conversation({ runner: createMockRunner([]) });
73
+ await conv.save(); // should not throw
74
+ });
75
+ it("load populates messages from store", async () => {
76
+ const store = {
77
+ load: vi.fn().mockResolvedValue(makeMessages()),
78
+ save: vi.fn(),
79
+ };
80
+ const conv = new Conversation({
81
+ runner: createMockRunner([]),
82
+ store,
83
+ sessionId: "test-session",
84
+ });
85
+ const loaded = await conv.load();
86
+ expect(loaded).toBe(true);
87
+ expect(conv.messages).toEqual(makeMessages());
88
+ expect(store.load).toHaveBeenCalledWith("test-session");
89
+ });
90
+ it("load returns false when store returns undefined", async () => {
91
+ const store = {
92
+ load: vi.fn().mockResolvedValue(undefined),
93
+ save: vi.fn(),
94
+ };
95
+ const conv = new Conversation({ runner: createMockRunner([]), store });
96
+ expect(await conv.load()).toBe(false);
97
+ expect(conv.messages).toEqual([]);
98
+ });
99
+ it("save persists messages to store", async () => {
100
+ const store = {
101
+ load: vi.fn(),
102
+ save: vi.fn(),
103
+ };
104
+ const conv = new Conversation({
105
+ runner: createMockRunner([doneEvent]),
106
+ store,
107
+ sessionId: "test-session",
108
+ });
109
+ await collectEvents(conv.send("hi"));
110
+ await conv.save();
111
+ expect(store.save).toHaveBeenCalledWith("test-session", makeMessages());
112
+ });
113
+ });
114
+ it("generates a sessionId when not provided", () => {
115
+ const conv = new Conversation({ runner: createMockRunner([]) });
116
+ expect(conv.sessionId).toBeTruthy();
117
+ expect(typeof conv.sessionId).toBe("string");
118
+ });
119
+ it("uses provided sessionId", () => {
120
+ const conv = new Conversation({
121
+ runner: createMockRunner([]),
122
+ sessionId: "my-session",
123
+ });
124
+ expect(conv.sessionId).toBe("my-session");
125
+ });
126
+ });
127
+ //# sourceMappingURL=conversation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation.test.js","sourceRoot":"","sources":["../../src/__tests__/conversation.test.ts"],"names":[],"mappings":"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"}
@@ -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 message with file", async () => {
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.length).toBeGreaterThan(0);
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":"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"}
1
+ {"version":3,"file":"messages.test.js","sourceRoot":"","sources":["../../src/__tests__/messages.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=compaction.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hooks.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=persistence.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=retry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/middleware/retry.test.ts"],"names":[],"mappings":""}