clanka 0.0.28 → 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 (73) hide show
  1. package/dist/Agent.d.ts +87 -40
  2. package/dist/Agent.d.ts.map +1 -1
  3. package/dist/Agent.js +154 -94
  4. package/dist/Agent.js.map +1 -1
  5. package/dist/AgentExecutor.d.ts +165 -0
  6. package/dist/AgentExecutor.d.ts.map +1 -0
  7. package/dist/AgentExecutor.js +121 -0
  8. package/dist/AgentExecutor.js.map +1 -0
  9. package/dist/AgentTools.d.ts +14 -132
  10. package/dist/AgentTools.d.ts.map +1 -1
  11. package/dist/AgentTools.js +10 -12
  12. package/dist/AgentTools.js.map +1 -1
  13. package/dist/OutputFormatter.d.ts.map +1 -1
  14. package/dist/OutputFormatter.js +18 -0
  15. package/dist/OutputFormatter.js.map +1 -1
  16. package/dist/WebToMarkdown.d.ts.map +1 -1
  17. package/dist/WebToMarkdown.js +1 -1
  18. package/dist/WebToMarkdown.js.map +1 -1
  19. package/dist/WebToMarkdown.test.d.ts +2 -0
  20. package/dist/WebToMarkdown.test.d.ts.map +1 -0
  21. package/dist/WebToMarkdown.test.js +37 -0
  22. package/dist/WebToMarkdown.test.js.map +1 -0
  23. package/dist/index.d.ts +4 -4
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +4 -4
  26. package/dist/index.js.map +1 -1
  27. package/package.json +3 -3
  28. package/src/Agent.ts +290 -215
  29. package/src/AgentExecutor.ts +245 -0
  30. package/src/AgentTools.ts +25 -22
  31. package/src/OutputFormatter.ts +18 -0
  32. package/src/WebToMarkdown.test.ts +65 -0
  33. package/src/WebToMarkdown.ts +1 -0
  34. package/src/index.ts +4 -4
  35. package/dist/Agent.test.d.ts +0 -2
  36. package/dist/Agent.test.d.ts.map +0 -1
  37. package/dist/Agent.test.js +0 -111
  38. package/dist/Agent.test.js.map +0 -1
  39. package/dist/AgentTools.test.d.ts +0 -2
  40. package/dist/AgentTools.test.d.ts.map +0 -1
  41. package/dist/AgentTools.test.js +0 -714
  42. package/dist/AgentTools.test.js.map +0 -1
  43. package/dist/DuckDuckGo.d.ts +0 -28
  44. package/dist/DuckDuckGo.d.ts.map +0 -1
  45. package/dist/DuckDuckGo.js +0 -131
  46. package/dist/DuckDuckGo.js.map +0 -1
  47. package/dist/Executor.d.ts +0 -20
  48. package/dist/Executor.d.ts.map +0 -1
  49. package/dist/Executor.js +0 -98
  50. package/dist/Executor.js.map +0 -1
  51. package/dist/GithubCopilot.d.ts +0 -11
  52. package/dist/GithubCopilot.d.ts.map +0 -1
  53. package/dist/GithubCopilot.js +0 -14
  54. package/dist/GithubCopilot.js.map +0 -1
  55. package/dist/GithubCopilotAuth.d.ts +0 -57
  56. package/dist/GithubCopilotAuth.d.ts.map +0 -1
  57. package/dist/GithubCopilotAuth.js +0 -218
  58. package/dist/GithubCopilotAuth.js.map +0 -1
  59. package/dist/GithubCopilotAuth.test.d.ts +0 -2
  60. package/dist/GithubCopilotAuth.test.d.ts.map +0 -1
  61. package/dist/GithubCopilotAuth.test.js +0 -267
  62. package/dist/GithubCopilotAuth.test.js.map +0 -1
  63. package/dist/MockSearch.d.ts +0 -12
  64. package/dist/MockSearch.d.ts.map +0 -1
  65. package/dist/MockSearch.js +0 -4
  66. package/dist/MockSearch.js.map +0 -1
  67. package/dist/ai.d.ts +0 -2
  68. package/dist/ai.d.ts.map +0 -1
  69. package/dist/ai.js +0 -29
  70. package/dist/ai.js.map +0 -1
  71. package/dist/examples/cli.mjs +0 -68514
  72. package/src/Agent.test.ts +0 -159
  73. package/src/Executor.ts +0 -151
