pi-agent-fleet 0.5.0 → 0.7.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.
- package/package.json +1 -1
- package/src/canvas-client.tsx +18 -51
- package/src/canvas-layout.ts +83 -0
- package/src/canvas.ts +8 -4
- package/src/command.ts +7 -35
- package/src/contracts.ts +9 -5
- package/src/controller.ts +117 -17
- package/src/dag.ts +5 -1
- package/src/edits.ts +24 -5
- package/src/fleet-recovery.ts +12 -2
- package/src/prompts.ts +11 -0
- package/src/runner.ts +63 -2
- package/src/scheduler.ts +173 -14
- package/src/state.ts +19 -17
- package/src/tools.ts +13 -29
- package/src/types.ts +9 -0
package/src/tools.ts
CHANGED
|
@@ -4,15 +4,15 @@ import { Type } from "typebox";
|
|
|
4
4
|
import { validateFleetSpec } from "./dag.js";
|
|
5
5
|
import { insertWorkers } from "./insert.js";
|
|
6
6
|
import { loadPreferences, mergeFleetConfig } from "./preferences.js";
|
|
7
|
-
import { activeFleet, currentState, dagPreview, ensureCanvas, killFleet,
|
|
7
|
+
import { activeFleet, currentState, dagPreview, ensureCanvas, killFleet, requestRelaunch, startLoop, statusText, stopCanvas, updateWidget } from "./controller.js";
|
|
8
8
|
import { openInBrowser, listFleetRoots } from "./canvas.js";
|
|
9
9
|
import { editConfig, editNode, type ConfigEditKey, type NodeEditKey } from "./edits.js";
|
|
10
|
-
import { ensureFleetGitignore, fleetRootFor, isInsideGitRepo,
|
|
10
|
+
import { ensureFleetGitignore, fleetRootFor, isInsideGitRepo, writePlanFiles, writeWorkerPrompts } from "./fleet-store.js";
|
|
11
11
|
import { recoverLatestFleet } from "./fleet-recovery.js";
|
|
12
|
-
import { listModelRefs,
|
|
12
|
+
import { listModelRefs, validateFleetModels } from "./model-resolution.js";
|
|
13
13
|
import { runFleetDesign, slugifyFleetName } from "./planner.js";
|
|
14
14
|
import { writeReport } from "./report.js";
|
|
15
|
-
import { initFleetState,
|
|
15
|
+
import { initFleetState, writeState } from "./state.js";
|
|
16
16
|
import { renderDag } from "./viz.js";
|
|
17
17
|
|
|
18
18
|
export function textResult(text: string, details: Record<string, unknown> = {}) {
|
|
@@ -58,6 +58,8 @@ export function registerFleetTools(pi: ExtensionAPI): void {
|
|
|
58
58
|
model: Type.Optional(Type.String({ description: "Fleet-wide default model" })),
|
|
59
59
|
effort: Type.Optional(EffortSchema),
|
|
60
60
|
warn_cost_usd: Type.Optional(Type.Number()),
|
|
61
|
+
max_cost_usd: Type.Optional(Type.Number()),
|
|
62
|
+
worker_extensions: Type.Optional(Type.Array(Type.String())),
|
|
61
63
|
loop: Type.Optional(Type.Object({
|
|
62
64
|
gate: Type.Union([Type.Literal("reviewer"), Type.Literal("none")]),
|
|
63
65
|
max_iterations: Type.Number(),
|
|
@@ -88,7 +90,7 @@ export function registerFleetTools(pi: ExtensionAPI): void {
|
|
|
88
90
|
const state = initFleetState(v.spec);
|
|
89
91
|
await ensureFleetGitignore(ctx.cwd);
|
|
90
92
|
await writePlanFiles(fleetRoot, v.spec, state);
|
|
91
|
-
const active = activeFleet.current = { spec: v.spec, fleetRoot, state, killSwitch: { killed: false }, pauseSwitch: { paused: false }, running: false, costWarned: false, sessions: new Map(), killedNodes: new Set(), widgetVisible: false };
|
|
93
|
+
const active = activeFleet.current = { spec: v.spec, fleetRoot, state, killSwitch: { killed: false }, pauseSwitch: { paused: false }, running: false, costWarned: false, sessions: new Map(), killedNodes: new Set(), relaunchRequests: new Set(), widgetVisible: false };
|
|
92
94
|
updateWidget(ctx, active);
|
|
93
95
|
|
|
94
96
|
const dag = await dagPreview(v.spec, undefined, fleetRoot);
|
|
@@ -242,7 +244,7 @@ export function registerFleetTools(pi: ExtensionAPI): void {
|
|
|
242
244
|
pi.registerTool({
|
|
243
245
|
name: "fleet_relaunch",
|
|
244
246
|
label: "Fleet Relaunch",
|
|
245
|
-
description: "Relaunch a failed node and any blocked downstream dependents. Optionally override the worker model for this run.",
|
|
247
|
+
description: "Relaunch a failed node and any blocked downstream dependents. Works while the fleet is running (queued for the next scheduler pass) and after it stops. Optionally override the worker model for this run.",
|
|
246
248
|
promptSnippet: "Relaunch a failed fleet node.",
|
|
247
249
|
parameters: Type.Object({
|
|
248
250
|
node_id: Type.String({ description: "Worker id to relaunch" }),
|
|
@@ -251,30 +253,12 @@ export function registerFleetTools(pi: ExtensionAPI): void {
|
|
|
251
253
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
252
254
|
const active = activeFleet.current;
|
|
253
255
|
if (!active) return textResult("no fleet planned yet");
|
|
254
|
-
if (active.running) return textResult("fleet is running");
|
|
255
256
|
const fleet = active;
|
|
256
257
|
await currentState(fleet);
|
|
257
258
|
if (fleet.state.status === "completed") return textResult("fleet completed, nothing to relaunch");
|
|
258
|
-
const
|
|
259
|
-
if (
|
|
260
|
-
|
|
261
|
-
const relaunchable: ReadonlySet<string> = new Set(["failed", "contract_failed", "killed"]);
|
|
262
|
-
if (!node || !relaunchable.has(node.status)) {
|
|
263
|
-
return textResult(`node "${params.node_id}" status ${node?.status ?? "missing"} cannot be relaunched; must be failed, contract_failed, or killed`);
|
|
264
|
-
}
|
|
265
|
-
if (params.model) {
|
|
266
|
-
const resolved = resolveModelReference(ctx.modelRegistry, params.model);
|
|
267
|
-
if (!resolved.ok) return textResult(resolved.error);
|
|
268
|
-
const canonical = `${resolved.model.provider}/${resolved.model.id}`;
|
|
269
|
-
fleet.spec.workers = fleet.spec.workers.map((w) => w.id === params.node_id ? { ...w, model: canonical } : w);
|
|
270
|
-
await persistFleetJson(fleet);
|
|
271
|
-
}
|
|
272
|
-
fleet.state = resetForRelaunch(fleet.state, fleet.spec, params.node_id);
|
|
273
|
-
await writeState(fleet.fleetRoot, fleet.state);
|
|
274
|
-
await writeWorkerPrompts(fleet);
|
|
275
|
-
prepareRelaunch(fleet, params.node_id);
|
|
276
|
-
void startLoop(fleet, ctx, false, true);
|
|
277
|
-
return textResult(`fleet relaunch requested for ${params.node_id}`);
|
|
259
|
+
const result = await requestRelaunch(fleet, params.node_id, params.model, ctx.modelRegistry);
|
|
260
|
+
if (result.startNow) void startLoop(fleet, ctx, false, true);
|
|
261
|
+
return textResult(result.message);
|
|
278
262
|
},
|
|
279
263
|
});
|
|
280
264
|
|
|
@@ -353,11 +337,11 @@ export function registerFleetTools(pi: ExtensionAPI): void {
|
|
|
353
337
|
pi.registerTool({
|
|
354
338
|
name: "fleet_edit",
|
|
355
339
|
label: "Fleet Edit",
|
|
356
|
-
description: "Edit the active fleet: a pending or relaunchable node's model, effort, or task — or fleet config (max_concurrent, warn_cost_usd, model, effort) when node_id is omitted. Changes persist to fleet.json and apply immediately. Edits to running
|
|
340
|
+
description: "Edit the active fleet: a pending or relaunchable node's model, effort, or task — or fleet config (max_concurrent, warn_cost_usd, max_cost_usd, model, effort) when node_id is omitted. Changes persist to fleet.json and apply immediately. Edits to running or completed nodes are refused; pending, blocked, failed, contract_failed, and killed nodes can be edited (blocked nodes have not started — nothing to invalidate).",
|
|
357
341
|
promptSnippet: "Edit a pending fleet node or fleet config.",
|
|
358
342
|
parameters: Type.Object({
|
|
359
343
|
node_id: Type.Optional(Type.String({ description: "Worker id to edit; omit for fleet config edits" })),
|
|
360
|
-
key: Type.String({ description: "Node keys: model, effort, task. Config keys: max_concurrent, warn_cost_usd, model, effort" }),
|
|
344
|
+
key: Type.String({ description: "Node keys: model, effort, task. Config keys: max_concurrent, warn_cost_usd, max_cost_usd, model, effort" }),
|
|
361
345
|
value: Type.String({ description: "New value" }),
|
|
362
346
|
}),
|
|
363
347
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
package/src/types.ts
CHANGED
|
@@ -44,6 +44,11 @@ export interface FleetConfig {
|
|
|
44
44
|
model?: string;
|
|
45
45
|
effort?: ThinkingLevelName;
|
|
46
46
|
warn_cost_usd?: number;
|
|
47
|
+
/** hard cap — fleet is killed when total estimated cost reaches it */
|
|
48
|
+
max_cost_usd?: number;
|
|
49
|
+
/** extension path fragments to load inside worker sessions (default: none).
|
|
50
|
+
* Needed for extension-provided model providers, e.g. ["opencode-pi"]. */
|
|
51
|
+
worker_extensions?: string[];
|
|
47
52
|
loop?: LoopConfig;
|
|
48
53
|
}
|
|
49
54
|
|
|
@@ -112,6 +117,10 @@ export interface FleetState {
|
|
|
112
117
|
lgtm_streak: number;
|
|
113
118
|
paused: boolean;
|
|
114
119
|
iterations: IterationSnapshot[];
|
|
120
|
+
/** liveness signal written by the running controller */
|
|
121
|
+
pid?: number;
|
|
122
|
+
/** ISO timestamp liveness signal written by the running controller */
|
|
123
|
+
heartbeat_at?: string;
|
|
115
124
|
}
|
|
116
125
|
|
|
117
126
|
export const WORKER_TYPE_TOOLS: Record<WorkerType, string[]> = {
|