clanka 0.0.2 → 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 +158 -42
- 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 +6 -14
- package/src/Agent.ts +216 -78
- package/src/AgentTools.ts +3 -4
- package/src/Executor.ts +0 -1
- package/src/OutputFormatter.ts +44 -68
- package/dist/ai.d.ts +0 -2
- package/dist/ai.d.ts.map +0 -1
- package/dist/ai.js +0 -29
- package/dist/ai.js.map +0 -1
- package/src/ai.ts +0 -48
|
@@ -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
|
},
|
|
@@ -10,13 +10,6 @@
|
|
|
10
10
|
"./*": "./dist/*.js",
|
|
11
11
|
"./package.json": "./package.json"
|
|
12
12
|
},
|
|
13
|
-
"scripts": {
|
|
14
|
-
"prepare": "husky && effect-language-service patch",
|
|
15
|
-
"check": "oxlint -c .oxlintrc.json --fix && pnpm tsgo --noEmit",
|
|
16
|
-
"test": "vitest run",
|
|
17
|
-
"build": "tsc -b tsconfig.json",
|
|
18
|
-
"prepublishOnly": "pnpm build"
|
|
19
|
-
},
|
|
20
13
|
"files": [
|
|
21
14
|
"dist",
|
|
22
15
|
"src"
|
|
@@ -28,7 +21,6 @@
|
|
|
28
21
|
"type": "git",
|
|
29
22
|
"url": "https://github.com/tim-smart/clanka.git"
|
|
30
23
|
},
|
|
31
|
-
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48",
|
|
32
24
|
"dependencies": {
|
|
33
25
|
"@vscode/ripgrep": "^1.17.0",
|
|
34
26
|
"chalk": "^5.6.2",
|
|
@@ -65,9 +57,9 @@
|
|
|
65
57
|
"prettier --write"
|
|
66
58
|
]
|
|
67
59
|
},
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
"scripts": {
|
|
61
|
+
"check": "oxlint -c .oxlintrc.json --fix && pnpm tsc --noEmit",
|
|
62
|
+
"test": "vitest run",
|
|
63
|
+
"build": "tsc -b tsconfig.json"
|
|
72
64
|
}
|
|
73
|
-
}
|
|
65
|
+
}
|
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,62 +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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
205
|
+
let id = agentCounter++
|
|
206
|
+
const stream = spawn(
|
|
207
|
+
id,
|
|
208
|
+
Prompt.make(`You have been spawned using "subagent" to complete the following task:
|
|
209
|
+
|
|
210
|
+
${prompt}`),
|
|
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) => {
|
|
154
237
|
Queue.offerUnsafe(
|
|
155
238
|
output,
|
|
156
|
-
new
|
|
157
|
-
id,
|
|
158
|
-
prompt,
|
|
159
|
-
provider,
|
|
160
|
-
model,
|
|
161
|
-
output: stream,
|
|
162
|
-
}),
|
|
239
|
+
new SubagentComplete({ id, summary: finished.summary }),
|
|
163
240
|
)
|
|
164
|
-
return
|
|
241
|
+
return Effect.succeed(finished.summary)
|
|
165
242
|
}),
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
Effect.as(""),
|
|
169
|
-
Effect.catch((e) => Effect.succeed(e.summary)),
|
|
243
|
+
)
|
|
244
|
+
}).pipe(
|
|
170
245
|
options.subagentModel
|
|
171
246
|
? Effect.provide(Layer.orDie(options.subagentModel))
|
|
172
247
|
: Effect.provideServices(services),
|
|
@@ -177,18 +252,6 @@ export const make: <
|
|
|
177
252
|
ServiceMap.add(TaskCompleteDeferred, deferred),
|
|
178
253
|
)
|
|
179
254
|
|
|
180
|
-
prompt = Prompt.concat(
|
|
181
|
-
prompt,
|
|
182
|
-
agentsMd.pipe(
|
|
183
|
-
Option.map((md) =>
|
|
184
|
-
Prompt.make(`Here is a copy of ./AGENTS.md. ALWAYS follow these instructions when completing the above task:
|
|
185
|
-
|
|
186
|
-
${md}`),
|
|
187
|
-
),
|
|
188
|
-
Option.getOrElse(() => Prompt.empty),
|
|
189
|
-
),
|
|
190
|
-
)
|
|
191
|
-
|
|
192
255
|
if (provider !== "openai") {
|
|
193
256
|
prompt = Prompt.setSystem(prompt, system)
|
|
194
257
|
}
|
|
@@ -197,7 +260,7 @@ ${md}`),
|
|
|
197
260
|
yield* Effect.gen(function* () {
|
|
198
261
|
while (true) {
|
|
199
262
|
if (currentScript.length > 0) {
|
|
200
|
-
|
|
263
|
+
maybeSend(agentId, new ScriptStart({ script: currentScript }))
|
|
201
264
|
const result = yield* pipe(
|
|
202
265
|
executor.execute({
|
|
203
266
|
tools,
|
|
@@ -205,8 +268,10 @@ ${md}`),
|
|
|
205
268
|
}),
|
|
206
269
|
Stream.mkString,
|
|
207
270
|
)
|
|
208
|
-
|
|
209
|
-
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
|
+
])
|
|
210
275
|
currentScript = ""
|
|
211
276
|
}
|
|
212
277
|
|
|
@@ -218,15 +283,37 @@ ${md}`),
|
|
|
218
283
|
return
|
|
219
284
|
}
|
|
220
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
|
+
|
|
221
300
|
let response = Array.empty<StreamPart<{}>>()
|
|
301
|
+
let reasoningStarted = false
|
|
302
|
+
let hadReasoningDelta = false
|
|
222
303
|
yield* pipe(
|
|
223
304
|
ai.streamText({ prompt }),
|
|
224
|
-
Stream.takeUntil(
|
|
225
|
-
(part)
|
|
226
|
-
|
|
227
|
-
|
|
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
|
+
}),
|
|
228
314
|
Stream.runForEachArray((parts) => {
|
|
229
315
|
response.push(...parts)
|
|
316
|
+
|
|
230
317
|
for (const part of parts) {
|
|
231
318
|
switch (part.type) {
|
|
232
319
|
case "text-start":
|
|
@@ -236,16 +323,23 @@ ${md}`),
|
|
|
236
323
|
currentScript += part.delta
|
|
237
324
|
break
|
|
238
325
|
case "reasoning-start":
|
|
239
|
-
|
|
326
|
+
reasoningStarted = true
|
|
240
327
|
break
|
|
241
328
|
case "reasoning-delta":
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
329
|
+
hadReasoningDelta = true
|
|
330
|
+
if (reasoningStarted) {
|
|
331
|
+
reasoningStarted = false
|
|
332
|
+
maybeSend(agentId, new ReasoningStart(), true)
|
|
333
|
+
}
|
|
334
|
+
maybeSend(agentId, new ReasoningDelta({ delta: part.delta }))
|
|
246
335
|
break
|
|
247
336
|
case "reasoning-end":
|
|
248
|
-
|
|
337
|
+
reasoningStarted = false
|
|
338
|
+
if (hadReasoningDelta) {
|
|
339
|
+
hadReasoningDelta = false
|
|
340
|
+
const sent = maybeSend(agentId, new ReasoningEnd())
|
|
341
|
+
if (sent) flushBuffer()
|
|
342
|
+
}
|
|
249
343
|
break
|
|
250
344
|
case "finish":
|
|
251
345
|
// console.log("Tokens used:", part.usage, "\n")
|
|
@@ -274,7 +368,7 @@ ${md}`),
|
|
|
274
368
|
return Stream.fromQueue(output)
|
|
275
369
|
}, Stream.unwrap)
|
|
276
370
|
|
|
277
|
-
const output = yield* spawn(Prompt.make(options.prompt)).pipe(
|
|
371
|
+
const output = yield* spawn(agentCounter++, Prompt.make(options.prompt)).pipe(
|
|
278
372
|
Stream.broadcast({
|
|
279
373
|
capacity: "unbounded",
|
|
280
374
|
}),
|
|
@@ -282,6 +376,12 @@ ${md}`),
|
|
|
282
376
|
|
|
283
377
|
return identity<Agent>({
|
|
284
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
|
+
}),
|
|
285
385
|
})
|
|
286
386
|
// oxlint-disable-next-line typescript/no-explicit-any
|
|
287
387
|
}) as any
|
|
@@ -290,19 +390,17 @@ ${md}`),
|
|
|
290
390
|
const generateSystem = Effect.fn(function* (tools: Toolkit.Toolkit<any>) {
|
|
291
391
|
const renderer = yield* ToolkitRenderer
|
|
292
392
|
|
|
293
|
-
return
|
|
294
|
-
|
|
295
|
-
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.
|
|
296
|
-
|
|
297
|
-
# 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.
|
|
298
394
|
|
|
299
|
-
To
|
|
395
|
+
To do your job, only respond with javascript code that will be executed for you.
|
|
300
396
|
|
|
301
397
|
- Do not add any markdown formatting, just code.
|
|
302
398
|
- Use \`console.log\` to print any output you need.
|
|
303
399
|
- Top level await is supported.
|
|
304
400
|
- **Prefer using the functions provided** over the bash tool
|
|
305
401
|
|
|
402
|
+
**When you have completed your task**, call the "taskComplete" function with the final output.
|
|
403
|
+
|
|
306
404
|
You have the following functions available to you:
|
|
307
405
|
|
|
308
406
|
\`\`\`ts
|
|
@@ -338,9 +436,7 @@ Javascript output:
|
|
|
338
436
|
|
|
339
437
|
- Use the current state of the codebase to inform your decisions. Don't look at git history unless explicity asked to.
|
|
340
438
|
- Only add comments when necessary.
|
|
341
|
-
-
|
|
342
|
-
- Use the "subagent" tool to delegate research / exploration tasks
|
|
343
|
-
- 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
|
|
344
440
|
`
|
|
345
441
|
})
|
|
346
442
|
|
|
@@ -407,7 +503,40 @@ export class AgentFinished extends Schema.TaggedErrorClass<AgentFinished>()(
|
|
|
407
503
|
* @since 1.0.0
|
|
408
504
|
* @category Output
|
|
409
505
|
*/
|
|
410
|
-
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([
|
|
411
540
|
ReasoningStart,
|
|
412
541
|
ReasoningDelta,
|
|
413
542
|
ReasoningEnd,
|
|
@@ -419,26 +548,35 @@ export const OutputPart = Schema.Union([
|
|
|
419
548
|
* @since 1.0.0
|
|
420
549
|
* @category Output
|
|
421
550
|
*/
|
|
422
|
-
export
|
|
551
|
+
export class SubagentPart extends Schema.TaggedClass<SubagentPart>()(
|
|
552
|
+
"SubagentPart",
|
|
553
|
+
{
|
|
554
|
+
id: Schema.Number,
|
|
555
|
+
part: ContentPart,
|
|
556
|
+
},
|
|
557
|
+
) {}
|
|
423
558
|
|
|
424
559
|
/**
|
|
425
560
|
* @since 1.0.0
|
|
426
561
|
* @category Output
|
|
427
562
|
*/
|
|
428
|
-
export
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
output: Stream.Stream<Output, AgentFinished>
|
|
434
|
-
}> {
|
|
435
|
-
get modelAndProvider() {
|
|
436
|
-
return `${this.provider}/${this.model}`
|
|
437
|
-
}
|
|
438
|
-
}
|
|
563
|
+
export type Output =
|
|
564
|
+
| ContentPart
|
|
565
|
+
| SubagentStart
|
|
566
|
+
| SubagentComplete
|
|
567
|
+
| SubagentPart
|
|
439
568
|
|
|
440
569
|
/**
|
|
441
570
|
* @since 1.0.0
|
|
442
571
|
* @category Output
|
|
443
572
|
*/
|
|
444
|
-
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