@getpipher/armory-fleet 0.9.0 → 0.9.2
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/engine/spawnSubagent.ts +7 -3
- package/src/index.ts +2 -2
- package/src/panel/fleet-widget.ts +13 -14
- package/src/panel/widget-rows.ts +8 -8
- package/src/tools/subagent.ts +1 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getpipher/armory-fleet",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The armory suite's subagent orchestrator for the pi coding agent — a cross-harness, superpowers-native fleet where every agent is armory-native from birth.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -221,13 +221,17 @@ export async function spawnSubagent(opts: SpawnOptions): Promise<SpawnResult> {
|
|
|
221
221
|
});
|
|
222
222
|
|
|
223
223
|
// SPEC-5b-4: retain a narrow live-session handle on the run record so the panel can
|
|
224
|
-
// steer/abort mid-flight.
|
|
225
|
-
|
|
224
|
+
// steer/abort mid-flight. Wrap abort so the local `aborted` flag is set when the panel
|
|
225
|
+
// calls handle.abort() (not just the signal path) — otherwise finishRun reports
|
|
226
|
+
// "completed" instead of "aborted" on a user-initiated Stop. Cleared by finishRun.
|
|
227
|
+
let aborted = false;
|
|
228
|
+
const handle = toLiveHandle(session);
|
|
229
|
+
handle.abort = async () => { aborted = true; await session.abort(); };
|
|
230
|
+
opts.runRegistry.update(runId, { session: handle });
|
|
226
231
|
|
|
227
232
|
const budget = createTurnBudget(maxTurns);
|
|
228
233
|
let finalText = "";
|
|
229
234
|
let tokenTotal = 0;
|
|
230
|
-
let aborted = false;
|
|
231
235
|
let turnIdx = -1;
|
|
232
236
|
|
|
233
237
|
const onSignalAbort = (): void => { aborted = true; void session.abort(); };
|
package/src/index.ts
CHANGED
|
@@ -171,7 +171,7 @@ export default async function (pi: ExtensionAPI): Promise<void> {
|
|
|
171
171
|
const fleetDir = (cwd: string) => join(cwd, ".pi", "fleet");
|
|
172
172
|
const bgRuns = new BgRunsStore();
|
|
173
173
|
const resultsInbox = new ResultsInbox();
|
|
174
|
-
// SPEC-5b-2: the live widget (above editor)
|
|
174
|
+
// SPEC-5b-2: the live widget (above editor) controller.
|
|
175
175
|
// Display-only, independent of the /fleet panel; constructed per-session in session_start.
|
|
176
176
|
let fleetWidget: FleetWidgetController | null = null;
|
|
177
177
|
// The async runner's runLifecycle adapter: call the real runLifecycle with the worktree as the
|
|
@@ -275,7 +275,7 @@ export default async function (pi: ExtensionAPI): Promise<void> {
|
|
|
275
275
|
if (cands.length > 0) {
|
|
276
276
|
ctx.ui.notify(`${cands.length} interrupted fleet run${cands.length > 1 ? "s" : ""} — open /fleet to resume`, "info");
|
|
277
277
|
}
|
|
278
|
-
// SPEC-5b-2: live widget (above editor)
|
|
278
|
+
// SPEC-5b-2: live widget (above editor). Display-only, independent
|
|
279
279
|
// of the /fleet panel. getTheme is a live getter (EditorTheme gotcha). Disposed on session end.
|
|
280
280
|
fleetWidget = new FleetWidgetController({
|
|
281
281
|
runRegistry: deps.runRegistry,
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
// src/panel/fleet-widget.ts
|
|
2
|
-
// SPEC-5b-2 — the live widget (above editor)
|
|
2
|
+
// SPEC-5b-2 — the live widget (above editor) controller.
|
|
3
3
|
//
|
|
4
4
|
// Display-only (Q1=A): pi widgets render into a layout container; the editor keeps keyboard
|
|
5
5
|
// focus. No input is routed here — /fleet is the action surface.
|
|
6
6
|
//
|
|
7
|
+
// v0.9.2 (fix/spec-5b-2): the below-editor FleetView widget was removed — it duplicated this
|
|
8
|
+
// above-editor widget (same renderer, same rows) and the "navigable list" intent was never
|
|
9
|
+
// achievable via pi widgets. `/fleet` is the action surface; this one widget is the glance surface.
|
|
10
|
+
//
|
|
7
11
|
// Lifecycle (Q5/Q6/Q7=A): visible only while ≥1 active run exists; hidden when idle (editor
|
|
8
|
-
// reclaims
|
|
12
|
+
// reclaims the slot). A 1s setInterval re-renders for the live duration clock; it starts
|
|
9
13
|
// lazily on the first active render and clears when the fleet goes idle + on dispose.
|
|
10
14
|
//
|
|
11
15
|
// Independent of the /fleet panel: constructed at session_start in index.ts, persists whether
|
|
@@ -14,11 +18,10 @@ import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
|
14
18
|
import type { RunRegistry } from "../engine/run-registry.ts";
|
|
15
19
|
import type { BgRunsStore } from "./bg-runs-store.ts";
|
|
16
20
|
import {
|
|
17
|
-
toWidgetRun, toWidgetRunFromBg, renderWidgetLines,
|
|
21
|
+
toWidgetRun, toWidgetRunFromBg, renderWidgetLines,
|
|
18
22
|
} from "./widget-rows.ts";
|
|
19
23
|
|
|
20
24
|
const WIDGET_KEY = "fleet-active";
|
|
21
|
-
const VIEW_KEY = "fleet-view";
|
|
22
25
|
|
|
23
26
|
export interface FleetWidgetDeps {
|
|
24
27
|
runRegistry: RunRegistry;
|
|
@@ -27,7 +30,7 @@ export interface FleetWidgetDeps {
|
|
|
27
30
|
setWidget: (
|
|
28
31
|
key: string,
|
|
29
32
|
content: string[] | undefined,
|
|
30
|
-
opts?: { placement?: "aboveEditor"
|
|
33
|
+
opts?: { placement?: "aboveEditor" },
|
|
31
34
|
) => void;
|
|
32
35
|
};
|
|
33
36
|
/** Live theme getter (EditorTheme gotcha: never capture a factory theme arg). */
|
|
@@ -72,7 +75,7 @@ export class FleetWidgetController {
|
|
|
72
75
|
const hasActive = active.some((r) => r.status === "running" || r.status === "queued" || r.status === "paused");
|
|
73
76
|
if (!hasActive) {
|
|
74
77
|
this.clearTimer();
|
|
75
|
-
this.
|
|
78
|
+
this.clearWidget();
|
|
76
79
|
return;
|
|
77
80
|
}
|
|
78
81
|
this.ensureTimer();
|
|
@@ -80,14 +83,10 @@ export class FleetWidgetController {
|
|
|
80
83
|
try {
|
|
81
84
|
this.deps.ui.setWidget(WIDGET_KEY, renderWidgetLines(active, now));
|
|
82
85
|
} catch { /* best-effort: a render failure never affects runs */ }
|
|
83
|
-
try {
|
|
84
|
-
this.deps.ui.setWidget(VIEW_KEY, renderFleetViewLines(active, now), { placement: "belowEditor" });
|
|
85
|
-
} catch { /* best-effort */ }
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
private
|
|
89
|
-
try { this.deps.ui.setWidget(WIDGET_KEY,
|
|
90
|
-
try { this.deps.ui.setWidget(VIEW_KEY, content, { placement: "belowEditor" }); } catch { /* best-effort */ }
|
|
88
|
+
private clearWidget(): void {
|
|
89
|
+
try { this.deps.ui.setWidget(WIDGET_KEY, undefined); } catch { /* best-effort */ }
|
|
91
90
|
}
|
|
92
91
|
|
|
93
92
|
private ensureTimer(): void {
|
|
@@ -102,13 +101,13 @@ export class FleetWidgetController {
|
|
|
102
101
|
}
|
|
103
102
|
}
|
|
104
103
|
|
|
105
|
-
/** Unsubscribe + clear timer + clear
|
|
104
|
+
/** Unsubscribe + clear timer + clear the widget. Idempotent. */
|
|
106
105
|
dispose(): void {
|
|
107
106
|
if (this.disposed) return;
|
|
108
107
|
this.disposed = true;
|
|
109
108
|
this.clearTimer();
|
|
110
109
|
for (const u of this.unsubs) u();
|
|
111
110
|
this.unsubs.length = 0;
|
|
112
|
-
this.
|
|
111
|
+
this.clearWidget();
|
|
113
112
|
}
|
|
114
113
|
}
|
package/src/panel/widget-rows.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
// src/panel/widget-rows.ts
|
|
2
|
-
// SPEC-5b-2 — pure render functions for the live widget (above editor)
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// SPEC-5b-2 — pure render functions for the live widget (above editor).
|
|
3
|
+
// Display-only; plain strings (no theme) — theming, if wanted, is applied at the setWidget boundary.
|
|
4
|
+
// Mirrors the runs-rows/fleet-items pure-renderer convention (unit-tested, no TUI).
|
|
5
|
+
//
|
|
6
|
+
// v0.9.2 (fix/spec-5b-2): the below-editor FleetView widget was removed — it was a display-only
|
|
7
|
+
// mirror of this same renderer (same `widgetLine`, cap 8 vs 5), and the PRD §5 "navigable agent
|
|
8
|
+
// list below editor" intent was never achievable via pi widgets (editor keeps keyboard focus).
|
|
9
|
+
// `/fleet` is the navigable action surface; this one above-editor widget is the glance surface.
|
|
5
10
|
import { fmtDuration } from "./rows.ts";
|
|
6
11
|
import type { RunRecord } from "../engine/run-registry.ts";
|
|
7
12
|
import type { BgRunStatus } from "./rows.ts";
|
|
@@ -72,8 +77,3 @@ export function renderWidgetLines(runs: WidgetRun[], now: number = Date.now()):
|
|
|
72
77
|
return shown;
|
|
73
78
|
}
|
|
74
79
|
|
|
75
|
-
/** Below-editor FleetView: active-only list, cap 8, no overflow line (list form). */
|
|
76
|
-
export function renderFleetViewLines(runs: WidgetRun[], now: number = Date.now()): string[] {
|
|
77
|
-
const active = filterActive(runs);
|
|
78
|
-
return active.slice(0, 8).map((r) => widgetLine(r, now));
|
|
79
|
-
}
|
package/src/tools/subagent.ts
CHANGED
|
@@ -62,7 +62,7 @@ export function createSubagentTool(deps: SubagentToolDeps) {
|
|
|
62
62
|
"Pass track:false only for trivial throwaway lookups that don't represent real work.",
|
|
63
63
|
],
|
|
64
64
|
parameters: subagentParams,
|
|
65
|
-
async execute(_toolCallId: string, params: SubagentInput, signal: AbortSignal, _onUpdate: unknown,
|
|
65
|
+
async execute(_toolCallId: string, params: SubagentInput, signal: AbortSignal, _onUpdate: unknown, _ctx: any) {
|
|
66
66
|
// SPEC-5a: background + schedule routing (Q1/Q2/Q5).
|
|
67
67
|
if (params.background && params.schedule) {
|
|
68
68
|
return { isError: true, content: [{ type: "text" as const, text: "A scheduled run is inherently background — pass only one of `background` or `schedule`, not both." }] };
|
|
@@ -117,11 +117,6 @@ export function createSubagentTool(deps: SubagentToolDeps) {
|
|
|
117
117
|
parentCwd: deps.parentCwd,
|
|
118
118
|
runLog: deps.runLog,
|
|
119
119
|
signal,
|
|
120
|
-
onEvent: (e) => {
|
|
121
|
-
if (ctx?.ui?.setWidget && e.type === "turn_end") {
|
|
122
|
-
ctx.ui.setWidget("fleet", [`▶ ${params.agent} · running`]);
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
120
|
});
|
|
126
121
|
const isError = res.status === "failed" || res.status === "aborted";
|
|
127
122
|
return {
|