clanka 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Agent.d.ts +37 -14
- package/dist/Agent.d.ts.map +1 -1
- package/dist/Agent.js +157 -43
- package/dist/Agent.js.map +1 -1
- package/dist/AgentTools.d.ts +1 -1
- package/dist/AgentTools.d.ts.map +1 -1
- package/dist/AgentTools.js +3 -4
- package/dist/AgentTools.js.map +1 -1
- package/dist/Codex.d.ts +1 -1
- package/dist/CodexAuth.d.ts +4 -4
- package/dist/Executor.d.ts.map +1 -1
- package/dist/Executor.js +1 -1
- package/dist/Executor.js.map +1 -1
- package/dist/OutputFormatter.d.ts +3 -3
- package/dist/OutputFormatter.d.ts.map +1 -1
- package/dist/OutputFormatter.js +43 -54
- package/dist/OutputFormatter.js.map +1 -1
- package/package.json +2 -2
- package/src/Agent.ts +213 -79
- package/src/AgentTools.ts +3 -4
- package/src/Executor.ts +0 -1
- package/src/OutputFormatter.ts +44 -68
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
import type
|
|
4
|
+
import { Stream } from "effect";
|
|
5
|
+
import { type Output, AgentFinished } from "./Agent.ts";
|
|
6
6
|
/**
|
|
7
7
|
* @since 1.0.0
|
|
8
8
|
* @category Models
|
|
9
9
|
*/
|
|
10
|
-
export type OutputFormatter<E = never, R = never> =
|
|
10
|
+
export type OutputFormatter<E = never, R = never> = (stream: Stream.Stream<Output, AgentFinished>) => Stream.Stream<string, E, R>;
|
|
11
11
|
/**
|
|
12
12
|
* @since 1.0.0
|
|
13
13
|
* @category Pretty
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OutputFormatter.d.ts","sourceRoot":"","sources":["../src/OutputFormatter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"OutputFormatter.d.ts","sourceRoot":"","sources":["../src/OutputFormatter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,KAAK,MAAM,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAGvD;;;GAGG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,IAAI,CAClD,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,KACzC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;AAEhC;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,eAgDlB,CAAA"}
|
package/dist/OutputFormatter.js
CHANGED
|
@@ -1,67 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { Stream } from "effect";
|
|
5
|
+
import { AgentFinished } from "./Agent.js";
|
|
5
6
|
import chalk from "chalk";
|
|
6
|
-
const prettyPrefixed = (prefix) => Sink.suspend(() => {
|
|
7
|
-
let hadReasoningDelta = false;
|
|
8
|
-
return Sink.forEach((output) => {
|
|
9
|
-
switch (output._tag) {
|
|
10
|
-
case "SubagentPart": {
|
|
11
|
-
console.log(chalkSubagentHeading(`${subagentIcon} Subagent #${output.id} starting (${output.modelAndProvider})`));
|
|
12
|
-
console.log("");
|
|
13
|
-
console.log(chalk.dim(output.prompt));
|
|
14
|
-
console.log("");
|
|
15
|
-
const prefix = chalk.magenta(`Subagent #${output.id}:`) + " ";
|
|
16
|
-
return output.output.pipe(Stream.run(prettyPrefixed(prefix)), Effect.catch((finished) => {
|
|
17
|
-
console.log(chalkSubagentHeading(`${subagentIcon} Subagent #${output.id} complete`));
|
|
18
|
-
console.log("");
|
|
19
|
-
console.log(finished.summary);
|
|
20
|
-
console.log("");
|
|
21
|
-
return Effect.void;
|
|
22
|
-
}), Effect.forkChild);
|
|
23
|
-
}
|
|
24
|
-
case "ReasoningDelta": {
|
|
25
|
-
process.stdout.write(output.delta);
|
|
26
|
-
hadReasoningDelta = true;
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
case "ReasoningEnd": {
|
|
30
|
-
if (hadReasoningDelta) {
|
|
31
|
-
console.log("\n");
|
|
32
|
-
hadReasoningDelta = false;
|
|
33
|
-
}
|
|
34
|
-
break;
|
|
35
|
-
}
|
|
36
|
-
case "ScriptStart": {
|
|
37
|
-
console.log(prefix + chalkScriptHeading(`${scriptIcon} Executing script`));
|
|
38
|
-
console.log("");
|
|
39
|
-
console.log(chalk.dim(output.script));
|
|
40
|
-
console.log("");
|
|
41
|
-
break;
|
|
42
|
-
}
|
|
43
|
-
case "ScriptEnd": {
|
|
44
|
-
console.log(prefix + chalkScriptHeading(`${scriptIcon} Script output`));
|
|
45
|
-
console.log("");
|
|
46
|
-
const lines = output.output.split("\n");
|
|
47
|
-
const truncated = lines.length > 20
|
|
48
|
-
? lines.slice(0, 20).join("\n") + "\n... (truncated)"
|
|
49
|
-
: output.output;
|
|
50
|
-
console.log(chalk.dim(truncated));
|
|
51
|
-
console.log(chalk.reset(""));
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return Effect.void;
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
7
|
/**
|
|
59
8
|
* @since 1.0.0
|
|
60
9
|
* @category Pretty
|
|
61
10
|
*/
|
|
62
|
-
export const pretty =
|
|
11
|
+
export const pretty = (stream) => stream.pipe(Stream.map((output) => {
|
|
12
|
+
let prefix = "";
|
|
13
|
+
if (output._tag === "SubagentPart") {
|
|
14
|
+
prefix = chalk.magenta(`Subagent #${output.id}:`) + " ";
|
|
15
|
+
output = output.part;
|
|
16
|
+
}
|
|
17
|
+
switch (output._tag) {
|
|
18
|
+
case "SubagentStart": {
|
|
19
|
+
return `${chalkSubagentHeading(`${subagentIcon} Subagent #${output.id} starting (${output.modelAndProvider})`)}
|
|
20
|
+
|
|
21
|
+
${chalk.dim(output.prompt)}\n\n`;
|
|
22
|
+
}
|
|
23
|
+
case "SubagentComplete": {
|
|
24
|
+
return `${chalkSubagentHeading(`${subagentIcon} Subagent #${output.id} complete`)}
|
|
25
|
+
|
|
26
|
+
${output.summary}\n\n`;
|
|
27
|
+
}
|
|
28
|
+
case "ReasoningStart": {
|
|
29
|
+
return (prefix + chalkReasoningHeading(`${thinkingIcon} Thinking:`) + " ");
|
|
30
|
+
}
|
|
31
|
+
case "ReasoningDelta": {
|
|
32
|
+
return output.delta;
|
|
33
|
+
}
|
|
34
|
+
case "ReasoningEnd": {
|
|
35
|
+
return "\n\n";
|
|
36
|
+
}
|
|
37
|
+
case "ScriptStart": {
|
|
38
|
+
return `${prefix}${chalkScriptHeading(`${scriptIcon} Executing script`)}\n\n${chalk.dim(output.script)}\n\n`;
|
|
39
|
+
}
|
|
40
|
+
case "ScriptEnd": {
|
|
41
|
+
const lines = output.output.split("\n");
|
|
42
|
+
const truncated = lines.length > 20
|
|
43
|
+
? lines.slice(0, 20).join("\n") + "\n... (truncated)"
|
|
44
|
+
: output.output;
|
|
45
|
+
return `${prefix}${chalkScriptHeading(`${scriptIcon} Script output`)}\n\n${chalk.dim(truncated)}\n\n`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}), Stream.catch((finished) => Stream.succeed(`\n${chalk.bold.green(`${doneIcon} Task complete:`)}\n\n${finished.summary}`)));
|
|
63
49
|
const chalkScriptHeading = chalk.bold.blue;
|
|
64
50
|
const chalkSubagentHeading = chalk.bold.magenta;
|
|
51
|
+
const chalkReasoningHeading = chalk.bold.yellow;
|
|
65
52
|
const scriptIcon = "\u{f0bc1}";
|
|
66
53
|
const subagentIcon = "\u{ee0d} ";
|
|
54
|
+
const thinkingIcon = "\u{f07f6}";
|
|
55
|
+
const doneIcon = "\u{eab2}";
|
|
67
56
|
//# sourceMappingURL=OutputFormatter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OutputFormatter.js","sourceRoot":"","sources":["../src/OutputFormatter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"OutputFormatter.js","sourceRoot":"","sources":["../src/OutputFormatter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clanka",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.4",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
]
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
|
-
"check": "oxlint -c .oxlintrc.json --fix && pnpm
|
|
61
|
+
"check": "oxlint -c .oxlintrc.json --fix && pnpm tsc --noEmit",
|
|
62
62
|
"test": "vitest run",
|
|
63
63
|
"build": "tsc -b tsconfig.json"
|
|
64
64
|
}
|
package/src/Agent.ts
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import {
|
|
5
5
|
Array,
|
|
6
|
-
Data,
|
|
7
6
|
Deferred,
|
|
8
7
|
Effect,
|
|
9
8
|
FileSystem,
|
|
@@ -39,6 +38,16 @@ import type { ChildProcessSpawner } from "effect/unstable/process"
|
|
|
39
38
|
*/
|
|
40
39
|
export interface Agent {
|
|
41
40
|
readonly output: Stream.Stream<Output, AgentFinished>
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Send a message to the agent to steer its behavior. This is useful for
|
|
44
|
+
* providing feedback or new instructions while the agent is running.
|
|
45
|
+
*
|
|
46
|
+
* The effect will only complete once the message has been sent.
|
|
47
|
+
* Interrupting the effect will withdraw the message, so it will not be sent
|
|
48
|
+
* to the agent.
|
|
49
|
+
*/
|
|
50
|
+
steer(message: string): Effect.Effect<void>
|
|
42
51
|
}
|
|
43
52
|
|
|
44
53
|
/**
|
|
@@ -111,66 +120,128 @@ export const make: <
|
|
|
111
120
|
| ProviderName
|
|
112
121
|
| ModelName
|
|
113
122
|
>()
|
|
123
|
+
const pendingMessages = new Set<{
|
|
124
|
+
readonly message: string
|
|
125
|
+
readonly resume: (effect: Effect.Effect<void>) => void
|
|
126
|
+
}>()
|
|
114
127
|
|
|
115
128
|
let system = yield* generateSystem(allTools)
|
|
129
|
+
|
|
130
|
+
const agentsMd = yield* pipe(
|
|
131
|
+
fs.readFileString(pathService.resolve(options.directory, "AGENTS.md")),
|
|
132
|
+
Effect.option,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if (Option.isSome(agentsMd)) {
|
|
136
|
+
system += `
|
|
137
|
+
# AGENTS.md
|
|
138
|
+
|
|
139
|
+
The following instructions are from ./AGENTS.md in the current directory.
|
|
140
|
+
You do not need to read this file again.
|
|
141
|
+
|
|
142
|
+
**ALWAYS follow these instructions when completing tasks**:
|
|
143
|
+
|
|
144
|
+
<!-- AGENTS.md start -->
|
|
145
|
+
${agentsMd.value}
|
|
146
|
+
<!-- AGENTS.md end -->
|
|
147
|
+
`
|
|
148
|
+
}
|
|
149
|
+
|
|
116
150
|
if (options.system) {
|
|
117
|
-
system += `\n${options.system}`
|
|
151
|
+
system += `\n${options.system}\n`
|
|
118
152
|
}
|
|
153
|
+
|
|
119
154
|
const withSystemPrompt = OpenAiLanguageModel.withConfigOverride({
|
|
120
155
|
store: false,
|
|
121
156
|
instructions: system,
|
|
122
157
|
})
|
|
123
158
|
|
|
124
|
-
|
|
125
|
-
fs.readFileString(pathService.resolve(options.directory, "AGENTS.md")),
|
|
126
|
-
Effect.option,
|
|
127
|
-
)
|
|
159
|
+
let agentCounter = 0
|
|
128
160
|
|
|
129
|
-
|
|
161
|
+
const outputBuffer = new Map<number, Array<Output>>()
|
|
162
|
+
let currentOutputAgent: number | null = null
|
|
130
163
|
|
|
131
164
|
const spawn: (
|
|
165
|
+
agentId: number,
|
|
132
166
|
prompt: Prompt.Prompt,
|
|
133
167
|
) => Stream.Stream<
|
|
134
168
|
Output,
|
|
135
169
|
AgentFinished,
|
|
136
170
|
LanguageModel.LanguageModel | ProviderName
|
|
137
|
-
> = Effect.fnUntraced(function* (prompt) {
|
|
171
|
+
> = Effect.fnUntraced(function* (agentId, prompt) {
|
|
138
172
|
const ai = yield* LanguageModel.LanguageModel
|
|
139
173
|
const provider = yield* ProviderName
|
|
140
174
|
const deferred = yield* Deferred.make<string>()
|
|
141
175
|
const output = yield* Queue.make<Output, AgentFinished>()
|
|
142
176
|
|
|
177
|
+
function maybeSend(agentId: number, part: Output, lock = false) {
|
|
178
|
+
if (currentOutputAgent === null || currentOutputAgent === agentId) {
|
|
179
|
+
Queue.offerUnsafe(output, part)
|
|
180
|
+
if (lock) {
|
|
181
|
+
currentOutputAgent = agentId
|
|
182
|
+
}
|
|
183
|
+
return true
|
|
184
|
+
}
|
|
185
|
+
const state = outputBuffer.get(agentId) ?? []
|
|
186
|
+
state.push(part)
|
|
187
|
+
return false
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function flushBuffer() {
|
|
191
|
+
currentOutputAgent = null
|
|
192
|
+
for (const [id, state] of outputBuffer) {
|
|
193
|
+
outputBuffer.delete(id)
|
|
194
|
+
Queue.offerAllUnsafe(output, state)
|
|
195
|
+
const lastPart = state[state.length - 1]!
|
|
196
|
+
if (lastPart._tag === "ReasoningDelta") {
|
|
197
|
+
currentOutputAgent = id
|
|
198
|
+
break
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
143
203
|
const taskServices = SubagentContext.serviceMap({
|
|
144
204
|
spawn: ({ prompt }) => {
|
|
145
|
-
let id = ++
|
|
146
|
-
|
|
205
|
+
let id = agentCounter++
|
|
206
|
+
const stream = spawn(
|
|
207
|
+
id,
|
|
147
208
|
Prompt.make(`You have been spawned using "subagent" to complete the following task:
|
|
148
209
|
|
|
149
210
|
${prompt}`),
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
211
|
+
)
|
|
212
|
+
return Effect.gen(function* () {
|
|
213
|
+
const provider = yield* ProviderName
|
|
214
|
+
const model = yield* ModelName
|
|
215
|
+
Queue.offerUnsafe(
|
|
216
|
+
output,
|
|
217
|
+
new SubagentStart({ id, prompt, model, provider }),
|
|
218
|
+
)
|
|
219
|
+
return yield* stream.pipe(
|
|
220
|
+
Stream.runForEachArray((parts) => {
|
|
221
|
+
for (const part of parts) {
|
|
222
|
+
switch (part._tag) {
|
|
223
|
+
case "SubagentStart":
|
|
224
|
+
case "SubagentComplete":
|
|
225
|
+
case "SubagentPart":
|
|
226
|
+
break
|
|
227
|
+
|
|
228
|
+
default:
|
|
229
|
+
Queue.offerUnsafe(output, new SubagentPart({ id, part }))
|
|
230
|
+
break
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return Effect.void
|
|
234
|
+
}),
|
|
235
|
+
Effect.as(""),
|
|
236
|
+
Effect.catch((finished) => {
|
|
158
237
|
Queue.offerUnsafe(
|
|
159
238
|
output,
|
|
160
|
-
new
|
|
161
|
-
id,
|
|
162
|
-
prompt,
|
|
163
|
-
provider,
|
|
164
|
-
model,
|
|
165
|
-
output: stream,
|
|
166
|
-
}),
|
|
239
|
+
new SubagentComplete({ id, summary: finished.summary }),
|
|
167
240
|
)
|
|
168
|
-
return
|
|
241
|
+
return Effect.succeed(finished.summary)
|
|
169
242
|
}),
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
Effect.as(""),
|
|
173
|
-
Effect.catch((e) => Effect.succeed(e.summary)),
|
|
243
|
+
)
|
|
244
|
+
}).pipe(
|
|
174
245
|
options.subagentModel
|
|
175
246
|
? Effect.provide(Layer.orDie(options.subagentModel))
|
|
176
247
|
: Effect.provideServices(services),
|
|
@@ -181,18 +252,6 @@ ${prompt}`),
|
|
|
181
252
|
ServiceMap.add(TaskCompleteDeferred, deferred),
|
|
182
253
|
)
|
|
183
254
|
|
|
184
|
-
prompt = Prompt.concat(
|
|
185
|
-
prompt,
|
|
186
|
-
agentsMd.pipe(
|
|
187
|
-
Option.map((md) =>
|
|
188
|
-
Prompt.make(`Here is a copy of ./AGENTS.md. ALWAYS follow these instructions when completing the above task:
|
|
189
|
-
|
|
190
|
-
${md}`),
|
|
191
|
-
),
|
|
192
|
-
Option.getOrElse(() => Prompt.empty),
|
|
193
|
-
),
|
|
194
|
-
)
|
|
195
|
-
|
|
196
255
|
if (provider !== "openai") {
|
|
197
256
|
prompt = Prompt.setSystem(prompt, system)
|
|
198
257
|
}
|
|
@@ -201,7 +260,7 @@ ${md}`),
|
|
|
201
260
|
yield* Effect.gen(function* () {
|
|
202
261
|
while (true) {
|
|
203
262
|
if (currentScript.length > 0) {
|
|
204
|
-
|
|
263
|
+
maybeSend(agentId, new ScriptStart({ script: currentScript }))
|
|
205
264
|
const result = yield* pipe(
|
|
206
265
|
executor.execute({
|
|
207
266
|
tools,
|
|
@@ -209,8 +268,10 @@ ${md}`),
|
|
|
209
268
|
}),
|
|
210
269
|
Stream.mkString,
|
|
211
270
|
)
|
|
212
|
-
|
|
213
|
-
prompt = Prompt.concat(prompt,
|
|
271
|
+
maybeSend(agentId, new ScriptEnd({ output: result }))
|
|
272
|
+
prompt = Prompt.concat(prompt, [
|
|
273
|
+
{ role: "assistant", content: `Javascript output:\n\n${result}` },
|
|
274
|
+
])
|
|
214
275
|
currentScript = ""
|
|
215
276
|
}
|
|
216
277
|
|
|
@@ -222,15 +283,37 @@ ${md}`),
|
|
|
222
283
|
return
|
|
223
284
|
}
|
|
224
285
|
|
|
286
|
+
if (pendingMessages.size > 0) {
|
|
287
|
+
prompt = Prompt.concat(
|
|
288
|
+
prompt,
|
|
289
|
+
Array.Array.from(pendingMessages, ({ message, resume }) => {
|
|
290
|
+
resume(Effect.void)
|
|
291
|
+
return {
|
|
292
|
+
role: "user",
|
|
293
|
+
content: message,
|
|
294
|
+
}
|
|
295
|
+
}),
|
|
296
|
+
)
|
|
297
|
+
pendingMessages.clear()
|
|
298
|
+
}
|
|
299
|
+
|
|
225
300
|
let response = Array.empty<StreamPart<{}>>()
|
|
301
|
+
let reasoningStarted = false
|
|
302
|
+
let hadReasoningDelta = false
|
|
226
303
|
yield* pipe(
|
|
227
304
|
ai.streamText({ prompt }),
|
|
228
|
-
Stream.takeUntil(
|
|
229
|
-
(part)
|
|
230
|
-
|
|
231
|
-
|
|
305
|
+
Stream.takeUntil((part) => {
|
|
306
|
+
if (part.type === "text-end" && currentScript.trim().length > 0) {
|
|
307
|
+
return true
|
|
308
|
+
}
|
|
309
|
+
if (part.type === "reasoning-end" && pendingMessages.size > 0) {
|
|
310
|
+
return true
|
|
311
|
+
}
|
|
312
|
+
return false
|
|
313
|
+
}),
|
|
232
314
|
Stream.runForEachArray((parts) => {
|
|
233
315
|
response.push(...parts)
|
|
316
|
+
|
|
234
317
|
for (const part of parts) {
|
|
235
318
|
switch (part.type) {
|
|
236
319
|
case "text-start":
|
|
@@ -240,16 +323,23 @@ ${md}`),
|
|
|
240
323
|
currentScript += part.delta
|
|
241
324
|
break
|
|
242
325
|
case "reasoning-start":
|
|
243
|
-
|
|
326
|
+
reasoningStarted = true
|
|
244
327
|
break
|
|
245
328
|
case "reasoning-delta":
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
329
|
+
hadReasoningDelta = true
|
|
330
|
+
if (reasoningStarted) {
|
|
331
|
+
reasoningStarted = false
|
|
332
|
+
maybeSend(agentId, new ReasoningStart(), true)
|
|
333
|
+
}
|
|
334
|
+
maybeSend(agentId, new ReasoningDelta({ delta: part.delta }))
|
|
250
335
|
break
|
|
251
336
|
case "reasoning-end":
|
|
252
|
-
|
|
337
|
+
reasoningStarted = false
|
|
338
|
+
if (hadReasoningDelta) {
|
|
339
|
+
hadReasoningDelta = false
|
|
340
|
+
const sent = maybeSend(agentId, new ReasoningEnd())
|
|
341
|
+
if (sent) flushBuffer()
|
|
342
|
+
}
|
|
253
343
|
break
|
|
254
344
|
case "finish":
|
|
255
345
|
// console.log("Tokens used:", part.usage, "\n")
|
|
@@ -278,7 +368,7 @@ ${md}`),
|
|
|
278
368
|
return Stream.fromQueue(output)
|
|
279
369
|
}, Stream.unwrap)
|
|
280
370
|
|
|
281
|
-
const output = yield* spawn(Prompt.make(options.prompt)).pipe(
|
|
371
|
+
const output = yield* spawn(agentCounter++, Prompt.make(options.prompt)).pipe(
|
|
282
372
|
Stream.broadcast({
|
|
283
373
|
capacity: "unbounded",
|
|
284
374
|
}),
|
|
@@ -286,6 +376,12 @@ ${md}`),
|
|
|
286
376
|
|
|
287
377
|
return identity<Agent>({
|
|
288
378
|
output,
|
|
379
|
+
steer: (message) =>
|
|
380
|
+
Effect.callback((resume) => {
|
|
381
|
+
const entry = { message, resume }
|
|
382
|
+
pendingMessages.add(entry)
|
|
383
|
+
return Effect.sync(() => pendingMessages.delete(entry))
|
|
384
|
+
}),
|
|
289
385
|
})
|
|
290
386
|
// oxlint-disable-next-line typescript/no-explicit-any
|
|
291
387
|
}) as any
|
|
@@ -294,19 +390,17 @@ ${md}`),
|
|
|
294
390
|
const generateSystem = Effect.fn(function* (tools: Toolkit.Toolkit<any>) {
|
|
295
391
|
const renderer = yield* ToolkitRenderer
|
|
296
392
|
|
|
297
|
-
return
|
|
298
|
-
|
|
299
|
-
You are a professional software engineer. You are precise, thoughtful and concise. You make changes with care and always do the due diligence to ensure the best possible outcome. You make no mistakes.
|
|
300
|
-
|
|
301
|
-
# Completing the task
|
|
393
|
+
return `You are a professional software engineer. You are precise, thoughtful and concise. You make changes with care and always do the due diligence to ensure the best possible outcome. You make no mistakes.
|
|
302
394
|
|
|
303
|
-
To
|
|
395
|
+
To do your job, only respond with javascript code that will be executed for you.
|
|
304
396
|
|
|
305
397
|
- Do not add any markdown formatting, just code.
|
|
306
398
|
- Use \`console.log\` to print any output you need.
|
|
307
399
|
- Top level await is supported.
|
|
308
400
|
- **Prefer using the functions provided** over the bash tool
|
|
309
401
|
|
|
402
|
+
**When you have completed your task**, call the "taskComplete" function with the final output.
|
|
403
|
+
|
|
310
404
|
You have the following functions available to you:
|
|
311
405
|
|
|
312
406
|
\`\`\`ts
|
|
@@ -342,9 +436,7 @@ Javascript output:
|
|
|
342
436
|
|
|
343
437
|
- Use the current state of the codebase to inform your decisions. Don't look at git history unless explicity asked to.
|
|
344
438
|
- Only add comments when necessary.
|
|
345
|
-
-
|
|
346
|
-
- Use the "subagent" tool to delegate research / exploration tasks
|
|
347
|
-
- When you have fully completed the task, call the "taskComplete" function
|
|
439
|
+
- Use the "subagent" tool to delegate large tasks / exploration. Run multiple subagents in parallel with Promise.all
|
|
348
440
|
`
|
|
349
441
|
})
|
|
350
442
|
|
|
@@ -411,7 +503,40 @@ export class AgentFinished extends Schema.TaggedErrorClass<AgentFinished>()(
|
|
|
411
503
|
* @since 1.0.0
|
|
412
504
|
* @category Output
|
|
413
505
|
*/
|
|
414
|
-
export
|
|
506
|
+
export class SubagentStart extends Schema.TaggedClass<SubagentStart>()(
|
|
507
|
+
"SubagentStart",
|
|
508
|
+
{
|
|
509
|
+
id: Schema.Number,
|
|
510
|
+
prompt: Schema.String,
|
|
511
|
+
model: Schema.String,
|
|
512
|
+
provider: Schema.String,
|
|
513
|
+
},
|
|
514
|
+
) {
|
|
515
|
+
get modelAndProvider() {
|
|
516
|
+
return `${this.provider}/${this.model}`
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* @since 1.0.0
|
|
522
|
+
* @category Output
|
|
523
|
+
*/
|
|
524
|
+
export class SubagentComplete extends Schema.TaggedClass<SubagentComplete>()(
|
|
525
|
+
"SubagentComplete",
|
|
526
|
+
{
|
|
527
|
+
id: Schema.Number,
|
|
528
|
+
summary: Schema.String,
|
|
529
|
+
},
|
|
530
|
+
) {}
|
|
531
|
+
|
|
532
|
+
export type ContentPart =
|
|
533
|
+
| ReasoningStart
|
|
534
|
+
| ReasoningDelta
|
|
535
|
+
| ReasoningEnd
|
|
536
|
+
| ScriptStart
|
|
537
|
+
| ScriptEnd
|
|
538
|
+
|
|
539
|
+
export const ContentPart = Schema.Union([
|
|
415
540
|
ReasoningStart,
|
|
416
541
|
ReasoningDelta,
|
|
417
542
|
ReasoningEnd,
|
|
@@ -423,26 +548,35 @@ export const OutputPart = Schema.Union([
|
|
|
423
548
|
* @since 1.0.0
|
|
424
549
|
* @category Output
|
|
425
550
|
*/
|
|
426
|
-
export
|
|
551
|
+
export class SubagentPart extends Schema.TaggedClass<SubagentPart>()(
|
|
552
|
+
"SubagentPart",
|
|
553
|
+
{
|
|
554
|
+
id: Schema.Number,
|
|
555
|
+
part: ContentPart,
|
|
556
|
+
},
|
|
557
|
+
) {}
|
|
427
558
|
|
|
428
559
|
/**
|
|
429
560
|
* @since 1.0.0
|
|
430
561
|
* @category Output
|
|
431
562
|
*/
|
|
432
|
-
export
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
output: Stream.Stream<Output, AgentFinished>
|
|
438
|
-
}> {
|
|
439
|
-
get modelAndProvider() {
|
|
440
|
-
return `${this.provider}/${this.model}`
|
|
441
|
-
}
|
|
442
|
-
}
|
|
563
|
+
export type Output =
|
|
564
|
+
| ContentPart
|
|
565
|
+
| SubagentStart
|
|
566
|
+
| SubagentComplete
|
|
567
|
+
| SubagentPart
|
|
443
568
|
|
|
444
569
|
/**
|
|
445
570
|
* @since 1.0.0
|
|
446
571
|
* @category Output
|
|
447
572
|
*/
|
|
448
|
-
export
|
|
573
|
+
export const Output = Schema.Union([
|
|
574
|
+
ReasoningStart,
|
|
575
|
+
ReasoningDelta,
|
|
576
|
+
ReasoningEnd,
|
|
577
|
+
ScriptStart,
|
|
578
|
+
ScriptEnd,
|
|
579
|
+
SubagentStart,
|
|
580
|
+
SubagentComplete,
|
|
581
|
+
SubagentPart,
|
|
582
|
+
])
|
package/src/AgentTools.ts
CHANGED
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
import { Tool, Toolkit } from "effect/unstable/ai"
|
|
17
17
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
|
|
18
18
|
import * as Glob from "glob"
|
|
19
|
-
import * as Rg from "@vscode/ripgrep"
|
|
20
19
|
import { parsePatch, patchChunks } from "./ApplyPatch.ts"
|
|
21
20
|
|
|
22
21
|
/**
|
|
@@ -180,9 +179,9 @@ export const AgentTools = Toolkit.make(
|
|
|
180
179
|
}),
|
|
181
180
|
Tool.make("taskComplete", {
|
|
182
181
|
description:
|
|
183
|
-
"
|
|
182
|
+
"Call this to send a final output message and end the current task.",
|
|
184
183
|
parameters: Schema.String.annotate({
|
|
185
|
-
identifier: "
|
|
184
|
+
identifier: "output",
|
|
186
185
|
}),
|
|
187
186
|
dependencies: [TaskCompleteDeferred],
|
|
188
187
|
}),
|
|
@@ -304,7 +303,7 @@ export const AgentToolHandlers = AgentTools.toLayer(
|
|
|
304
303
|
args.push(options.pattern)
|
|
305
304
|
let stream = pipe(
|
|
306
305
|
spawner.streamLines(
|
|
307
|
-
ChildProcess.make(
|
|
306
|
+
ChildProcess.make("rg", args, {
|
|
308
307
|
cwd,
|
|
309
308
|
stdin: "ignore",
|
|
310
309
|
}),
|
package/src/Executor.ts
CHANGED