@funkai/agents 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/.generated/req.txt +1 -0
  2. package/.turbo/turbo-build.log +21 -0
  3. package/.turbo/turbo-test$colon$coverage.log +109 -0
  4. package/.turbo/turbo-test.log +141 -0
  5. package/.turbo/turbo-typecheck.log +4 -0
  6. package/CHANGELOG.md +16 -0
  7. package/ISSUES.md +540 -0
  8. package/LICENSE +21 -0
  9. package/README.md +128 -0
  10. package/banner.svg +97 -0
  11. package/coverage/lcov-report/base.css +224 -0
  12. package/coverage/lcov-report/block-navigation.js +87 -0
  13. package/coverage/lcov-report/core/agents/base/agent.ts.html +1705 -0
  14. package/coverage/lcov-report/core/agents/base/index.html +146 -0
  15. package/coverage/lcov-report/core/agents/base/output.ts.html +256 -0
  16. package/coverage/lcov-report/core/agents/base/utils.ts.html +694 -0
  17. package/coverage/lcov-report/core/agents/flow/engine.ts.html +928 -0
  18. package/coverage/lcov-report/core/agents/flow/flow-agent.ts.html +1462 -0
  19. package/coverage/lcov-report/core/agents/flow/index.html +146 -0
  20. package/coverage/lcov-report/core/agents/flow/messages.ts.html +508 -0
  21. package/coverage/lcov-report/core/agents/flow/steps/factory.ts.html +1975 -0
  22. package/coverage/lcov-report/core/agents/flow/steps/index.html +116 -0
  23. package/coverage/lcov-report/core/index.html +131 -0
  24. package/coverage/lcov-report/core/logger.ts.html +541 -0
  25. package/coverage/lcov-report/core/models/providers/index.html +116 -0
  26. package/coverage/lcov-report/core/models/providers/openai.ts.html +337 -0
  27. package/coverage/lcov-report/core/provider/index.html +131 -0
  28. package/coverage/lcov-report/core/provider/provider.ts.html +346 -0
  29. package/coverage/lcov-report/core/provider/usage.ts.html +376 -0
  30. package/coverage/lcov-report/core/tool.ts.html +577 -0
  31. package/coverage/lcov-report/favicon.png +0 -0
  32. package/coverage/lcov-report/index.html +221 -0
  33. package/coverage/lcov-report/lib/hooks.ts.html +262 -0
  34. package/coverage/lcov-report/lib/index.html +161 -0
  35. package/coverage/lcov-report/lib/middleware.ts.html +274 -0
  36. package/coverage/lcov-report/lib/runnable.ts.html +151 -0
  37. package/coverage/lcov-report/lib/trace.ts.html +520 -0
  38. package/coverage/lcov-report/prettify.css +1 -0
  39. package/coverage/lcov-report/prettify.js +2 -0
  40. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  41. package/coverage/lcov-report/sorter.js +210 -0
  42. package/coverage/lcov-report/utils/attempt.ts.html +199 -0
  43. package/coverage/lcov-report/utils/error.ts.html +421 -0
  44. package/coverage/lcov-report/utils/index.html +176 -0
  45. package/coverage/lcov-report/utils/resolve.ts.html +208 -0
  46. package/coverage/lcov-report/utils/result.ts.html +538 -0
  47. package/coverage/lcov-report/utils/zod.ts.html +178 -0
  48. package/coverage/lcov.info +1566 -0
  49. package/dist/index.d.mts +2883 -0
  50. package/dist/index.d.mts.map +1 -0
  51. package/dist/index.mjs +2312 -0
  52. package/dist/index.mjs.map +1 -0
  53. package/docs/core/agent.md +231 -0
  54. package/docs/core/hooks.md +95 -0
  55. package/docs/core/overview.md +87 -0
  56. package/docs/core/step.md +279 -0
  57. package/docs/core/tools.md +98 -0
  58. package/docs/core/workflow.md +235 -0
  59. package/docs/guides/create-agent.md +224 -0
  60. package/docs/guides/create-tool.md +137 -0
  61. package/docs/guides/create-workflow.md +374 -0
  62. package/docs/overview.md +244 -0
  63. package/docs/provider/models.md +55 -0
  64. package/docs/provider/overview.md +106 -0
  65. package/docs/provider/usage.md +100 -0
  66. package/docs/research/experimental-context.md +167 -0
  67. package/docs/research/gap-analysis.md +86 -0
  68. package/docs/research/prepare-step-and-active-tools.md +138 -0
  69. package/docs/research/sub-agent-model.md +249 -0
  70. package/docs/troubleshooting.md +60 -0
  71. package/logo.svg +17 -0
  72. package/models.config.json +18 -0
  73. package/package.json +60 -0
  74. package/scripts/generate-models.ts +324 -0
  75. package/src/core/agents/base/agent.test.ts +1522 -0
  76. package/src/core/agents/base/agent.ts +547 -0
  77. package/src/core/agents/base/output.test.ts +93 -0
  78. package/src/core/agents/base/output.ts +57 -0
  79. package/src/core/agents/base/types.test-d.ts +69 -0
  80. package/src/core/agents/base/types.ts +503 -0
  81. package/src/core/agents/base/utils.test.ts +397 -0
  82. package/src/core/agents/base/utils.ts +197 -0
  83. package/src/core/agents/flow/engine.test.ts +452 -0
  84. package/src/core/agents/flow/engine.ts +281 -0
  85. package/src/core/agents/flow/flow-agent.test.ts +1027 -0
  86. package/src/core/agents/flow/flow-agent.ts +473 -0
  87. package/src/core/agents/flow/messages.test.ts +198 -0
  88. package/src/core/agents/flow/messages.ts +141 -0
  89. package/src/core/agents/flow/steps/agent.test.ts +280 -0
  90. package/src/core/agents/flow/steps/agent.ts +87 -0
  91. package/src/core/agents/flow/steps/all.test.ts +300 -0
  92. package/src/core/agents/flow/steps/all.ts +73 -0
  93. package/src/core/agents/flow/steps/builder.ts +124 -0
  94. package/src/core/agents/flow/steps/each.test.ts +257 -0
  95. package/src/core/agents/flow/steps/each.ts +61 -0
  96. package/src/core/agents/flow/steps/factory.test-d.ts +50 -0
  97. package/src/core/agents/flow/steps/factory.test.ts +1025 -0
  98. package/src/core/agents/flow/steps/factory.ts +645 -0
  99. package/src/core/agents/flow/steps/map.test.ts +273 -0
  100. package/src/core/agents/flow/steps/map.ts +75 -0
  101. package/src/core/agents/flow/steps/race.test.ts +290 -0
  102. package/src/core/agents/flow/steps/race.ts +59 -0
  103. package/src/core/agents/flow/steps/reduce.test.ts +310 -0
  104. package/src/core/agents/flow/steps/reduce.ts +73 -0
  105. package/src/core/agents/flow/steps/result.ts +27 -0
  106. package/src/core/agents/flow/steps/step.test.ts +402 -0
  107. package/src/core/agents/flow/steps/step.ts +51 -0
  108. package/src/core/agents/flow/steps/while.test.ts +283 -0
  109. package/src/core/agents/flow/steps/while.ts +75 -0
  110. package/src/core/agents/flow/types.ts +348 -0
  111. package/src/core/logger.test.ts +163 -0
  112. package/src/core/logger.ts +152 -0
  113. package/src/core/models/index.test.ts +137 -0
  114. package/src/core/models/index.ts +152 -0
  115. package/src/core/models/providers/openai.ts +84 -0
  116. package/src/core/provider/provider.test.ts +128 -0
  117. package/src/core/provider/provider.ts +99 -0
  118. package/src/core/provider/types.ts +98 -0
  119. package/src/core/provider/usage.test.ts +304 -0
  120. package/src/core/provider/usage.ts +97 -0
  121. package/src/core/tool.test.ts +65 -0
  122. package/src/core/tool.ts +164 -0
  123. package/src/core/types.ts +66 -0
  124. package/src/index.ts +95 -0
  125. package/src/lib/context.test.ts +86 -0
  126. package/src/lib/context.ts +49 -0
  127. package/src/lib/hooks.test.ts +102 -0
  128. package/src/lib/hooks.ts +59 -0
  129. package/src/lib/middleware.test.ts +122 -0
  130. package/src/lib/middleware.ts +63 -0
  131. package/src/lib/runnable.test.ts +41 -0
  132. package/src/lib/runnable.ts +22 -0
  133. package/src/lib/trace.test.ts +291 -0
  134. package/src/lib/trace.ts +145 -0
  135. package/src/models/index.ts +123 -0
  136. package/src/models/providers/index.ts +15 -0
  137. package/src/models/providers/openai.ts +84 -0
  138. package/src/testing/context.ts +32 -0
  139. package/src/testing/index.ts +2 -0
  140. package/src/testing/logger.ts +19 -0
  141. package/src/utils/attempt.test.ts +127 -0
  142. package/src/utils/attempt.ts +38 -0
  143. package/src/utils/error.test.ts +179 -0
  144. package/src/utils/error.ts +112 -0
  145. package/src/utils/resolve.test.ts +38 -0
  146. package/src/utils/resolve.ts +41 -0
  147. package/src/utils/result.test.ts +79 -0
  148. package/src/utils/result.ts +151 -0
  149. package/src/utils/zod.test.ts +69 -0
  150. package/src/utils/zod.ts +31 -0
  151. package/tsconfig.json +25 -0
  152. package/tsdown.config.ts +15 -0
  153. package/vitest.config.ts +46 -0