@@ -0,0 +1,245 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+ import {
5
+ Cause,
6
+ Console,
7
+ Effect,
8
+ Exit,
9
+ Fiber,
10
+ FileSystem,
11
+ Layer,
12
+ Option,
13
+ Path,
14
+ pipe,
15
+ Queue,
16
+ Scope,
17
+ ServiceMap,
18
+ Stream,
19
+ } from "effect"
20
+ import { Tool, Toolkit } from "effect/unstable/ai"
21
+ import * as NodeConsole from "node:console"
22
+ import * as NodeVm from "node:vm"
23
+ import { Writable } from "node:stream"
24
+ import {
25
+ AgentToolHandlers,
26
+ AgentTools,
27
+ CurrentDirectory,
28
+ SubagentExecutor,
29
+ TaskCompleter,
30
+ } from "./AgentTools.ts"
31
+ import { ToolkitRenderer } from "./ToolkitRenderer.ts"
32
+ import type { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
33
+ import type { HttpClient } from "effect/unstable/http/HttpClient"
34
+
35
+ /**
36
+ * @since 1.0.0
37
+ * @category Services
38
+ */
39
+ export class AgentExecutor extends ServiceMap.Service<
40
+ AgentExecutor,
41
+ {
42
+ readonly toolsDts: Effect.Effect<string>
43
+ readonly agentsMd: Effect.Effect<Option.Option<string>>
44
+ execute(options: {
45
+ readonly script: string
46
+ readonly onTaskComplete: (summary: string) => Effect.Effect<void>
47
+ readonly onSubagent: (message: string) => Effect.Effect<string>
48
+ }): Stream.Stream<string>
49
+ }
50
+ >()("clanka/AgentExecutor") {}
51
+
52
+ /**
53
+ * @since 1.0.0
54
+ * @category Constructors
55
+ */
56
+ export const makeLocal = Effect.fnUntraced(function* <
57
+ Toolkit extends Toolkit.Any = never,
58
+ >(options: {
59
+ readonly directory: string
60
+ readonly tools?: Toolkit | undefined
61
+ }): Effect.fn.Return<
62
+ AgentExecutor["Service"],
63
+ never,
64
+ | ToolkitRenderer
65
+ | FileSystem.FileSystem
66
+ | Path.Path
67
+ | Tool.HandlersFor<typeof AgentTools.tools>
68
+ | Exclude<
69
+ Toolkit extends Toolkit.Toolkit<infer T>
70
+ ? Tool.HandlersFor<T> | Tool.HandlerServices<T[keyof T]>
71
+ : never,
72
+ CurrentDirectory | SubagentExecutor | TaskCompleter
73
+ >
74
+ > {
75
+ const fs = yield* FileSystem.FileSystem
76
+ const pathService = yield* Path.Path
77
+ const renderer = yield* ToolkitRenderer
78
+ const AllTools = Toolkit.merge(
79
+ AgentTools,
80
+ (options.tools as unknown as Toolkit.Toolkit<{}>) ?? Toolkit.empty,
81
+ )
82
+ const tools = yield* AllTools
83
+ const toolsDts = Effect.succeed(renderer.render(AllTools))
84
+
85
+ const services = yield* Effect.services()
86
+
87
+ const toolEntries = Object.entries(tools.tools).map(([name, tool]) => {
88
+ const handler = services.mapUnsafe.get(tool.id) as Tool.Handler<string>
89
+ return {
90
+ name,
91
+ services: ServiceMap.merge(services, handler.services),
92
+ handler: handler.handler,
93
+ }
94
+ })
95
+
96
+ const execute = Effect.fnUntraced(function* (opts: {
97
+ readonly script: string
98
+ readonly onTaskComplete: (summary: string) => Effect.Effect<void>
99
+ readonly onSubagent: (message: string) => Effect.Effect<string>
100
+ }) {
101
+ const output = yield* Queue.unbounded<string, Cause.Done>()
102
+ const console = yield* makeConsole(output)
103
+ const handlerScope = Scope.makeUnsafe("parallel")
104
+ const trackFiber = Fiber.runIn(handlerScope)
105
+
106
+ const taskServices = ServiceMap.make(
107
+ TaskCompleter,
108
+ opts.onTaskComplete,
109
+ ).pipe(
110
+ ServiceMap.add(CurrentDirectory, options.directory),
111
+ ServiceMap.add(SubagentExecutor, opts.onSubagent),
112
+ ServiceMap.add(Console.Console, console),
113
+ )
114
+
115
+ yield* Effect.gen(function* () {
116
+ const console = yield* Console.Console
117
+ let running = 0
118
+
119
+ const vmScript = new NodeVm.Script(`async function main() {
120
+ ${opts.script}
121
+ }`)
122
+ const sandbox: ScriptSandbox = {
123
+ main: defaultMain,
124
+ console,
125
+ fetch,
126
+ process: undefined,
127
+ }
128
+
129
+ for (let i = 0; i < toolEntries.length; i++) {
130
+ const { name, handler, services } = toolEntries[i]!
131
+ const runFork = Effect.runForkWith(
132
+ ServiceMap.merge(services, taskServices),
133
+ )
134
+
135
+ // oxlint-disable-next-line typescript/no-explicit-any
136
+ sandbox[name] = function (params: any) {
137
+ running++
138
+ const fiber = trackFiber(runFork(handler(params, {})))
139
+ return new Promise((resolve, reject) => {
140
+ fiber.addObserver((exit) => {
141
+ running--
142
+ if (exit._tag === "Success") {
143
+ return resolve(exit.value)
144
+ }
145
+ if (Cause.hasInterruptsOnly(exit.cause)) return
146
+ reject(Cause.squash(exit.cause))
147
+ })
148
+ })
149
+ }
150
+ }
151
+
152
+ vmScript.runInNewContext(sandbox, {
153
+ timeout: 1000,
154
+ })
155
+ yield* Effect.promise(sandbox.main)
156
+ while (true) {
157
+ yield* Effect.yieldNow
158
+ if (running === 0) break
159
+ }
160
+ }).pipe(
161
+ Effect.ensuring(Scope.close(handlerScope, Exit.void)),
162
+ Effect.catchCause(Effect.logFatal),
163
+ Effect.provideService(Console.Console, console),
164
+ Effect.ensuring(Queue.end(output)),
165
+ Effect.forkScoped,
166
+ )
167
+
168
+ return Stream.fromQueue(output)
169
+ }, Stream.unwrap)
170
+
171
+ return AgentExecutor.of({
172
+ toolsDts,
173
+ agentsMd: pipe(
174
+ fs.readFileString(pathService.join(options.directory, "AGENTS.md")),
175
+ Effect.option,
176
+ ),
177
+ execute,
178
+ })
179
+ })
180
+
181
+ /**
182
+ * @since 1.0.0
183
+ * @category Layers
184
+ */
185
+ export const layerLocal = <Toolkit extends Toolkit.Any = never>(options: {
186
+ readonly directory: string
187
+ readonly tools?: Toolkit | undefined
188
+ }): Layer.Layer<
189
+ AgentExecutor,
190
+ never,
191
+ | FileSystem.FileSystem
192
+ | Path.Path
193
+ | ChildProcessSpawner
194
+ | HttpClient
195
+ | Exclude<
196
+ Toolkit extends Toolkit.Toolkit<infer T>
197
+ ? Tool.HandlersFor<T> | Tool.HandlerServices<T[keyof T]>
198
+ : never,
199
+ CurrentDirectory | SubagentExecutor | TaskCompleter
200
+ >
201
+ > =>
202
+ Layer.effect(AgentExecutor, makeLocal(options)).pipe(
203
+ Layer.provide([AgentToolHandlers, ToolkitRenderer.layer]),
204
+ )
205
+
206
+ // ------------------------------------------
207
+ // Internal
208
+ // -------------------------------------------
209
+
210
+ interface ScriptSandbox {
211
+ main: () => Promise<void>
212
+ console: Console.Console
213
+ [toolName: string]: unknown
214
+ }
215
+
216
+ const defaultMain = () => Promise.resolve()
217
+
218
+ const makeConsole = Effect.fn(function* (
219
+ queue: Queue.Queue<string, Cause.Done>,
220
+ ) {
221
+ const writable = new QueueWriteStream(queue)
222
+ const newConsole = new NodeConsole.Console(writable)
223
+ yield* Effect.addFinalizer(() => {
224
+ writable.end()
225
+ return Effect.void
226
+ })
227
+ return newConsole
228
+ })
229
+
230
+ class QueueWriteStream extends Writable {
231
+ readonly queue: Queue.Enqueue<string, Cause.Done>
232
+ constructor(queue: Queue.Enqueue<string, Cause.Done>) {
233
+ super()
234
+ this.queue = queue
235
+ }
236
+ _write(
237
+ // oxlint-disable-next-line typescript/no-explicit-any
238
+ chunk: any,
239
+ _encoding: BufferEncoding,
240
+ callback: (error?: Error | null) => void,
241
+ ): void {
242
+ Queue.offerUnsafe(this.queue, chunk.toString())
243
+ callback()
244
+ }
245
+ }
package/src/AgentTools.ts CHANGED
@@ -4,7 +4,6 @@
4
4
  import {
5
5
  Array,
6
6
  Data,
7
- Deferred,
8
7
  Effect,
9
8
  FileSystem,
10
9
  Layer,
@@ -20,6 +19,7 @@ import * as Glob from "glob"
20
19
  import { parsePatch, patchChunks } from "./ApplyPatch.ts"
21
20
  import * as ExaSearch from "./ExaSearch.ts"
22
21
  import * as WebToMarkdown from "./WebToMarkdown.ts"
22
+ import type { HttpClient } from "effect/unstable/http/HttpClient"
23
23
 
24
24
  /**
25
25
  * @since 1.0.0
@@ -34,32 +34,28 @@ export class CurrentDirectory extends ServiceMap.Service<
34
34
  * @since 1.0.0
35
35
  * @category Context
36
36
  */
37
- export class TaskCompleteDeferred extends ServiceMap.Service<
38
- TaskCompleteDeferred,
39
- Deferred.Deferred<string>
40
- >()("clanka/AgentTools/TaskCompleteDeferred") {}
37
+ export class TaskCompleter extends ServiceMap.Service<
38
+ TaskCompleter,
39
+ (output: string) => Effect.Effect<void>
40
+ >()("clanka/AgentTools/TaskCompleter") {}
41
41
 
42
42
  /**
43
43
  * @since 1.0.0
44
44
  * @category Context
45
45
  */
46
- export class SubagentContext extends ServiceMap.Service<
47
- SubagentContext,
48
- {
49
- spawn(options: { readonly prompt: string }): Effect.Effect<string>
50
- }
51
- >()("clanka/AgentTools/SubagentContext") {}
46
+ export class SubagentExecutor extends ServiceMap.Service<
47
+ SubagentExecutor,
48
+ (prompt: string) => Effect.Effect<string>
49
+ >()("clanka/AgentTools/SubagentExecutor") {}
52
50
 
53
51
  /**
54
52
  * @since 1.0.0
55
53
  * @category Context
56
54
  */
57
55
  export const makeContextNoop = (cwd?: string) =>
58
- SubagentContext.serviceMap({
59
- spawn: () => Effect.die("Not implemented"),
60
- }).pipe(
56
+ SubagentExecutor.serviceMap(() => Effect.die("Not implemented")).pipe(
61
57
  ServiceMap.add(CurrentDirectory, cwd ?? "/"),
62
- ServiceMap.add(TaskCompleteDeferred, Deferred.makeUnsafe()),
58
+ ServiceMap.add(TaskCompleter, () => Effect.void),
63
59
  )
64
60
 
65
61
  /**
@@ -176,7 +172,7 @@ export const AgentTools = Toolkit.make(
176
172
  identifier: "task",
177
173
  }),
178
174
  success: Schema.String,
179
- dependencies: [SubagentContext],
175
+ dependencies: [SubagentExecutor],
180
176
  }),
181
177
  Tool.make("webSearch", {
182
178
  description: "Search the web for recent information.",
@@ -202,7 +198,7 @@ export const AgentTools = Toolkit.make(
202
198
  parameters: Schema.String.annotate({
203
199
  identifier: "output",
204
200
  }),
205
- dependencies: [TaskCompleteDeferred],
201
+ dependencies: [TaskCompleter],
206
202
  }),
207
203
  )
208
204
 
@@ -525,12 +521,12 @@ export const AgentToolHandlersNoDeps = AgentTools.toLayer(
525
521
  }, Effect.orDie),
526
522
  delegate: Effect.fn("AgentTools.delegate")(function* (prompt) {
527
523
  yield* Effect.logInfo(`Calling "delegate"`)
528
- const context = yield* SubagentContext
529
- return yield* context.spawn({ prompt })
524
+ const spawn = yield* SubagentExecutor
525
+ return yield* spawn(prompt)
530
526
  }, Effect.orDie),
531
527
  taskComplete: Effect.fn("AgentTools.taskComplete")(function* (message) {
532
- const deferred = yield* TaskCompleteDeferred
533
- yield* Deferred.succeed(deferred, message)
528
+ const deferred = yield* TaskCompleter
529
+ yield* deferred(message)
534
530
  }),
535
531
  })
536
532
  }),
@@ -540,7 +536,14 @@ export const AgentToolHandlersNoDeps = AgentTools.toLayer(
540
536
  * @since 1.0.0
541
537
  * @category Layers
542
538
  */
543
- export const AgentToolHandlers = AgentToolHandlersNoDeps.pipe(
539
+ export const AgentToolHandlers: Layer.Layer<
540
+ Tool.HandlersFor<typeof AgentTools.tools>,
541
+ never,
542
+ | FileSystem.FileSystem
543
+ | Path.Path
544
+ | ChildProcessSpawner.ChildProcessSpawner
545
+ | HttpClient
546
+ > = AgentToolHandlersNoDeps.pipe(
544
547
  Layer.provide([ExaSearch.layer, WebToMarkdown.layer]),
545
548
  )
546
549
 
@@ -4,6 +4,7 @@
4
4
  import { Effect, Layer, PubSub, Semaphore, ServiceMap, Stream } from "effect"
5
5
  import { type Output, AgentFinished } from "./Agent.ts"
6
6
  import chalk from "chalk"
7
+ import type { Prompt } from "effect/unstable/ai"
7
8
 
8
9
  /**
9
10
  * @since 1.0.0
@@ -26,6 +27,9 @@ export const pretty: OutputFormatter = (stream) =>
26
27
  output = output.part
27
28
  }
28
29
  switch (output._tag) {
30
+ case "AgentStart": {
31
+ return `${chalkAgentHeading(`${subagentIcon} Agent #${output.id} starting (${output.modelAndProvider})`)}\n\n${promptToString(output.prompt)}\n\n`
32
+ }
29
33
  case "SubagentStart": {
30
34
  return `${chalkSubagentHeading(`${subagentIcon} Subagent #${output.id} starting (${output.modelAndProvider})`)}
31
35
 
@@ -73,6 +77,20 @@ ${output.summary}\n\n`
73
77
  ),
74
78
  )
75
79
 
80
+ const promptToString = (prompt: Prompt.Prompt): string => {
81
+ let textParts: string[] = []
82
+ for (const message of prompt.content) {
83
+ if (message.role !== "user") continue
84
+ let content = message.content
85
+ for (const part of content) {
86
+ if (part.type !== "text") continue
87
+ textParts.push(part.text)
88
+ }
89
+ }
90
+ return textParts.join("\n")
91
+ }
92
+
93
+ const chalkAgentHeading = chalk.bold.green
76
94
  const chalkScriptHeading = chalk.bold.blue
77
95
  const chalkSubagentHeading = chalk.bold.magenta
78
96
  const chalkReasoningHeading = chalk.bold.yellow
@@ -0,0 +1,65 @@
1
+ import { assert, describe, it } from "@effect/vitest"
2
+ import { Effect, Ref } from "effect"
3
+ import {
4
+ HttpClient,
5
+ HttpClientRequest,
6
+ HttpClientResponse,
7
+ } from "effect/unstable/http"
8
+ import * as WebToMarkdown from "./WebToMarkdown.ts"
9
+
10
+ describe("WebToMarkdown", () => {
11
+ it.effect("convertUrl follows redirects", () =>
12
+ Effect.gen(function* () {
13
+ const requests = yield* Ref.make<Array<string>>([])
14
+ const client = HttpClient.make(
15
+ (request: HttpClientRequest.HttpClientRequest) =>
16
+ Effect.gen(function* () {
17
+ yield* Ref.update(requests, (current) => [...current, request.url])
18
+
19
+ if (request.url === "https://example.com/start") {
20
+ return HttpClientResponse.fromWeb(
21
+ request,
22
+ new Response(null, {
23
+ status: 302,
24
+ headers: { location: "/article" },
25
+ }),
26
+ )
27
+ }
28
+
29
+ if (request.url === "https://example.com/article") {
30
+ return HttpClientResponse.fromWeb(
31
+ request,
32
+ new Response("<main><h1>Hello</h1><p>World</p></main>", {
33
+ status: 200,
34
+ headers: { "content-type": "text/html" },
35
+ }),
36
+ )
37
+ }
38
+
39
+ return HttpClientResponse.fromWeb(
40
+ request,
41
+ new Response("not found", { status: 404 }),
42
+ )
43
+ }),
44
+ )
45
+
46
+ const { markdown, seen } = yield* Effect.gen(function* () {
47
+ const webToMarkdown = yield* WebToMarkdown.WebToMarkdown
48
+ const markdown = yield* webToMarkdown.convertUrl(
49
+ "https://example.com/start",
50
+ )
51
+ const seen = yield* Ref.get(requests)
52
+ return { markdown, seen }
53
+ }).pipe(
54
+ Effect.provide(WebToMarkdown.layer),
55
+ Effect.provideService(HttpClient.HttpClient, client),
56
+ )
57
+
58
+ assert.deepStrictEqual(seen, [
59
+ "https://example.com/start",
60
+ "https://example.com/article",
61
+ ])
62
+ assert.strictEqual(markdown.trim(), "Hello\n=====\n\nWorld")
63
+ }),
64
+ )
65
+ })
@@ -27,6 +27,7 @@ export const layer = Layer.effect(
27
27
  WebToMarkdown,
28
28
  Effect.gen(function* () {
29
29
  const client = (yield* HttpClient.HttpClient).pipe(
30
+ HttpClient.followRedirects(),
30
31
  HttpClient.filterStatusOk,
31
32
  HttpClient.retryTransient({
32
33
  times: 3,
package/src/index.ts CHANGED
@@ -6,22 +6,22 @@ export * as Agent from "./Agent.ts"
6
6
  /**
7
7
  * @since 1.0.0
8
8
  */
9
- export * as AgentTools from "./AgentTools.ts"
9
+ export * as AgentExecutor from "./AgentExecutor.ts"
10
10
 
11
11
  /**
12
12
  * @since 1.0.0
13
13
  */
14
- export * as Codex from "./Codex.ts"
14
+ export * as AgentTools from "./AgentTools.ts"
15
15
 
16
16
  /**
17
17
  * @since 1.0.0
18
18
  */
19
- export * as Copilot from "./Copilot.ts"
19
+ export * as Codex from "./Codex.ts"
20
20
 
21
21
  /**
22
22
  * @since 1.0.0
23
23
  */
24
- export * as Executor from "./Executor.ts"
24
+ export * as Copilot from "./Copilot.ts"
25
25
 
26
26
  /**
27
27
  * @since 1.0.0
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=Agent.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Agent.test.d.ts","sourceRoot":"","sources":["../src/Agent.test.ts"],"names":[],"mappings":""}
@@ -1,111 +0,0 @@
1
- import { NodeServices } from "@effect/platform-node";
2
- import { Effect, Layer, Stream } from "effect";
3
- import { describe, it } from "@effect/vitest";
4
- import { expect } from "vitest";
5
- import { AgentModelConfig, make } from "./Agent.js";
6
- import { pretty } from "./OutputFormatter.js";
7
- import { LanguageModel, Prompt } from "effect/unstable/ai";
8
- import * as Model from "effect/unstable/ai/Model";
9
- import { Executor } from "./Executor.js";
10
- import { ToolkitRenderer } from "./ToolkitRenderer.js";
11
- import { AgentToolHandlersTest } from "./AgentTools.js";
12
- const usage = {
13
- inputTokens: {
14
- uncached: undefined,
15
- total: undefined,
16
- cacheRead: undefined,
17
- cacheWrite: undefined,
18
- },
19
- outputTokens: {
20
- total: undefined,
21
- text: undefined,
22
- reasoning: undefined,
23
- },
24
- };
25
- const promptText = (prompt) => prompt.content
26
- .flatMap((message) => {
27
- if (typeof message.content === "string") {
28
- return [message.content];
29
- }
30
- return message.content.flatMap((part) => {
31
- switch (part.type) {
32
- case "text":
33
- case "reasoning":
34
- return [part.text];
35
- default:
36
- return [];
37
- }
38
- });
39
- })
40
- .join("\n");
41
- const scriptResponse = (script) => Stream.fromIterable([
42
- { type: "text-start", id: "script" },
43
- { type: "text-delta", id: "script", delta: script },
44
- { type: "text-end", id: "script" },
45
- {
46
- type: "finish",
47
- reason: "stop",
48
- usage,
49
- response: undefined,
50
- },
51
- ]);
52
- const TestModel = Model.make("test-provider", "test-model", Layer.effect(LanguageModel.LanguageModel, LanguageModel.make({
53
- generateText: () => Effect.succeed([]),
54
- streamText: ({ prompt }) => {
55
- const text = promptText(prompt);
56
- if (text.includes("grandchild task")) {
57
- return scriptResponse([
58
- 'console.log("grandchild output")',
59
- 'await taskComplete("grandchild summary")',
60
- ].join("\n"));
61
- }
62
- if (text.includes("child task")) {
63
- return scriptResponse([
64
- 'const result = await delegate("grandchild task")',
65
- "await taskComplete(`child summary: ${result}`)",
66
- ].join("\n"));
67
- }
68
- return scriptResponse([
69
- 'const result = await delegate("child task")',
70
- "await taskComplete(`root summary: ${result}`)",
71
- ].join("\n"));
72
- },
73
- })));
74
- describe("Agent", () => {
75
- it.effect("forwards nested subagent output", () => Effect.gen(function* () {
76
- const seen = [];
77
- const agent = yield* make({
78
- directory: process.cwd(),
79
- prompt: "root task",
80
- });
81
- const output = yield* agent.output.pipe(Stream.tap((part) => Effect.sync(() => {
82
- switch (part._tag) {
83
- case "SubagentStart":
84
- case "SubagentComplete":
85
- seen.push(`${part._tag}:${part.id}`);
86
- break;
87
- case "SubagentPart":
88
- seen.push(`${part._tag}:${part.id}:${part.part._tag}`);
89
- break;
90
- default:
91
- seen.push(part._tag);
92
- break;
93
- }
94
- })), pretty, Stream.mkString);
95
- expect(seen).toContain("SubagentStart:1");
96
- expect(seen).toContain("SubagentStart:2");
97
- expect(seen).toContain("SubagentPart:2:ScriptStart");
98
- expect(seen).toContain("SubagentPart:2:ScriptOutput");
99
- expect(seen).toContain("SubagentComplete:2");
100
- expect(seen).toContain("SubagentComplete:1");
101
- expect(output).toContain("Subagent #1 starting");
102
- expect(output).toContain("Subagent #2 starting");
103
- expect(output).toContain("grandchild output");
104
- expect(output).toContain("Subagent #2 complete");
105
- expect(output).toContain("Task complete:");
106
- expect(output).toContain("root summary: child summary: grandchild summary");
107
- }).pipe(Effect.provide(Layer.mergeAll(AgentToolHandlersTest, TestModel, AgentModelConfig.layer({
108
- supportsNoTools: true,
109
- }), Executor.layer, ToolkitRenderer.layer).pipe(Layer.provideMerge(NodeServices.layer)))));
110
- });
111
- //# sourceMappingURL=Agent.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Agent.test.js","sourceRoot":"","sources":["../src/Agent.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=AgentTools.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AgentTools.test.d.ts","sourceRoot":"","sources":["../src/AgentTools.test.ts"],"names":[],"mappings":""}