@akanjs/cli 3.0.0-beta.16 → 3.0.0-beta.17
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/.build-stamp +1 -1
- package/{CodeAgent-zgccwajm.js → CodeAgent-gjqnc2mg.js} +2 -2
- package/{CodeTui-993tx7gf.js → CodeTui-nd9hqvrw.js} +196 -11
- package/akanCode.js +9 -9
- package/{cloud.command-4x35k1z6.js → cloud.command-dk5tj39k.js} +2 -2
- package/{cloud.script-3k8nbbpn.js → cloud.script-ch3xrw3s.js} +2 -2
- package/{code.command-mrfb7a1g.js → code.command-88sebgnn.js} +5 -5
- package/{index-5405kc0x.js → index-2sq06vv0.js} +169 -199
- package/{index-sxg87a8f.js → index-bzakk0c2.js} +160 -96
- package/{index-wfwknnje.js → index-jwrpsrg3.js} +8 -6
- package/{index-zg0v1zte.js → index-n8kt2wh6.js} +1 -1
- package/{index-018dszb3.js → index-p0tew3fq.js} +2 -0
- package/index-qt8dqgnf.js +250 -0
- package/index.js +6 -6
- package/{localRegistry.command-tt00nwwg.js → localRegistry.command-yppdqrxv.js} +1 -1
- package/{package.command-8sy6jjwy.js → package.command-cgp2p2v5.js} +1 -1
- package/package.json +8 -6
- package/{subspace.command-x9897gj4.js → subspace.command-jqv5n79w.js} +1 -1
- package/{workspace.command-ayqa1rgt.js → workspace.command-txv1yv5e.js} +2 -2
- package/index-jgrnpt6y.js +0 -121
|
@@ -1,4 +1,160 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// pkgs/@akanjs/cli/code/CodeTuiLines.ts
|
|
3
|
+
import stringWidth from "string-width";
|
|
4
|
+
|
|
5
|
+
class CodeTuiLines2 {
|
|
6
|
+
static width(text) {
|
|
7
|
+
return stringWidth(text);
|
|
8
|
+
}
|
|
9
|
+
static rows(rows, width, keyBase) {
|
|
10
|
+
const lines = [];
|
|
11
|
+
rows.forEach((row, idx) => {
|
|
12
|
+
for (const line of CodeTuiLines2.#row(row, Math.max(8, width), `${keyBase}:${idx}`))
|
|
13
|
+
lines.push(line);
|
|
14
|
+
});
|
|
15
|
+
return lines;
|
|
16
|
+
}
|
|
17
|
+
static #row(row, width, key) {
|
|
18
|
+
const prefix = row.prefix;
|
|
19
|
+
const head = prefix ? CodeTuiLines2.width(prefix.text) : 0;
|
|
20
|
+
const hang = row.hang ?? " ".repeat(head);
|
|
21
|
+
const body = width - Math.max(head, CodeTuiLines2.width(hang));
|
|
22
|
+
const wrapped = CodeTuiLines2.#wrap(row.spans, Math.max(4, body));
|
|
23
|
+
if (!wrapped.length)
|
|
24
|
+
wrapped.push([]);
|
|
25
|
+
return wrapped.map((spans, at) => ({
|
|
26
|
+
key: `${key}:${at}`,
|
|
27
|
+
spans: [...at === 0 ? prefix ? [prefix] : [] : hang ? [{ text: hang }] : [], ...spans]
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
static #wrap(spans, width) {
|
|
31
|
+
const lines = [];
|
|
32
|
+
let line = [];
|
|
33
|
+
let used = 0;
|
|
34
|
+
let soft = false;
|
|
35
|
+
const push = (fromWrap) => {
|
|
36
|
+
lines.push(line);
|
|
37
|
+
line = [];
|
|
38
|
+
used = 0;
|
|
39
|
+
soft = fromWrap;
|
|
40
|
+
};
|
|
41
|
+
for (const span of spans) {
|
|
42
|
+
const { text, ...style } = span;
|
|
43
|
+
for (const piece of text.split(`
|
|
44
|
+
`).flatMap((part, at) => at ? [null, part] : [part])) {
|
|
45
|
+
if (piece === null) {
|
|
46
|
+
push(false);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
for (const word of CodeTuiLines2.#words(piece)) {
|
|
50
|
+
const size = CodeTuiLines2.width(word);
|
|
51
|
+
if (!word.trim() && !used && soft)
|
|
52
|
+
continue;
|
|
53
|
+
if (used && used + size > width)
|
|
54
|
+
push(true);
|
|
55
|
+
if (size > width) {
|
|
56
|
+
for (const chunk of CodeTuiLines2.#chunks(word, width)) {
|
|
57
|
+
if (used)
|
|
58
|
+
push(true);
|
|
59
|
+
line.push({ ...style, text: chunk });
|
|
60
|
+
used = CodeTuiLines2.width(chunk);
|
|
61
|
+
if (used >= width)
|
|
62
|
+
push(true);
|
|
63
|
+
}
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
line.push({ ...style, text: word });
|
|
67
|
+
used += size;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (line.length)
|
|
72
|
+
lines.push(line);
|
|
73
|
+
return lines;
|
|
74
|
+
}
|
|
75
|
+
static #words(text) {
|
|
76
|
+
return text.match(/\S+\s*|\s+/g) ?? [];
|
|
77
|
+
}
|
|
78
|
+
static #chunks(word, width) {
|
|
79
|
+
const chunks = [];
|
|
80
|
+
let chunk = "";
|
|
81
|
+
for (const char of word) {
|
|
82
|
+
if (CodeTuiLines2.width(chunk + char) > width) {
|
|
83
|
+
chunks.push(chunk);
|
|
84
|
+
chunk = char;
|
|
85
|
+
} else
|
|
86
|
+
chunk += char;
|
|
87
|
+
}
|
|
88
|
+
if (chunk)
|
|
89
|
+
chunks.push(chunk);
|
|
90
|
+
return chunks;
|
|
91
|
+
}
|
|
92
|
+
static text(line) {
|
|
93
|
+
return line.spans.map((span) => span.text).join("");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// pkgs/@akanjs/cli/code/CodeTuiAgents.ts
|
|
98
|
+
class CodeTuiAgents2 {
|
|
99
|
+
static markWidth = 4;
|
|
100
|
+
static minDescription = 12;
|
|
101
|
+
static rows(agents, session, width, now = Date.now()) {
|
|
102
|
+
if (!agents.length)
|
|
103
|
+
return [];
|
|
104
|
+
const kindColumn = Math.max(...agents.map((agent) => CodeTuiLines2.width(agent.kind))) + 2;
|
|
105
|
+
return [
|
|
106
|
+
{
|
|
107
|
+
key: "agents:self",
|
|
108
|
+
spans: [{ text: "\u276F ", color: "cyan" }, { text: "\u23FA ", color: "green" }, { text: session }]
|
|
109
|
+
},
|
|
110
|
+
...agents.map((agent, at) => ({
|
|
111
|
+
key: `agents:${agent.id || at}`,
|
|
112
|
+
spans: CodeTuiAgents2.#row(agent, kindColumn, width, now)
|
|
113
|
+
}))
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
static #row(agent, kindColumn, width, now) {
|
|
117
|
+
const meta = `${CodeTuiAgents2.elapsed(now - agent.startedAt)} \xB7 \u2193 ${CodeTuiAgents2.tokens(agent.tokens)} tokens`;
|
|
118
|
+
const head = CodeTuiAgents2.markWidth + kindColumn;
|
|
119
|
+
const room = width - head - CodeTuiLines2.width(meta) - 2;
|
|
120
|
+
const spans = [{ text: " \u25EF ", color: "yellow" }, { text: agent.kind.padEnd(kindColumn) }];
|
|
121
|
+
if (room < CodeTuiAgents2.minDescription)
|
|
122
|
+
return [...spans, { text: CodeTuiAgents2.#clip(agent.description, Math.max(1, width - head)) }];
|
|
123
|
+
const description = CodeTuiAgents2.#clip(agent.description, room);
|
|
124
|
+
const pad = " ".repeat(Math.max(0, room - CodeTuiLines2.width(description)));
|
|
125
|
+
return [...spans, { text: `${description}${pad}` }, { text: ` ${meta}`, dim: true }];
|
|
126
|
+
}
|
|
127
|
+
static elapsed(ms) {
|
|
128
|
+
const total = Math.max(0, Math.round(ms / 1000));
|
|
129
|
+
const hours = Math.floor(total / 3600);
|
|
130
|
+
const minutes = Math.floor(total % 3600 / 60);
|
|
131
|
+
const seconds = total % 60;
|
|
132
|
+
if (hours)
|
|
133
|
+
return `${hours}h ${minutes}m`;
|
|
134
|
+
if (minutes)
|
|
135
|
+
return `${minutes}m ${seconds}s`;
|
|
136
|
+
return `${seconds}s`;
|
|
137
|
+
}
|
|
138
|
+
static tokens(count) {
|
|
139
|
+
if (count < 1000)
|
|
140
|
+
return `${Math.max(0, Math.round(count))}`;
|
|
141
|
+
if (count < 1e6)
|
|
142
|
+
return `${(count / 1000).toFixed(1)}k`;
|
|
143
|
+
return `${(count / 1e6).toFixed(1)}M`;
|
|
144
|
+
}
|
|
145
|
+
static #clip(text, width) {
|
|
146
|
+
if (CodeTuiLines2.width(text) <= width)
|
|
147
|
+
return text;
|
|
148
|
+
let clipped = "";
|
|
149
|
+
for (const char of text) {
|
|
150
|
+
if (CodeTuiLines2.width(clipped + char) > width - 1)
|
|
151
|
+
break;
|
|
152
|
+
clipped += char;
|
|
153
|
+
}
|
|
154
|
+
return `${clipped}\u2026`;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
2
158
|
// pkgs/@akanjs/cli/code/CodeTuiClipboard.ts
|
|
3
159
|
import { existsSync } from "fs";
|
|
4
160
|
import { tmpdir } from "os";
|
|
@@ -102,6 +258,7 @@ class CodeTuiCommands2 {
|
|
|
102
258
|
{ name: "name", desc: "name this session", arg: "<text>" },
|
|
103
259
|
{ name: "fork", desc: "continue this conversation twice; the copy takes over here", arg: "[name]" },
|
|
104
260
|
{ name: "agents", desc: "what a sub-agent may be, and what one may spend" },
|
|
261
|
+
{ name: "mcp", desc: "the MCP servers this session reached", arg: "[add|remove|reload]" },
|
|
105
262
|
{ name: "peers", desc: "the other akan code sessions running here" },
|
|
106
263
|
{ name: "msg", desc: "hand a message to another session", arg: "<peer> <text>" },
|
|
107
264
|
{ name: "quit", desc: "leave the session" }
|
|
@@ -131,6 +288,8 @@ class CodeTuiCommands2 {
|
|
|
131
288
|
"esc interrupt \xB7 pgup/pgdn scroll \xB7 ^c quit",
|
|
132
289
|
"@ completes a file in this repo \xB7 \u2190 at the start of the prompt lists past sessions",
|
|
133
290
|
"",
|
|
291
|
+
"Scroll: the wheel scrolls this transcript \xB7 hold shift to select text with the mouse instead",
|
|
292
|
+
"",
|
|
134
293
|
"Images: cmd+v attaches what is on the clipboard \xB7 ^v does too, without the terminal in the way",
|
|
135
294
|
"",
|
|
136
295
|
'shift+enter needs a terminal that reports it \u2014 in VS Code bind it to send "\\u001b\\r".'
|
|
@@ -139,101 +298,6 @@ class CodeTuiCommands2 {
|
|
|
139
298
|
}
|
|
140
299
|
}
|
|
141
300
|
|
|
142
|
-
// pkgs/@akanjs/cli/code/CodeTuiLines.ts
|
|
143
|
-
import stringWidth from "string-width";
|
|
144
|
-
|
|
145
|
-
class CodeTuiLines2 {
|
|
146
|
-
static width(text) {
|
|
147
|
-
return stringWidth(text);
|
|
148
|
-
}
|
|
149
|
-
static rows(rows, width, keyBase) {
|
|
150
|
-
const lines = [];
|
|
151
|
-
rows.forEach((row, idx) => {
|
|
152
|
-
for (const line of CodeTuiLines2.#row(row, Math.max(8, width), `${keyBase}:${idx}`))
|
|
153
|
-
lines.push(line);
|
|
154
|
-
});
|
|
155
|
-
return lines;
|
|
156
|
-
}
|
|
157
|
-
static #row(row, width, key) {
|
|
158
|
-
const prefix = row.prefix;
|
|
159
|
-
const head = prefix ? CodeTuiLines2.width(prefix.text) : 0;
|
|
160
|
-
const hang = row.hang ?? " ".repeat(head);
|
|
161
|
-
const body = width - Math.max(head, CodeTuiLines2.width(hang));
|
|
162
|
-
const wrapped = CodeTuiLines2.#wrap(row.spans, Math.max(4, body));
|
|
163
|
-
if (!wrapped.length)
|
|
164
|
-
wrapped.push([]);
|
|
165
|
-
return wrapped.map((spans, at) => ({
|
|
166
|
-
key: `${key}:${at}`,
|
|
167
|
-
spans: [...at === 0 ? prefix ? [prefix] : [] : hang ? [{ text: hang }] : [], ...spans]
|
|
168
|
-
}));
|
|
169
|
-
}
|
|
170
|
-
static #wrap(spans, width) {
|
|
171
|
-
const lines = [];
|
|
172
|
-
let line = [];
|
|
173
|
-
let used = 0;
|
|
174
|
-
let soft = false;
|
|
175
|
-
const push = (fromWrap) => {
|
|
176
|
-
lines.push(line);
|
|
177
|
-
line = [];
|
|
178
|
-
used = 0;
|
|
179
|
-
soft = fromWrap;
|
|
180
|
-
};
|
|
181
|
-
for (const span of spans) {
|
|
182
|
-
const { text, ...style } = span;
|
|
183
|
-
for (const piece of text.split(`
|
|
184
|
-
`).flatMap((part, at) => at ? [null, part] : [part])) {
|
|
185
|
-
if (piece === null) {
|
|
186
|
-
push(false);
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
for (const word of CodeTuiLines2.#words(piece)) {
|
|
190
|
-
const size = CodeTuiLines2.width(word);
|
|
191
|
-
if (!word.trim() && !used && soft)
|
|
192
|
-
continue;
|
|
193
|
-
if (used && used + size > width)
|
|
194
|
-
push(true);
|
|
195
|
-
if (size > width) {
|
|
196
|
-
for (const chunk of CodeTuiLines2.#chunks(word, width)) {
|
|
197
|
-
if (used)
|
|
198
|
-
push(true);
|
|
199
|
-
line.push({ ...style, text: chunk });
|
|
200
|
-
used = CodeTuiLines2.width(chunk);
|
|
201
|
-
if (used >= width)
|
|
202
|
-
push(true);
|
|
203
|
-
}
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
line.push({ ...style, text: word });
|
|
207
|
-
used += size;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
if (line.length)
|
|
212
|
-
lines.push(line);
|
|
213
|
-
return lines;
|
|
214
|
-
}
|
|
215
|
-
static #words(text) {
|
|
216
|
-
return text.match(/\S+\s*|\s+/g) ?? [];
|
|
217
|
-
}
|
|
218
|
-
static #chunks(word, width) {
|
|
219
|
-
const chunks = [];
|
|
220
|
-
let chunk = "";
|
|
221
|
-
for (const char of word) {
|
|
222
|
-
if (CodeTuiLines2.width(chunk + char) > width) {
|
|
223
|
-
chunks.push(chunk);
|
|
224
|
-
chunk = char;
|
|
225
|
-
} else
|
|
226
|
-
chunk += char;
|
|
227
|
-
}
|
|
228
|
-
if (chunk)
|
|
229
|
-
chunks.push(chunk);
|
|
230
|
-
return chunks;
|
|
231
|
-
}
|
|
232
|
-
static text(line) {
|
|
233
|
-
return line.spans.map((span) => span.text).join("");
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
301
|
// pkgs/@akanjs/cli/code/CodeTuiEditor.ts
|
|
238
302
|
class CodeTuiEditor2 {
|
|
239
303
|
#text = "";
|
|
@@ -724,4 +788,4 @@ class CodeTuiParts2 {
|
|
|
724
788
|
}
|
|
725
789
|
}
|
|
726
790
|
|
|
727
|
-
export { CodeTuiClipboard2, CodeTuiCommands2,
|
|
791
|
+
export { CodeTuiLines2, CodeTuiAgents2, CodeTuiClipboard2, CodeTuiCommands2, CodeTuiEditor2, CodeTuiFiles2, CodeTuiMarkdown2, CodeTuiParts2 };
|
|
@@ -9,19 +9,19 @@ var correctInputs = (model) => {
|
|
|
9
9
|
return model;
|
|
10
10
|
return { ...model, input: [...model.input, "image"] };
|
|
11
11
|
};
|
|
12
|
-
var akanCodeModel2 = async (
|
|
12
|
+
var akanCodeModel2 = async (runtime, ref) => {
|
|
13
13
|
if (ref) {
|
|
14
|
-
const requested =
|
|
14
|
+
const requested = runtime.getModel(ref.provider, ref.id);
|
|
15
15
|
if (!requested)
|
|
16
16
|
throw new Error(`Unknown model: ${ref.provider}/${ref.id}`);
|
|
17
|
-
if (!
|
|
17
|
+
if (!runtime.hasConfiguredAuth(ref.provider))
|
|
18
18
|
throw new Error(`No API key configured for ${ref.provider}. Set ${ref.provider.toUpperCase()}_API_KEY.`);
|
|
19
19
|
return correctInputs(requested);
|
|
20
20
|
}
|
|
21
|
-
const fallback =
|
|
22
|
-
if (fallback &&
|
|
21
|
+
const fallback = runtime.getModel(akanCodeDefaultModel2.provider, akanCodeDefaultModel2.id);
|
|
22
|
+
if (fallback && runtime.hasConfiguredAuth(fallback.provider))
|
|
23
23
|
return correctInputs(fallback);
|
|
24
|
-
return correctInputs(
|
|
24
|
+
return correctInputs((await runtime.getAvailable())[0]);
|
|
25
25
|
};
|
|
26
26
|
var akanCodeModelSupportsImages2 = (model) => !!model?.input.includes("image");
|
|
27
27
|
var akanCodeModelWarnings2 = (model, compactionFloor) => {
|
|
@@ -78,6 +78,8 @@ class CodeAgentStreamPrinter2 {
|
|
|
78
78
|
case "queue":
|
|
79
79
|
case "context":
|
|
80
80
|
return;
|
|
81
|
+
case "subagent":
|
|
82
|
+
return;
|
|
81
83
|
case "question":
|
|
82
84
|
return this.#line(chalk.yellow(`? ${event.question.prompt}`));
|
|
83
85
|
case "approval":
|
|
@@ -155,8 +155,10 @@ class PackageRunner extends runner("package") {
|
|
|
155
155
|
const packageRuntimeDependencies = {
|
|
156
156
|
"@akanjs/devkit": [
|
|
157
157
|
"tailwind-scrollbar",
|
|
158
|
+
"@earendil-works/chord",
|
|
158
159
|
"@earendil-works/pi-agent-core",
|
|
159
160
|
"@earendil-works/pi-ai",
|
|
161
|
+
"@earendil-works/pi-telemetry",
|
|
160
162
|
"@earendil-works/pi-tui"
|
|
161
163
|
]
|
|
162
164
|
};
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// pkgs/@akanjs/devkit/codeAgent/agent/SubagentPool.ts
|
|
3
|
+
import { codeAgentReadOnlyBuiltins } from "akanjs/common";
|
|
4
|
+
import { Type } from "typebox";
|
|
5
|
+
var maxResultChars = 8000;
|
|
6
|
+
var tickMs = 1000;
|
|
7
|
+
|
|
8
|
+
class SubagentPool2 {
|
|
9
|
+
static kinds = {
|
|
10
|
+
explore: {
|
|
11
|
+
readOnly: true,
|
|
12
|
+
desc: "reads, searches and reports back. It cannot write a file or run a command."
|
|
13
|
+
},
|
|
14
|
+
code: {
|
|
15
|
+
readOnly: false,
|
|
16
|
+
desc: "makes the change itself, with the same tools as this session."
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
static defaultKind = "explore";
|
|
20
|
+
#options;
|
|
21
|
+
#children = new Map;
|
|
22
|
+
#running = 0;
|
|
23
|
+
#spent = 0;
|
|
24
|
+
#ticker = null;
|
|
25
|
+
constructor(options) {
|
|
26
|
+
this.#options = options;
|
|
27
|
+
}
|
|
28
|
+
static extensionFor(options) {
|
|
29
|
+
if (!options.parent.tools.subagent)
|
|
30
|
+
return;
|
|
31
|
+
if (options.depth >= options.parent.tools.subagent.maxDepth)
|
|
32
|
+
return;
|
|
33
|
+
const pool = new SubagentPool2(options);
|
|
34
|
+
return { name: "akan-subagent", factory: (pi) => pool.#register(pi) };
|
|
35
|
+
}
|
|
36
|
+
static toolName = "task";
|
|
37
|
+
#register(pi) {
|
|
38
|
+
pi.registerTool({
|
|
39
|
+
name: SubagentPool2.toolName,
|
|
40
|
+
label: "Task",
|
|
41
|
+
description: "Run a focused sub-agent on one self-contained question and get back its answer. Use it for searching and reading that would otherwise fill this conversation \u2014 the sub-agent's own reading does not come back, only its answer. Give it everything it needs: it cannot see this conversation.",
|
|
42
|
+
promptSnippet: "task: run a sub-agent on one self-contained question and get back only its answer",
|
|
43
|
+
parameters: Type.Object({
|
|
44
|
+
description: Type.String({ description: "Three to five words naming the task, for the activity log." }),
|
|
45
|
+
prompt: Type.String({
|
|
46
|
+
description: "The complete instruction. The sub-agent sees nothing of this conversation."
|
|
47
|
+
}),
|
|
48
|
+
type: Type.Optional(Type.Union(Object.entries(SubagentPool2.kinds).map(([kind, spec]) => Type.Literal(kind, { description: spec.desc })), { description: `What the sub-agent may do. Defaults to ${SubagentPool2.defaultKind}.` }))
|
|
49
|
+
}),
|
|
50
|
+
execute: async (id, params, signal) => {
|
|
51
|
+
const kind = params.type ?? SubagentPool2.defaultKind;
|
|
52
|
+
const refusal = this.#refuse();
|
|
53
|
+
if (refusal)
|
|
54
|
+
return { content: [{ type: "text", text: refusal }], details: undefined, isError: true };
|
|
55
|
+
this.#running += 1;
|
|
56
|
+
this.#children.set(id, { kind, description: params.description, startedAt: Date.now() });
|
|
57
|
+
this.#report();
|
|
58
|
+
this.#options.onNotice?.(`${kind} sub-agent \xB7 ${params.description}`);
|
|
59
|
+
try {
|
|
60
|
+
const text = await this.#run(id, kind, params.prompt, signal);
|
|
61
|
+
return { content: [{ type: "text", text }], details: undefined };
|
|
62
|
+
} finally {
|
|
63
|
+
this.#running -= 1;
|
|
64
|
+
this.#children.delete(id);
|
|
65
|
+
this.#report();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
#refuse() {
|
|
71
|
+
const budget = this.#options.parent.tools.subagent;
|
|
72
|
+
if (!budget)
|
|
73
|
+
return "Sub-agents are disabled by this profile.";
|
|
74
|
+
if (this.#running >= budget.maxConcurrent)
|
|
75
|
+
return `Too many sub-agents are already running (limit ${budget.maxConcurrent}). Wait for one to finish.`;
|
|
76
|
+
if (this.#spent >= budget.budget)
|
|
77
|
+
return `The sub-agent token budget (${budget.budget}) is spent. Do the rest of this work yourself.`;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
static childOf(parent, kind) {
|
|
81
|
+
const budget = parent.tools.subagent;
|
|
82
|
+
return {
|
|
83
|
+
...parent,
|
|
84
|
+
name: `${parent.name}:${kind}`,
|
|
85
|
+
tools: {
|
|
86
|
+
...parent.tools,
|
|
87
|
+
builtin: SubagentPool2.kinds[kind].readOnly ? parent.tools.builtin.filter((tool) => codeAgentReadOnlyBuiltins.includes(tool)) : parent.tools.builtin,
|
|
88
|
+
subagent: budget ? { ...budget, maxDepth: budget.maxDepth } : false
|
|
89
|
+
},
|
|
90
|
+
session: { store: "memory", crossSession: false },
|
|
91
|
+
ui: { canPrompt: false },
|
|
92
|
+
context: { projectFiles: false, skills: parent.context.skills }
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
#report() {
|
|
96
|
+
const onAgents = this.#options.onAgents;
|
|
97
|
+
if (!onAgents)
|
|
98
|
+
return;
|
|
99
|
+
onAgents([...this.#children].map(([id, child]) => ({
|
|
100
|
+
id,
|
|
101
|
+
kind: child.kind,
|
|
102
|
+
description: child.description,
|
|
103
|
+
startedAt: child.startedAt,
|
|
104
|
+
tokens: child.agent?.tokensUsed ?? 0
|
|
105
|
+
})));
|
|
106
|
+
if (this.#children.size && !this.#ticker) {
|
|
107
|
+
this.#ticker = setInterval(() => this.#report(), tickMs);
|
|
108
|
+
this.#ticker.unref?.();
|
|
109
|
+
} else if (!this.#children.size && this.#ticker) {
|
|
110
|
+
clearInterval(this.#ticker);
|
|
111
|
+
this.#ticker = null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async#run(id, kind, prompt, signal) {
|
|
115
|
+
const { CodeAgent } = await import("./CodeAgent-gjqnc2mg.js");
|
|
116
|
+
const budget = this.#options.parent.tools.subagent;
|
|
117
|
+
const child = SubagentPool2.childOf(this.#options.parent, kind);
|
|
118
|
+
const agent = await CodeAgent.create({
|
|
119
|
+
workspace: this.#options.workspace,
|
|
120
|
+
cwd: this.#options.cwd,
|
|
121
|
+
profile: child,
|
|
122
|
+
apps: [],
|
|
123
|
+
depth: this.#options.depth + 1
|
|
124
|
+
});
|
|
125
|
+
const running = this.#children.get(id);
|
|
126
|
+
if (running)
|
|
127
|
+
running.agent = agent;
|
|
128
|
+
const abort = () => void agent.abort();
|
|
129
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
130
|
+
try {
|
|
131
|
+
let answer = "";
|
|
132
|
+
agent.on((event) => {
|
|
133
|
+
if (event.type === "message" && event.role === "assistant")
|
|
134
|
+
answer = event.text;
|
|
135
|
+
});
|
|
136
|
+
await agent.prompt(prompt);
|
|
137
|
+
await agent.waitForIdle();
|
|
138
|
+
this.#spent += agent.tokensUsed;
|
|
139
|
+
const ceiling = budget ? budget.budget : 0;
|
|
140
|
+
this.#options.onNotice?.(`${kind} sub-agent finished, ${agent.tokensUsed} tokens (${this.#spent} of ${ceiling} spent)`);
|
|
141
|
+
return answer.length > maxResultChars ? `${answer.slice(0, maxResultChars)}
|
|
142
|
+
\u2026(truncated)` : answer || "(no answer)";
|
|
143
|
+
} finally {
|
|
144
|
+
signal?.removeEventListener("abort", abort);
|
|
145
|
+
agent.dispose();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// pkgs/@akanjs/devkit/codeAgent/agent/akanCodePaths.ts
|
|
151
|
+
import { existsSync } from "fs";
|
|
152
|
+
import { homedir } from "os";
|
|
153
|
+
import path from "path";
|
|
154
|
+
var builtinSkillsDir = () => {
|
|
155
|
+
const candidates = [path.join(import.meta.dir, "skills"), path.join(import.meta.dir, "..", "skills")];
|
|
156
|
+
return candidates.find((candidate) => existsSync(candidate));
|
|
157
|
+
};
|
|
158
|
+
var akanCodePaths2 = {
|
|
159
|
+
workspaceDir: (workspaceRoot) => path.join(workspaceRoot, ".akan", "code"),
|
|
160
|
+
sessionsDir: (workspaceRoot) => path.join(workspaceRoot, ".akan", "code", "sessions"),
|
|
161
|
+
extensionsDir: (workspaceRoot) => path.join(workspaceRoot, ".akan", "code", "extensions"),
|
|
162
|
+
skillsDir: (workspaceRoot) => path.join(workspaceRoot, ".akan", "code", "skills"),
|
|
163
|
+
mailDir: (workspaceRoot) => path.join(workspaceRoot, ".akan", "code", "mail"),
|
|
164
|
+
builtinSkillsDir,
|
|
165
|
+
mcpFile: (workspaceRoot) => path.join(workspaceRoot, ".akan", "code", "mcp.json"),
|
|
166
|
+
globalDir: () => path.join(homedir(), ".akan", "code"),
|
|
167
|
+
authFile: () => path.join(homedir(), ".akan", "code", "auth.json"),
|
|
168
|
+
modelsFile: () => path.join(homedir(), ".akan", "code", "models.json")
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// pkgs/@akanjs/devkit/codeAgent/tools/McpServerConfig.ts
|
|
172
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
173
|
+
import path2 from "path";
|
|
174
|
+
class McpServerConfig {
|
|
175
|
+
static file(workspaceRoot) {
|
|
176
|
+
return akanCodePaths2.mcpFile(workspaceRoot);
|
|
177
|
+
}
|
|
178
|
+
static read(workspaceRoot) {
|
|
179
|
+
const entries = Object.entries(McpServerConfig.#parse(workspaceRoot).map);
|
|
180
|
+
return entries.filter(([, entry]) => !entry.disabled).map(([name, entry]) => McpServerConfig.#refOf(name, entry));
|
|
181
|
+
}
|
|
182
|
+
static declared(workspaceRoot) {
|
|
183
|
+
return Object.entries(McpServerConfig.#parse(workspaceRoot).map).map(([name, entry]) => ({
|
|
184
|
+
ref: McpServerConfig.#refOf(name, entry),
|
|
185
|
+
disabled: !!entry.disabled
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
188
|
+
static add(workspaceRoot, name, target) {
|
|
189
|
+
const [head, ...rest] = target;
|
|
190
|
+
if (!head)
|
|
191
|
+
throw new Error("Give the server a command to run, or a url to reach.");
|
|
192
|
+
const entry = /^https?:\/\//.test(head) ? { url: head } : { command: head, args: rest };
|
|
193
|
+
const { file, key, map } = McpServerConfig.#parse(workspaceRoot);
|
|
194
|
+
const next = { ...map, [name]: entry };
|
|
195
|
+
mkdirSync(path2.dirname(file), { recursive: true });
|
|
196
|
+
writeFileSync(file, `${JSON.stringify({ [key]: next }, null, 2)}
|
|
197
|
+
`);
|
|
198
|
+
return McpServerConfig.#refOf(name, entry);
|
|
199
|
+
}
|
|
200
|
+
static remove(workspaceRoot, name) {
|
|
201
|
+
const { file, key, map } = McpServerConfig.#parse(workspaceRoot);
|
|
202
|
+
if (!(name in map))
|
|
203
|
+
return false;
|
|
204
|
+
const next = { ...map };
|
|
205
|
+
delete next[name];
|
|
206
|
+
writeFileSync(file, `${JSON.stringify({ [key]: next }, null, 2)}
|
|
207
|
+
`);
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
static #refOf(name, entry) {
|
|
211
|
+
return {
|
|
212
|
+
name,
|
|
213
|
+
transport: entry.url ? "http" : "stdio",
|
|
214
|
+
...entry.command ? { command: entry.command } : {},
|
|
215
|
+
...entry.args ? { args: entry.args } : {},
|
|
216
|
+
...entry.url ? { url: entry.url } : {},
|
|
217
|
+
...entry.env ? { env: entry.env } : {}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
static targetOf(ref) {
|
|
221
|
+
return ref.url ?? [ref.command, ...ref.args ?? []].filter(Boolean).join(" ");
|
|
222
|
+
}
|
|
223
|
+
static problem(workspaceRoot) {
|
|
224
|
+
const file = McpServerConfig.file(workspaceRoot);
|
|
225
|
+
if (!existsSync2(file))
|
|
226
|
+
return;
|
|
227
|
+
try {
|
|
228
|
+
JSON.parse(readFileSync(file, "utf8"));
|
|
229
|
+
return;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
return `${file} is not valid JSON \u2014 ${String(error)}`;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
static #parse(workspaceRoot) {
|
|
235
|
+
const file = McpServerConfig.file(workspaceRoot);
|
|
236
|
+
const empty = { file, key: "mcpServers", map: {} };
|
|
237
|
+
if (!existsSync2(file))
|
|
238
|
+
return empty;
|
|
239
|
+
try {
|
|
240
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
241
|
+
if (parsed.servers && !parsed.mcpServers)
|
|
242
|
+
return { file, key: "servers", map: parsed.servers };
|
|
243
|
+
return { ...empty, map: parsed.mcpServers ?? {} };
|
|
244
|
+
} catch {
|
|
245
|
+
return empty;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export { SubagentPool2, akanCodePaths2, McpServerConfig };
|
package/index.js
CHANGED
|
@@ -14,18 +14,18 @@ import path from "path";
|
|
|
14
14
|
|
|
15
15
|
// pkgs/@akanjs/cli/commandModules.ts
|
|
16
16
|
var commandModules = {
|
|
17
|
-
workspace: async () => (await import("./workspace.command-
|
|
17
|
+
workspace: async () => (await import("./workspace.command-txv1yv5e.js")).WorkspaceCommand,
|
|
18
18
|
agent: async () => (await import("./agent.command-n664dtcv.js")).AgentCommand,
|
|
19
|
-
code: async () => (await import("./code.command-
|
|
19
|
+
code: async () => (await import("./code.command-88sebgnn.js")).CodeCommand,
|
|
20
20
|
application: async () => (await import("./application.command-t5542jte.js")).ApplicationCommand,
|
|
21
21
|
library: async () => (await import("./library.command-m8p2r1dh.js")).LibraryCommand,
|
|
22
|
-
localRegistry: async () => (await import("./localRegistry.command-
|
|
23
|
-
package: async () => (await import("./package.command-
|
|
22
|
+
localRegistry: async () => (await import("./localRegistry.command-yppdqrxv.js")).LocalRegistryCommand,
|
|
23
|
+
package: async () => (await import("./package.command-cgp2p2v5.js")).PackageCommand,
|
|
24
24
|
module: async () => (await import("./module.command-mfzvqt0n.js")).ModuleCommand,
|
|
25
25
|
page: async () => (await import("./page.command-taenb02b.js")).PageCommand,
|
|
26
26
|
context: async () => (await import("./context.command-pqsm5jrk.js")).ContextCommand,
|
|
27
|
-
cloud: async () => (await import("./cloud.command-
|
|
28
|
-
subspace: async () => (await import("./subspace.command-
|
|
27
|
+
cloud: async () => (await import("./cloud.command-dk5tj39k.js")).CloudCommand,
|
|
28
|
+
subspace: async () => (await import("./subspace.command-jqv5n79w.js")).SubspaceCommand,
|
|
29
29
|
guideline: async () => (await import("./guideline.command-rpc2emw8.js")).GuidelineCommand,
|
|
30
30
|
scalar: async () => (await import("./scalar.command-mq8qfp4t.js")).ScalarCommand,
|
|
31
31
|
primitive: async () => (await import("./primitive.command-7kpncmkk.js")).PrimitiveCommand,
|
|
@@ -10,7 +10,7 @@ import"./index-6wwzs1bc.js";
|
|
|
10
10
|
import"./index-kcxmv48c.js";
|
|
11
11
|
import {
|
|
12
12
|
PackageScript
|
|
13
|
-
} from "./index-
|
|
13
|
+
} from "./index-p0tew3fq.js";
|
|
14
14
|
|
|
15
15
|
// pkgs/@akanjs/cli/package/package.command.ts
|
|
16
16
|
class PackageCommand extends command("package", [PackageScript], ({ public: target }) => ({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/cli",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.17",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -31,14 +31,16 @@
|
|
|
31
31
|
"./package.json": "./package.json"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@earendil-works/
|
|
35
|
-
"@earendil-works/pi-
|
|
36
|
-
"@earendil-works/pi-
|
|
37
|
-
"@earendil-works/pi-
|
|
34
|
+
"@earendil-works/chord": "0.86.1",
|
|
35
|
+
"@earendil-works/pi-agent-core": "0.86.1",
|
|
36
|
+
"@earendil-works/pi-ai": "0.86.1",
|
|
37
|
+
"@earendil-works/pi-coding-agent": "0.86.1",
|
|
38
|
+
"@earendil-works/pi-telemetry": "0.86.1",
|
|
39
|
+
"@earendil-works/pi-tui": "0.86.1",
|
|
38
40
|
"@inquirer/prompts": "^8.4.3",
|
|
39
41
|
"@tailwindcss/node": "^4.3.0",
|
|
40
42
|
"@trapezedev/project": "^7.1.4",
|
|
41
|
-
"akanjs": "3.0.0-beta.
|
|
43
|
+
"akanjs": "3.0.0-beta.17",
|
|
42
44
|
"chalk": "^5.6.2",
|
|
43
45
|
"commander": "^14.0.3",
|
|
44
46
|
"dayjs": "^1.11.20",
|
|
@@ -725,7 +725,7 @@ class SubspaceRunner extends runner("subspace") {
|
|
|
725
725
|
if (!asked)
|
|
726
726
|
return { name: subspace.name, workspaceId, host, apps, libs: [], outcome: "cancelled", files: [] };
|
|
727
727
|
const { libs } = await subspace.slice();
|
|
728
|
-
const { CloudScript } = await import("./cloud.script-
|
|
728
|
+
const { CloudScript } = await import("./cloud.script-ch3xrw3s.js");
|
|
729
729
|
const { files } = await CommandContainer.get(CloudScript).uploadEnv(workspace, {
|
|
730
730
|
host,
|
|
731
731
|
workspaceId,
|