@@ -0,0 +1,257 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+
3
+ import { createStepBuilder } from "@/core/agents/flow/steps/factory.js";
4
+ import { createMockCtx } from "@/testing/index.js";
5
+
6
+ describe("each()", () => {
7
+ it("iterates items sequentially", async () => {
8
+ const ctx = createMockCtx();
9
+ const $ = createStepBuilder({ ctx });
10
+ const order: number[] = [];
11
+
12
+ const result = await $.each({
13
+ id: "each-seq",
14
+ input: [1, 2, 3],
15
+ execute: async ({ item }) => {
16
+ order.push(item);
17
+ },
18
+ });
19
+
20
+ expect(result.ok).toBe(true);
21
+ expect(order).toEqual([1, 2, 3]);
22
+ expect(result.step.type).toBe("each");
23
+ });
24
+
25
+ it("returns ok: true with void value on success", async () => {
26
+ const ctx = createMockCtx();
27
+ const $ = createStepBuilder({ ctx });
28
+
29
+ const result = await $.each({
30
+ id: "each-void",
31
+ input: [1],
32
+ execute: async () => {},
33
+ });
34
+
35
+ expect(result.ok).toBe(true);
36
+ if (!result.ok) {
37
+ return;
38
+ }
39
+ expect(result.value).toBeUndefined();
40
+ });
41
+
42
+ it("propagates errors from execute", async () => {
43
+ const ctx = createMockCtx();
44
+ const $ = createStepBuilder({ ctx });
45
+
46
+ const result = await $.each({
47
+ id: "each-err",
48
+ input: [1, 2, 3],
49
+ execute: async ({ item }) => {
50
+ if (item === 2) {
51
+ throw new Error("stop at 2");
52
+ }
53
+ },
54
+ });
55
+
56
+ expect(result.ok).toBe(false);
57
+ if (result.ok) {
58
+ return;
59
+ }
60
+ expect(result.error.message).toBe("stop at 2");
61
+ expect(result.error.stepId).toBe("each-err");
62
+ });
63
+
64
+ it("stops iteration on error (does not process remaining items)", async () => {
65
+ const ctx = createMockCtx();
66
+ const $ = createStepBuilder({ ctx });
67
+ const processed: number[] = [];
68
+
69
+ await $.each({
70
+ id: "each-stop",
71
+ input: [1, 2, 3],
72
+ execute: async ({ item }) => {
73
+ if (item === 2) {
74
+ throw new Error("stop");
75
+ }
76
+ processed.push(item);
77
+ },
78
+ });
79
+
80
+ expect(processed).toEqual([1]);
81
+ });
82
+
83
+ it("handles empty input array", async () => {
84
+ const ctx = createMockCtx();
85
+ const $ = createStepBuilder({ ctx });
86
+ const executeSpy = vi.fn();
87
+
88
+ const result = await $.each({
89
+ id: "each-empty",
90
+ input: [],
91
+ execute: executeSpy,
92
+ });
93
+
94
+ expect(result.ok).toBe(true);
95
+ expect(executeSpy).not.toHaveBeenCalled();
96
+ });
97
+
98
+ it("handles single-item input", async () => {
99
+ const ctx = createMockCtx();
100
+ const $ = createStepBuilder({ ctx });
101
+ const processed: string[] = [];
102
+
103
+ const result = await $.each({
104
+ id: "each-single",
105
+ input: ["only"],
106
+ execute: async ({ item }) => {
107
+ processed.push(item);
108
+ },
109
+ });
110
+
111
+ expect(result.ok).toBe(true);
112
+ expect(processed).toEqual(["only"]);
113
+ });
114
+
115
+ it("passes index to execute callback", async () => {
116
+ const ctx = createMockCtx();
117
+ const $ = createStepBuilder({ ctx });
118
+ const indices: number[] = [];
119
+
120
+ await $.each({
121
+ id: "each-index",
122
+ input: ["a", "b", "c"],
123
+ execute: async ({ index }) => {
124
+ indices.push(index);
125
+ },
126
+ });
127
+
128
+ expect(indices).toEqual([0, 1, 2]);
129
+ });
130
+
131
+ it("respects abort signal", async () => {
132
+ const controller = new AbortController();
133
+ const ctx = createMockCtx({ signal: controller.signal });
134
+ const $ = createStepBuilder({ ctx });
135
+ const processed: number[] = [];
136
+
137
+ controller.abort();
138
+
139
+ const result = await $.each({
140
+ id: "each-aborted",
141
+ input: [1, 2, 3],
142
+ execute: async ({ item }) => {
143
+ processed.push(item);
144
+ },
145
+ });
146
+
147
+ expect(result.ok).toBe(false);
148
+ if (result.ok) {
149
+ return;
150
+ }
151
+ expect(result.error.message).toBe("Aborted");
152
+ });
153
+
154
+ it("fires onStart and onFinish hooks", async () => {
155
+ const order: string[] = [];
156
+ const ctx = createMockCtx();
157
+ const $ = createStepBuilder({ ctx });
158
+
159
+ await $.each({
160
+ id: "each-hooks",
161
+ input: [1, 2],
162
+ onStart: () => {
163
+ order.push("onStart");
164
+ },
165
+ execute: async ({ item }) => {
166
+ order.push(`execute:${item}`);
167
+ },
168
+ onFinish: () => {
169
+ order.push("onFinish");
170
+ },
171
+ });
172
+
173
+ expect(order).toEqual(["onStart", "execute:1", "execute:2", "onFinish"]);
174
+ });
175
+
176
+ it("fires onError hook on failure", async () => {
177
+ const onError = vi.fn();
178
+ const ctx = createMockCtx();
179
+ const $ = createStepBuilder({ ctx });
180
+
181
+ await $.each({
182
+ id: "each-onerror",
183
+ input: [1],
184
+ execute: async () => {
185
+ throw new Error("each failure");
186
+ },
187
+ onError,
188
+ });
189
+
190
+ expect(onError).toHaveBeenCalledTimes(1);
191
+ expect(onError).toHaveBeenCalledWith(
192
+ expect.objectContaining({
193
+ id: "each-onerror",
194
+ error: expect.any(Error),
195
+ }),
196
+ );
197
+ });
198
+
199
+ it("onFinish receives duration but no result", async () => {
200
+ const onFinish = vi.fn();
201
+ const ctx = createMockCtx();
202
+ const $ = createStepBuilder({ ctx });
203
+
204
+ await $.each({
205
+ id: "each-finish",
206
+ input: [1],
207
+ execute: async () => {},
208
+ onFinish,
209
+ });
210
+
211
+ expect(onFinish).toHaveBeenCalledTimes(1);
212
+ expect(onFinish).toHaveBeenCalledWith(
213
+ expect.objectContaining({
214
+ id: "each-finish",
215
+ duration: expect.any(Number),
216
+ }),
217
+ );
218
+ });
219
+
220
+ it("records trace entry", async () => {
221
+ const ctx = createMockCtx();
222
+ const $ = createStepBuilder({ ctx });
223
+
224
+ await $.each({
225
+ id: "each-trace",
226
+ input: [10, 20],
227
+ execute: async () => {},
228
+ });
229
+
230
+ const traceEntry = ctx.trace[0];
231
+ if (traceEntry === undefined) {
232
+ throw new Error("Expected trace entry");
233
+ }
234
+ expect(traceEntry.id).toBe("each-trace");
235
+ expect(traceEntry.type).toBe("each");
236
+ expect(traceEntry.input).toEqual([10, 20]);
237
+ });
238
+
239
+ it("provides child $ for nested operations", async () => {
240
+ const ctx = createMockCtx();
241
+ const $$ = createStepBuilder({ ctx });
242
+
243
+ await $$.each({
244
+ id: "each-nested",
245
+ input: [1],
246
+ execute: async ({ $ }) => {
247
+ await $.step({ id: "inner-step", execute: async () => "nested" });
248
+ },
249
+ });
250
+
251
+ const traceEntry = ctx.trace[0];
252
+ if (traceEntry === undefined) {
253
+ throw new Error("Expected trace entry");
254
+ }
255
+ expect(traceEntry.children).toHaveLength(1);
256
+ });
257
+ });
@@ -0,0 +1,61 @@
1
+ import type { StepBuilder } from "@/core/agents/flow/steps/builder.js";
2
+
3
+ /**
4
+ * Configuration for `$.each()` — sequential side effects.
5
+ *
6
+ * Runs items one at a time in order. Returns `void`. If you need
7
+ * accumulated results from sequential iteration, use `$.reduce()`.
8
+ *
9
+ * @typeParam T - Input item type.
10
+ */
11
+ export interface EachConfig<T> {
12
+ /**
13
+ * Unique step identifier.
14
+ *
15
+ * Appears in the execution trace.
16
+ */
17
+ id: string;
18
+
19
+ /**
20
+ * Array of items to process sequentially.
21
+ *
22
+ * Each item is passed to the `execute` callback in order.
23
+ */
24
+ input: readonly T[];
25
+
26
+ /**
27
+ * Process a single item (side effect).
28
+ *
29
+ * @param params - Execution parameters.
30
+ * @param params.item - The current item from the input array.
31
+ * @param params.index - The item's zero-based index in the input array.
32
+ * @param params.$ - The step builder for nesting further operations.
33
+ */
34
+ execute: (params: { item: T; index: number; $: StepBuilder }) => Promise<void>;
35
+
36
+ /**
37
+ * Hook: fires when the each operation starts.
38
+ *
39
+ * @param event - Event containing the step id.
40
+ * @param event.id - The step's unique identifier.
41
+ */
42
+ onStart?: (event: { id: string }) => void | Promise<void>;
43
+
44
+ /**
45
+ * Hook: fires when all items are processed.
46
+ *
47
+ * @param event - Event containing the step id and duration.
48
+ * @param event.id - The step's unique identifier.
49
+ * @param event.duration - Wall-clock time in milliseconds.
50
+ */
51
+ onFinish?: (event: { id: string; duration: number }) => void | Promise<void>;
52
+
53
+ /**
54
+ * Hook: fires if the each operation encounters an error.
55
+ *
56
+ * @param event - Event containing the step id and error.
57
+ * @param event.id - The step's unique identifier.
58
+ * @param event.error - The error that occurred.
59
+ */
60
+ onError?: (event: { id: string; error: Error }) => void | Promise<void>;
61
+ }
@@ -0,0 +1,50 @@
1
+ import { describe, expectTypeOf, it } from "vitest";
2
+
3
+ import type { StepBuilder } from "@/core/agents/flow/steps/builder.js";
4
+ import { createStepBuilder } from "@/core/agents/flow/steps/factory.js";
5
+ import type { StepResult, StepError } from "@/core/agents/flow/steps/result.js";
6
+ import type { ResultError } from "@/utils/result.js";
7
+
8
+ describe("StepError extends ResultError", () => {
9
+ it("is assignable to ResultError", () => {
10
+ expectTypeOf<StepError>().toExtend<ResultError>();
11
+ });
12
+
13
+ it("has stepId field", () => {
14
+ expectTypeOf<StepError["stepId"]>().toBeString();
15
+ });
16
+ });
17
+
18
+ describe("StepResult<T>", () => {
19
+ it("success branch has ok: true", () => {
20
+ type Success = Extract<StepResult<{ value: number }>, { ok: true }>;
21
+ expectTypeOf<Success["ok"]>().toEqualTypeOf<true>();
22
+ });
23
+
24
+ it("success branch has value: T field", () => {
25
+ type Success = Extract<StepResult<{ value: number }>, { ok: true }>;
26
+ expectTypeOf<Success["value"]>().toEqualTypeOf<{ value: number }>();
27
+ });
28
+
29
+ it("success branch has step and duration", () => {
30
+ type Success = Extract<StepResult<{ value: number }>, { ok: true }>;
31
+ expectTypeOf<Success["step"]>().toHaveProperty("id");
32
+ expectTypeOf<Success["duration"]>().toBeNumber();
33
+ });
34
+
35
+ it("failure branch has ok: false", () => {
36
+ type Failure = Extract<StepResult<{ value: number }>, { ok: false }>;
37
+ expectTypeOf<Failure["ok"]>().toEqualTypeOf<false>();
38
+ });
39
+
40
+ it("failure branch has StepError", () => {
41
+ type Failure = Extract<StepResult<{ value: number }>, { ok: false }>;
42
+ expectTypeOf<Failure["error"]>().toExtend<StepError>();
43
+ });
44
+ });
45
+
46
+ describe("createStepBuilder", () => {
47
+ it("returns StepBuilder", () => {
48
+ expectTypeOf(createStepBuilder).returns.toExtend<StepBuilder>();
49
+ });
50
+ });