@dougbots/avenor-pi 0.10.4 → 0.12.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/dist/index.d.ts +3 -1
- package/dist/index.js +121 -9
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InspectResult, StatusResult, Supervisor, answerPermissionTool, brokerReceiveTool, brokerReplyTool, dial, eventsTool, followUpTool, inspectTool, observeRun, resultTool, shutdownTool, spawnTool, statusTool, workflowCompleteTool, workflowEventsTool, workflowGateTool, workflowInspectTool, workflowStatusTool, workflowWaitTool } from "@dougbots/avenor-core";
|
|
1
|
+
import { InspectResult, StatusResult, Supervisor, answerPermissionTool, brokerReceiveTool, brokerReplyTool, dial, eventsTool, followUpTool, inspectTool, observeRun, resultTool, shutdownTool, spawnTool, statusTool, workflowCompleteTool, workflowControllerStatusTool, workflowEventsTool, workflowGateTool, workflowInspectTool, workflowReadyTool, workflowStatusTool, workflowWaitTool } from "@dougbots/avenor-core";
|
|
2
2
|
import { EventBus, ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
|
|
4
4
|
//#region src/subagent-broker.d.ts
|
|
@@ -134,6 +134,8 @@ interface ExtensionDeps {
|
|
|
134
134
|
workflowEventsTool: typeof workflowEventsTool;
|
|
135
135
|
workflowCompleteTool: typeof workflowCompleteTool;
|
|
136
136
|
workflowGateTool: typeof workflowGateTool;
|
|
137
|
+
workflowControllerStatusTool: typeof workflowControllerStatusTool;
|
|
138
|
+
workflowReadyTool: typeof workflowReadyTool;
|
|
137
139
|
observeRun: typeof observeRun;
|
|
138
140
|
dial: typeof dial;
|
|
139
141
|
Supervisor: typeof Supervisor;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Input, Key, Text, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
-
import { Supervisor, THINKING_LEVELS, answerPermissionTool, askTool, brokerReceiveTool, brokerReplyTool, cancelTool, dial, eventsTool, extractEventText, followUpTool, inspectTool, observeRun, peersTool, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool, validateSpawnSelection, workflowCompleteTool, workflowEventsTool, workflowGateTool, workflowInspectTool, workflowStatusTool, workflowWaitTool } from "@dougbots/avenor-core";
|
|
4
|
+
import { Supervisor, THINKING_LEVELS, answerPermissionTool, askTool, brokerReceiveTool, brokerReplyTool, cancelTool, dial, eventsTool, extractEventText, followUpTool, inspectTool, observeRun, peersTool, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool, validateSpawnSelection, workflowCompleteTool, workflowControllerStatusTool, workflowEventsTool, workflowGateTool, workflowInspectTool, workflowReadyTool, workflowStatusTool, workflowWaitTool } from "@dougbots/avenor-core";
|
|
5
5
|
import * as fs from "node:fs";
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
//#region src/subagent-broker.ts
|
|
@@ -1230,6 +1230,66 @@ function shutdownLines(details, args, expanded) {
|
|
|
1230
1230
|
...paths.length > CLEANUP_ITEMS ? [itemMarker(paths.length - CLEANUP_ITEMS)] : []
|
|
1231
1231
|
];
|
|
1232
1232
|
}
|
|
1233
|
+
function controllerLeaderLine(leader) {
|
|
1234
|
+
if (!leader) return "Leader: none";
|
|
1235
|
+
const owner = leader.is_this_process === true ? "this process" : `other owner (${scalar(leader.owner_id)})`;
|
|
1236
|
+
const expires = optionalScalar(leader.expires_at);
|
|
1237
|
+
return `Leader: ${owner}${expires ? ` — lease expires ${expires}` : ""}`;
|
|
1238
|
+
}
|
|
1239
|
+
function controllerStatusLines(details, args, expanded) {
|
|
1240
|
+
const status = asRecord(details);
|
|
1241
|
+
if (!status || !stringValue(status.controller_id) || !stringValue(status.desired_state)) return void 0;
|
|
1242
|
+
const controllerId = selected(status, "controller_id", args.controller_id);
|
|
1243
|
+
const desiredState = selected(status, "desired_state");
|
|
1244
|
+
const inflight = numberValue(status.inflight) ?? 0;
|
|
1245
|
+
const maxInflight = numberValue(status.max_inflight);
|
|
1246
|
+
const blocked = asRecord(status.capacity_blocked);
|
|
1247
|
+
const blockedDetail = blocked ? optionalScalar(blocked.detail) : void 0;
|
|
1248
|
+
const lines = [`Controller: ${controllerId} — ${desiredState}`, controllerLeaderLine(asRecord(status.leader))];
|
|
1249
|
+
const candidates = numberValue(status.candidate_count);
|
|
1250
|
+
if (candidates !== void 0) lines.push(`Candidates: ${candidates} (advisory)`);
|
|
1251
|
+
lines.push(`In-flight: ${inflight}${maxInflight !== void 0 ? ` of ${maxInflight}` : ""}`);
|
|
1252
|
+
const lastReconcile = optionalScalar(status.last_reconcile);
|
|
1253
|
+
if (lastReconcile) lines.push(`Last reconcile: ${lastReconcile}`);
|
|
1254
|
+
if (blocked) lines.push(`Capacity blocked: ${selected(blocked, "source")}${blockedDetail ? ` — ${blockedDetail}` : ""}`);
|
|
1255
|
+
const nextPoll = optionalScalar(status.next_poll_at);
|
|
1256
|
+
if (nextPoll) lines.push(`Next external poll: ${nextPoll}`);
|
|
1257
|
+
lines.push(desiredState === "disabled" ? `Guidance: Run \`avenor workflow controller enable ${controllerId}\` to start dispatch and polling.` : `Guidance: Call avenor_workflow_controller_status without controller_id to review all controllers.`);
|
|
1258
|
+
if (!expanded) return lines;
|
|
1259
|
+
const supervisor = stringValue(args.supervisor_id) ? scalar(args.supervisor_id) : "singleton";
|
|
1260
|
+
return [
|
|
1261
|
+
...lines,
|
|
1262
|
+
`Supervisor: ${supervisor}`,
|
|
1263
|
+
...maxInflight !== void 0 ? [`Max in-flight: ${maxInflight}`] : [],
|
|
1264
|
+
...optionalNumber(status.revision) !== void 0 ? [`Revision: ${optionalNumber(status.revision)}`] : [],
|
|
1265
|
+
...optionalNumber(status.owner_epoch) !== void 0 ? [`Owner epoch: ${optionalNumber(status.owner_epoch)}`] : []
|
|
1266
|
+
];
|
|
1267
|
+
}
|
|
1268
|
+
function controllerListLines(details, args, expanded) {
|
|
1269
|
+
const result = asRecord(details);
|
|
1270
|
+
if (!result || !Array.isArray(result.controllers) || result.controllers.some((controller) => !asRecord(controller))) return void 0;
|
|
1271
|
+
const controllers = result.controllers.map((controller) => controller);
|
|
1272
|
+
stringValue(args.supervisor_id) && scalar(args.supervisor_id);
|
|
1273
|
+
if (controllers.length === 0) return ["No workflow controllers configured."];
|
|
1274
|
+
const displayed = controllers.slice(0, STATUS_ROWS);
|
|
1275
|
+
const lines = [
|
|
1276
|
+
`Workflow controllers — ${controllers.length}`,
|
|
1277
|
+
...displayed.map(controllerListRow),
|
|
1278
|
+
...controllers.length > displayed.length ? [itemMarker(controllers.length - displayed.length)] : []
|
|
1279
|
+
];
|
|
1280
|
+
if (!expanded) return lines;
|
|
1281
|
+
for (const controller of displayed) {
|
|
1282
|
+
const leader = asRecord(controller.leader);
|
|
1283
|
+
const ownerEpoch = leader ? numberValue(leader.owner_epoch) : void 0;
|
|
1284
|
+
lines.push(`${selected(controller, "controller_id")}:`, controllerLeaderLine(leader), ...ownerEpoch !== void 0 ? [`Owner epoch: ${ownerEpoch}`] : []);
|
|
1285
|
+
}
|
|
1286
|
+
return lines;
|
|
1287
|
+
}
|
|
1288
|
+
function controllerListRow(controller) {
|
|
1289
|
+
const leader = asRecord(controller.leader);
|
|
1290
|
+
const owner = leader ? leader.is_this_process === true ? "this process" : `other owner (${scalar(leader.owner_id)})` : "none";
|
|
1291
|
+
return `${selected(controller, "controller_id")} — ${selected(controller, "desired_state")} — leader: ${owner}`;
|
|
1292
|
+
}
|
|
1233
1293
|
function render(tool, result, options, theme, build) {
|
|
1234
1294
|
try {
|
|
1235
1295
|
return linesText(options.isPartial ? partial(result) : build() ?? fallback(tool), theme);
|
|
@@ -1322,6 +1382,15 @@ function renderEventsResult(result, options, theme, args) {
|
|
|
1322
1382
|
function renderShutdownResult(result, options, theme, args) {
|
|
1323
1383
|
return render("shutdown", result, options, theme, () => shutdownLines(result.details, args, options.expanded));
|
|
1324
1384
|
}
|
|
1385
|
+
function renderWorkflowControllerStatusCall(args, theme) {
|
|
1386
|
+
const pieces = stringValue(args.controller_id) ? [`avenor_workflow_controller_status controller_id ${quoted(args.controller_id)}`] : ["avenor_workflow_controller_status (all controllers)"];
|
|
1387
|
+
const supervisor = optionalQuoted(args.supervisor_id);
|
|
1388
|
+
if (supervisor) pieces.push(`supervisor_id ${supervisor}`);
|
|
1389
|
+
return linesText([pieces.join(" ")], theme);
|
|
1390
|
+
}
|
|
1391
|
+
function renderWorkflowControllerStatusResult(result, options, theme, args) {
|
|
1392
|
+
return render("workflow controller status", result, options, theme, () => controllerStatusLines(result.details, args, options.expanded) ?? controllerListLines(result.details, args, options.expanded));
|
|
1393
|
+
}
|
|
1325
1394
|
//#endregion
|
|
1326
1395
|
//#region src/index.ts
|
|
1327
1396
|
const POLL_INTERVAL_MS = 3e3;
|
|
@@ -1349,6 +1418,8 @@ const defaultDeps = {
|
|
|
1349
1418
|
workflowEventsTool,
|
|
1350
1419
|
workflowCompleteTool,
|
|
1351
1420
|
workflowGateTool,
|
|
1421
|
+
workflowControllerStatusTool,
|
|
1422
|
+
workflowReadyTool,
|
|
1352
1423
|
observeRun,
|
|
1353
1424
|
dial,
|
|
1354
1425
|
Supervisor
|
|
@@ -2077,12 +2148,14 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2077
2148
|
promptSnippet: "Dispatch sub-agent runs via avenor (spawn, status, result, inspect, events, follow-up, shutdown)",
|
|
2078
2149
|
promptGuidelines: [
|
|
2079
2150
|
"Use avenor_spawn to delegate well-defined tasks to sub-agents.",
|
|
2080
|
-
"Use avenor_status
|
|
2081
|
-
"Use avenor_result to
|
|
2151
|
+
"Use avenor_status (view \"lifecycle\") only to check progress or pending permissions.",
|
|
2152
|
+
"Use avenor_result to retrieve the final result of a sub-agent; it waits by default, but set wait=false for runs that already completed.",
|
|
2082
2153
|
"Use avenor_inspect to review a bounded transcript and tool activity from a run.",
|
|
2083
|
-
"Use avenor_events only when raw event payloads
|
|
2154
|
+
"Use avenor_events only when you specifically need raw event payloads.",
|
|
2084
2155
|
"Use avenor_follow_up to iterate on a completed run.",
|
|
2085
|
-
"Use avenor_shutdown when done delegating work."
|
|
2156
|
+
"Use avenor_shutdown when done delegating work.",
|
|
2157
|
+
"When roster_entry is set, the roster selects agent, model, and backend; do not pass those fields alongside it.",
|
|
2158
|
+
"If a roster spawn fails validation or resolution, do not retry the roster: respawn without roster_file/roster_entry. Direct selectors (agent, model, backend) are optional; omission uses runtime defaults."
|
|
2086
2159
|
],
|
|
2087
2160
|
parameters: Type.Object({
|
|
2088
2161
|
agent: Type.Optional(Type.String({ description: "Optional agent name; omission uses the supplied model or runtime defaults" })),
|
|
@@ -2103,7 +2176,7 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2103
2176
|
], { description: "Thinking level; omission uses the backend default" })),
|
|
2104
2177
|
backend: Type.Optional(Type.String({ description: "Backend override (defaults to pi for direct mode)" })),
|
|
2105
2178
|
roster_file: Type.Optional(Type.String({ description: "Path to the roster map" })),
|
|
2106
|
-
roster_entry: Type.Optional(Type.String({ description: "Roster entry to select" })),
|
|
2179
|
+
roster_entry: Type.Optional(Type.String({ description: "Roster entry to select. Requires roster_file unless a roster context is configured; disables agent, model, and backend" })),
|
|
2107
2180
|
server_url: Type.Optional(Type.String({ description: "Backend server URL" })),
|
|
2108
2181
|
supervisor_id: Type.Optional(Type.String({ description: "Reuse an existing supervisor by socket path" })),
|
|
2109
2182
|
wait: Type.Optional(Type.Boolean({ description: "Block until complete (default true)" }))
|
|
@@ -2331,10 +2404,10 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2331
2404
|
pi.registerTool({
|
|
2332
2405
|
name: "avenor_result",
|
|
2333
2406
|
label: "Avenor Result",
|
|
2334
|
-
description: "
|
|
2407
|
+
description: "Retrieve the complete final output of a run without transcript or event details. Waits by default; set wait=false to retrieve an already-completed run without blocking.",
|
|
2335
2408
|
parameters: Type.Object({
|
|
2336
|
-
run_id: Type.String({ description: "Run ID
|
|
2337
|
-
wait: Type.Optional(Type.Boolean({ description: "Wait for a terminal result (default true)" })),
|
|
2409
|
+
run_id: Type.String({ description: "Run ID or label" }),
|
|
2410
|
+
wait: Type.Optional(Type.Boolean({ description: "Wait for a terminal result (default true); false retrieves the current state without blocking" })),
|
|
2338
2411
|
timeout: Type.Optional(Type.String({ description: "Maximum time to wait (e.g. 30s, 5m)" })),
|
|
2339
2412
|
supervisor_id: Type.Optional(Type.String({ description: "Reuse an existing supervisor by socket path" }))
|
|
2340
2413
|
}),
|
|
@@ -2883,6 +2956,45 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2883
2956
|
};
|
|
2884
2957
|
}
|
|
2885
2958
|
});
|
|
2959
|
+
pi.registerTool({
|
|
2960
|
+
name: "avenor_workflow_controller_status",
|
|
2961
|
+
label: "Avenor Workflow Controller Status",
|
|
2962
|
+
description: "Show workflow controller status. With controller_id, returns that controller's full status; without it, lists all controllers with summary state. Create, enable, and disable are CLI-only (avenor workflow controller ...).",
|
|
2963
|
+
parameters: Type.Object({
|
|
2964
|
+
controller_id: Type.Optional(Type.String({ description: "Controller ID; omit to list all controllers" })),
|
|
2965
|
+
supervisor_id: Type.Optional(Type.String({ description: "Supervisor ID for multi-supervisor mode" }))
|
|
2966
|
+
}),
|
|
2967
|
+
async execute(_toolCallId, params) {
|
|
2968
|
+
const result = await deps.workflowControllerStatusTool({
|
|
2969
|
+
controllerId: params.controller_id,
|
|
2970
|
+
supervisorId: params.supervisor_id
|
|
2971
|
+
});
|
|
2972
|
+
const details = { ...result };
|
|
2973
|
+
if (params.controller_id) try {
|
|
2974
|
+
const ready = await deps.workflowReadyTool({
|
|
2975
|
+
controllerId: params.controller_id,
|
|
2976
|
+
supervisorId: params.supervisor_id
|
|
2977
|
+
});
|
|
2978
|
+
if (Array.isArray(ready.candidates)) details.candidate_count = ready.candidates.length;
|
|
2979
|
+
} catch {}
|
|
2980
|
+
return {
|
|
2981
|
+
content: [{
|
|
2982
|
+
type: "text",
|
|
2983
|
+
text: JSON.stringify(result, null, 2)
|
|
2984
|
+
}],
|
|
2985
|
+
details
|
|
2986
|
+
};
|
|
2987
|
+
},
|
|
2988
|
+
renderCall(args, theme, context) {
|
|
2989
|
+
return renderWorkflowControllerStatusCall(context.args, theme);
|
|
2990
|
+
},
|
|
2991
|
+
renderResult(result, { expanded, isPartial }, theme, context) {
|
|
2992
|
+
return renderWorkflowControllerStatusResult(result, {
|
|
2993
|
+
expanded,
|
|
2994
|
+
isPartial
|
|
2995
|
+
}, theme, context.args);
|
|
2996
|
+
}
|
|
2997
|
+
});
|
|
2886
2998
|
pi.registerCommand("avenor-status", {
|
|
2887
2999
|
description: "Show status of all avenor runs",
|
|
2888
3000
|
handler: async (_args, ctx) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dougbots/avenor-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dev": "tsdown --watch"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@dougbots/avenor-core": "^0.
|
|
38
|
+
"@dougbots/avenor-core": "^0.12.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@earendil-works/pi-coding-agent": ">=0.74.0",
|