oh-pi 0.1.37 → 0.1.39
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/package.json
CHANGED
|
@@ -110,6 +110,9 @@ For simple single-file tasks, work directly without the colony.`,
|
|
|
110
110
|
goal: Type.String({ description: "What the colony should accomplish" }),
|
|
111
111
|
maxAnts: Type.Optional(Type.Number({ description: "Max concurrent ants (default: auto-adapt)", minimum: 1, maximum: 8 })),
|
|
112
112
|
maxCost: Type.Optional(Type.Number({ description: "Max cost budget in USD (default: unlimited)", minimum: 0.01 })),
|
|
113
|
+
scoutModel: Type.Optional(Type.String({ description: "Model for scout ants (default: current session model)" })),
|
|
114
|
+
workerModel: Type.Optional(Type.String({ description: "Model for worker ants (default: current session model)" })),
|
|
115
|
+
soldierModel: Type.Optional(Type.String({ description: "Model for soldier ants (default: current session model)" })),
|
|
113
116
|
}),
|
|
114
117
|
|
|
115
118
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
@@ -169,12 +172,18 @@ For simple single-file tasks, work directly without the colony.`,
|
|
|
169
172
|
appendFileSync(gitignorePath, `${content.length && !content.endsWith("\n") ? "\n" : ""}.ant-colony/\n`);
|
|
170
173
|
}
|
|
171
174
|
|
|
175
|
+
const modelOverrides: Record<string, string> = {};
|
|
176
|
+
if (params.scoutModel) modelOverrides.scout = params.scoutModel;
|
|
177
|
+
if (params.workerModel) modelOverrides.worker = params.workerModel;
|
|
178
|
+
if (params.soldierModel) modelOverrides.soldier = params.soldierModel;
|
|
179
|
+
|
|
172
180
|
const state = await runColony({
|
|
173
181
|
cwd: ctx.cwd,
|
|
174
182
|
goal: params.goal,
|
|
175
183
|
maxAnts: params.maxAnts,
|
|
176
184
|
maxCost: params.maxCost,
|
|
177
185
|
currentModel,
|
|
186
|
+
modelOverrides,
|
|
178
187
|
signal: signal ?? undefined,
|
|
179
188
|
callbacks,
|
|
180
189
|
});
|
|
@@ -241,21 +250,81 @@ For simple single-file tasks, work directly without the colony.`,
|
|
|
241
250
|
const details = result.details as ColonyDetails | undefined;
|
|
242
251
|
|
|
243
252
|
// ─── 运行中 ───
|
|
244
|
-
if (!details?.state) {
|
|
253
|
+
if (!details?.state || (details.state.status !== "done" && details.state.status !== "failed")) {
|
|
254
|
+
const state = details?.state;
|
|
245
255
|
const log = details?.log ?? [];
|
|
246
256
|
const container = new Container();
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
257
|
+
|
|
258
|
+
if (state) {
|
|
259
|
+
const m = state.metrics;
|
|
260
|
+
const elapsed = formatDuration(Date.now() - state.createdAt);
|
|
261
|
+
|
|
262
|
+
// 标题行:● N ants launched (phase)
|
|
263
|
+
const activeAnts = state.ants.filter(a => a.status === "working");
|
|
264
|
+
const totalAnts = state.ants.length;
|
|
265
|
+
container.addChild(new Text(
|
|
266
|
+
theme.fg("warning", "● ") +
|
|
267
|
+
theme.fg("toolTitle", theme.bold(`${totalAnts} ant${totalAnts !== 1 ? "s" : ""} launched `)) +
|
|
268
|
+
theme.fg("muted", `(${state.status}) `) +
|
|
269
|
+
theme.fg("dim", `${elapsed} │ ${formatCost(m.totalCost)}`),
|
|
270
|
+
0, 0,
|
|
271
|
+
));
|
|
272
|
+
|
|
273
|
+
// 进度条
|
|
274
|
+
if (m.tasksTotal > 0) {
|
|
275
|
+
container.addChild(new Text(` ${progressBar(m.tasksDone, m.tasksTotal, 20, theme)}`, 0, 0));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// 蚂蚁树
|
|
279
|
+
const ants = expanded ? state.ants : state.ants.slice(-8);
|
|
280
|
+
for (let i = 0; i < ants.length; i++) {
|
|
281
|
+
const a = ants[i];
|
|
282
|
+
const isLast = i === ants.length - 1;
|
|
283
|
+
const branch = isLast ? "└─" : "├─";
|
|
284
|
+
const pipe = isLast ? " " : "│ ";
|
|
285
|
+
|
|
286
|
+
const statusDot = a.status === "working" ? theme.fg("warning", "◉")
|
|
287
|
+
: a.status === "done" ? theme.fg("success", "✓")
|
|
288
|
+
: theme.fg("error", "✗");
|
|
289
|
+
|
|
290
|
+
const task = state.tasks.find(t => t.id === a.taskId);
|
|
291
|
+
const taskTitle = task?.title?.slice(0, 55) || "...";
|
|
292
|
+
const dur = a.finishedAt ? formatDuration(a.finishedAt - a.startedAt) : formatDuration(Date.now() - a.startedAt);
|
|
293
|
+
const turns = a.usage.turns > 0 ? `${a.usage.turns}t` : "";
|
|
294
|
+
const model = a.model ? a.model.split("/").pop()! : "";
|
|
295
|
+
|
|
296
|
+
container.addChild(new Text(
|
|
297
|
+
theme.fg("muted", ` ${branch} `) + statusDot + " " +
|
|
298
|
+
theme.fg("accent", `@${a.id.slice(0, 20)} `) +
|
|
299
|
+
theme.fg("dim", `(${a.caste}) ${dur}${turns ? " │ " + turns : ""}`) +
|
|
300
|
+
(model ? " " + theme.fg("muted", model) : ""),
|
|
301
|
+
0, 0,
|
|
302
|
+
));
|
|
303
|
+
container.addChild(new Text(
|
|
304
|
+
theme.fg("muted", ` ${pipe}`) + theme.fg("dim", `⎿ ${taskTitle}`),
|
|
305
|
+
0, 0,
|
|
306
|
+
));
|
|
307
|
+
}
|
|
308
|
+
if (!expanded && state.ants.length > 8) {
|
|
309
|
+
container.addChild(new Text(theme.fg("muted", ` ⋯ +${state.ants.length - 8} more (expand to see all)`), 0, 0));
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
container.addChild(new Text(
|
|
313
|
+
theme.fg("warning", "● ") + theme.fg("toolTitle", theme.bold("Colony ")) +
|
|
314
|
+
theme.fg("accent", details?.phase || "initializing..."),
|
|
315
|
+
0, 0,
|
|
316
|
+
));
|
|
255
317
|
}
|
|
256
|
-
|
|
257
|
-
|
|
318
|
+
|
|
319
|
+
// 最近日志(仅展开时)
|
|
320
|
+
if (expanded && log.length > 0) {
|
|
321
|
+
container.addChild(new Spacer(1));
|
|
322
|
+
const recent = log.slice(-10);
|
|
323
|
+
for (const l of recent) {
|
|
324
|
+
container.addChild(new Text(theme.fg("dim", ` ${l}`), 0, 0));
|
|
325
|
+
}
|
|
258
326
|
}
|
|
327
|
+
|
|
259
328
|
return container;
|
|
260
329
|
}
|
|
261
330
|
|
|
@@ -37,6 +37,7 @@ export interface QueenOptions {
|
|
|
37
37
|
maxAnts?: number;
|
|
38
38
|
maxCost?: number;
|
|
39
39
|
currentModel: string;
|
|
40
|
+
modelOverrides?: ModelOverrides;
|
|
40
41
|
signal?: AbortSignal;
|
|
41
42
|
callbacks: QueenCallbacks;
|
|
42
43
|
}
|
|
@@ -138,6 +139,7 @@ interface WaveOptions {
|
|
|
138
139
|
cwd: string;
|
|
139
140
|
caste: AntCaste;
|
|
140
141
|
currentModel: string;
|
|
142
|
+
modelOverrides?: ModelOverrides;
|
|
141
143
|
signal?: AbortSignal;
|
|
142
144
|
callbacks: QueenCallbacks;
|
|
143
145
|
}
|
|
@@ -147,7 +149,8 @@ interface WaveOptions {
|
|
|
147
149
|
*/
|
|
148
150
|
async function runAntWave(opts: WaveOptions): Promise<"ok"> {
|
|
149
151
|
const { nest, cwd, caste, signal, callbacks, currentModel } = opts;
|
|
150
|
-
const
|
|
152
|
+
const casteModel = opts.modelOverrides?.[caste] || currentModel;
|
|
153
|
+
const config = { ...DEFAULT_ANT_CONFIGS[caste], model: casteModel };
|
|
151
154
|
|
|
152
155
|
let backoffMs = 0; // 429 退避时间
|
|
153
156
|
let consecutiveRateLimits = 0; // 连续限流计数
|
|
@@ -160,7 +163,8 @@ async function runAntWave(opts: WaveOptions): Promise<"ok"> {
|
|
|
160
163
|
|
|
161
164
|
const ant: Ant = {
|
|
162
165
|
id: "", caste, status: "idle", taskId: task.id,
|
|
163
|
-
pid: null,
|
|
166
|
+
pid: null, model: casteModel,
|
|
167
|
+
usage: { input: 0, output: 0, cost: 0, turns: 0 },
|
|
164
168
|
startedAt: Date.now(), finishedAt: null,
|
|
165
169
|
};
|
|
166
170
|
callbacks.onAntSpawn(ant, task);
|
|
@@ -311,6 +315,7 @@ export async function runColony(opts: QueenOptions): Promise<ColonyState> {
|
|
|
311
315
|
const waveBase: Omit<WaveOptions, "caste"> = {
|
|
312
316
|
nest, cwd: opts.cwd, signal, callbacks,
|
|
313
317
|
currentModel: opts.currentModel,
|
|
318
|
+
modelOverrides: opts.modelOverrides,
|
|
314
319
|
};
|
|
315
320
|
|
|
316
321
|
const cleanup = () => {
|