@dougbots/avenor-pi 0.4.0 → 0.5.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/README.md +11 -7
- package/dist/index.d.ts +2 -1
- package/dist/index.js +96 -19
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -112,7 +112,9 @@ Available tools for LLM sub-agent management:
|
|
|
112
112
|
| Tool | Description |
|
|
113
113
|
|---|---|
|
|
114
114
|
| `avenor_spawn` | Dispatch an agent run (blocking or fire-and-forget). Accepts `agent` (optional, maps to a named profile in `agents.json`), `model`, `prompt`, and `dir`. |
|
|
115
|
-
| `avenor_status` | Get status of a run or all runs |
|
|
115
|
+
| `avenor_status` | Get status of a run or all runs; use `view="lifecycle"` for compact progress and permission checks |
|
|
116
|
+
| `avenor_result` | Wait for a run and return its bounded final output without transcript details |
|
|
117
|
+
| `avenor_inspect` | Review a bounded transcript, tool activity, permissions, and final output |
|
|
116
118
|
| `avenor_answer_permission` | Answer a pending permission request |
|
|
117
119
|
| `avenor_follow_up` | Resume a completed run with a follow-up message |
|
|
118
120
|
| `avenor_events` | Read events from a run |
|
|
@@ -154,18 +156,20 @@ If you don't need a named profile, `model` alone is sufficient — no agent conf
|
|
|
154
156
|
**Blocking (default):**
|
|
155
157
|
```
|
|
156
158
|
1. avenor_spawn → tool call shows live progress, blocks until done
|
|
157
|
-
2. tool call returns →
|
|
158
|
-
3.
|
|
159
|
-
4.
|
|
160
|
-
5.
|
|
159
|
+
2. tool call returns → completion preview with status + session_id
|
|
160
|
+
3. avenor_result → retrieve the bounded final output when needed
|
|
161
|
+
4. avenor_inspect → inspect transcript and tool details when needed
|
|
162
|
+
5. avenor_follow_up → optionally iterate
|
|
163
|
+
6. avenor_shutdown → clean up when finished
|
|
161
164
|
```
|
|
162
165
|
|
|
163
166
|
**Parallel / fire-and-forget (`wait=false`):**
|
|
164
167
|
```
|
|
165
168
|
1. avenor_spawn × N → each returns run_id immediately
|
|
166
169
|
2. (status widget updates) → persistent widget shows all active runs
|
|
167
|
-
3.
|
|
168
|
-
4.
|
|
170
|
+
3. avenor_status → optional compact lifecycle/permission check
|
|
171
|
+
4. avenor_result → wait for and retrieve each final output
|
|
172
|
+
5. /avenor-watch <id> → open live diagnostics for a specific run
|
|
169
173
|
```
|
|
170
174
|
|
|
171
175
|
## Dependencies
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InspectResult, StatusResult, Supervisor, answerPermissionTool, dial, eventsTool, followUpTool, inspectTool, observeRun, shutdownTool, spawnTool, statusTool } from "@dougbots/avenor-core";
|
|
1
|
+
import { InspectResult, StatusResult, Supervisor, answerPermissionTool, dial, eventsTool, followUpTool, inspectTool, observeRun, resultTool, shutdownTool, spawnTool, statusTool } from "@dougbots/avenor-core";
|
|
2
2
|
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
|
|
4
4
|
//#region src/types.d.ts
|
|
@@ -21,6 +21,7 @@ interface ExtensionDeps {
|
|
|
21
21
|
answerPermissionTool: typeof answerPermissionTool;
|
|
22
22
|
followUpTool: typeof followUpTool;
|
|
23
23
|
inspectTool: typeof inspectTool;
|
|
24
|
+
resultTool: typeof resultTool;
|
|
24
25
|
shutdownTool: typeof shutdownTool;
|
|
25
26
|
observeRun: typeof observeRun;
|
|
26
27
|
dial: typeof dial;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Input, Key, Text, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
|
-
import { Supervisor, answerPermissionTool, dial, eventsTool, followUpTool, inspectTool, observeRun, shutdownTool, spawnTool, statusTool } from "@dougbots/avenor-core";
|
|
3
|
+
import { Supervisor, answerPermissionTool, dial, eventsTool, followUpTool, inspectTool, observeRun, resultTool, shutdownTool, spawnTool, statusTool } from "@dougbots/avenor-core";
|
|
4
4
|
//#region src/watch.ts
|
|
5
5
|
const ANSI_PATTERN = /[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[a-zA-Z\d]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
|
|
6
6
|
const TRANSCRIPT_SCROLL_STEP = 4;
|
|
@@ -469,22 +469,44 @@ var RunInspectorOverlay = class {
|
|
|
469
469
|
return lines.map((line) => truncateToWidth(line, innerWidth));
|
|
470
470
|
}
|
|
471
471
|
};
|
|
472
|
+
function matchingWatchRuns(runs, selector) {
|
|
473
|
+
const query = selector.trim().toLowerCase();
|
|
474
|
+
if (!query) return [];
|
|
475
|
+
const byId = runs.find((run) => run.runId.toLowerCase() === query);
|
|
476
|
+
if (byId) return [byId];
|
|
477
|
+
return runs.filter((run) => run.label?.toLowerCase() === query || run.agent?.toLowerCase() === query);
|
|
478
|
+
}
|
|
479
|
+
function watchRunChoice(run) {
|
|
480
|
+
return `${sanitizeText(run.label ?? run.runId)}${run.agent ? ` · ${sanitizeText(run.agent)}` : ""} · ${sanitizeText(run.runId).slice(0, 8)}`;
|
|
481
|
+
}
|
|
482
|
+
async function selectWatchRun(ctx, runs) {
|
|
483
|
+
const choices = runs.map(watchRunChoice);
|
|
484
|
+
const selected = await ctx.ui.select("Watch run:", choices);
|
|
485
|
+
if (!selected) return void 0;
|
|
486
|
+
return runs.find((_run, index) => choices[index] === selected);
|
|
487
|
+
}
|
|
472
488
|
async function openRunInspector(ctx, options) {
|
|
473
489
|
if (!ctx.hasUI) {
|
|
474
490
|
ctx.ui.notify("avenor-watch requires TUI mode", "warning");
|
|
475
491
|
return;
|
|
476
492
|
}
|
|
477
|
-
|
|
478
|
-
|
|
493
|
+
const selector = options.initialRunId?.trim();
|
|
494
|
+
let tracked;
|
|
495
|
+
let runId;
|
|
496
|
+
if (selector) {
|
|
497
|
+
const matches = matchingWatchRuns(options.trackedRuns, selector);
|
|
498
|
+
tracked = matches.length === 1 ? matches[0] : matches.length > 1 ? await selectWatchRun(ctx, matches) : void 0;
|
|
499
|
+
if (matches.length > 1 && !tracked) return;
|
|
500
|
+
runId = tracked?.runId ?? selector;
|
|
501
|
+
} else {
|
|
479
502
|
if (options.trackedRuns.length === 0) {
|
|
480
503
|
ctx.ui.notify("No tracked runs", "warning");
|
|
481
504
|
return;
|
|
482
505
|
}
|
|
483
|
-
|
|
484
|
-
if (!
|
|
485
|
-
runId =
|
|
506
|
+
tracked = await selectWatchRun(ctx, options.trackedRuns);
|
|
507
|
+
if (!tracked) return;
|
|
508
|
+
runId = tracked.runId;
|
|
486
509
|
}
|
|
487
|
-
const tracked = options.trackedRuns.find((run) => run.runId === runId);
|
|
488
510
|
await ctx.ui.custom((tui, theme, keybindings, done) => new RunInspectorOverlay(tui, theme, keybindings, options.deps, tracked ?? { runId }, () => done(), options.onTrackRun), {
|
|
489
511
|
overlay: true,
|
|
490
512
|
overlayOptions: {
|
|
@@ -497,6 +519,15 @@ async function openRunInspector(ctx, options) {
|
|
|
497
519
|
}
|
|
498
520
|
//#endregion
|
|
499
521
|
//#region src/types.ts
|
|
522
|
+
function findLiveStatusForTrackedRun(run, liveStatuses) {
|
|
523
|
+
const knownRunIds = new Set([run.runId, run.lastStatus?.run_id].filter((id) => !!id));
|
|
524
|
+
let runtimeMatch;
|
|
525
|
+
for (const status of liveStatuses) {
|
|
526
|
+
if (knownRunIds.has(status.run_id)) return status;
|
|
527
|
+
if (!run.supervisorId && run.runtimeId === status.runtime_id) runtimeMatch = status;
|
|
528
|
+
}
|
|
529
|
+
return runtimeMatch;
|
|
530
|
+
}
|
|
500
531
|
const TERMINAL_STATUSES = new Set([
|
|
501
532
|
"done",
|
|
502
533
|
"failed",
|
|
@@ -537,6 +568,7 @@ const defaultDeps = {
|
|
|
537
568
|
answerPermissionTool,
|
|
538
569
|
followUpTool,
|
|
539
570
|
inspectTool,
|
|
571
|
+
resultTool,
|
|
540
572
|
shutdownTool,
|
|
541
573
|
observeRun,
|
|
542
574
|
dial,
|
|
@@ -699,7 +731,7 @@ function createExtension(deps = defaultDeps) {
|
|
|
699
731
|
}
|
|
700
732
|
const entries = [];
|
|
701
733
|
for (const run of trackedRuns.values()) {
|
|
702
|
-
const live = liveMap.
|
|
734
|
+
const live = findLiveStatusForTrackedRun(run, liveMap.values());
|
|
703
735
|
if (live) {
|
|
704
736
|
liveMap.delete(live.run_id);
|
|
705
737
|
run.lastStatus = live;
|
|
@@ -1070,11 +1102,12 @@ function createExtension(deps = defaultDeps) {
|
|
|
1070
1102
|
name: "avenor_spawn",
|
|
1071
1103
|
label: "Avenor Spawn",
|
|
1072
1104
|
description: "Dispatch an agent run via avenor. Blocks by default, showing live progress. Set wait=false for fire-and-forget.",
|
|
1073
|
-
promptSnippet: "Dispatch sub-agent runs via avenor (spawn, status, inspect, events, follow-up, shutdown)",
|
|
1105
|
+
promptSnippet: "Dispatch sub-agent runs via avenor (spawn, status, result, inspect, events, follow-up, shutdown)",
|
|
1074
1106
|
promptGuidelines: [
|
|
1075
1107
|
"Use avenor_spawn to delegate well-defined tasks to sub-agents.",
|
|
1076
|
-
"Use avenor_status to check progress
|
|
1077
|
-
"Use
|
|
1108
|
+
"Use avenor_status with view \"lifecycle\" only to check progress or pending permissions.",
|
|
1109
|
+
"Use avenor_result to wait for and retrieve a sub-agent final result.",
|
|
1110
|
+
"Use avenor_inspect to review a bounded transcript and tool activity from a run.",
|
|
1078
1111
|
"Use avenor_events only when raw event payloads are specifically needed.",
|
|
1079
1112
|
"Use avenor_follow_up to iterate on a completed run.",
|
|
1080
1113
|
"Use avenor_shutdown when done delegating work."
|
|
@@ -1125,7 +1158,7 @@ function createExtension(deps = defaultDeps) {
|
|
|
1125
1158
|
if (!wait) return {
|
|
1126
1159
|
content: [{
|
|
1127
1160
|
type: "text",
|
|
1128
|
-
text: `Dispatched "${label}" (run_id: ${result.run_id}).
|
|
1161
|
+
text: `Dispatched "${label}" (run_id: ${result.run_id}). Completion will be delivered automatically; use avenor_status with view "lifecycle" for progress or avenor_result to wait for the final result.`
|
|
1129
1162
|
}],
|
|
1130
1163
|
details: {
|
|
1131
1164
|
run_id: result.run_id,
|
|
@@ -1242,15 +1275,21 @@ function createExtension(deps = defaultDeps) {
|
|
|
1242
1275
|
pi.registerTool({
|
|
1243
1276
|
name: "avenor_status",
|
|
1244
1277
|
label: "Avenor Status",
|
|
1245
|
-
description: "Get status of a run or all runs.
|
|
1278
|
+
description: "Get lifecycle status of a run or all runs. Use view=\"lifecycle\" for compact polling; full is the compatibility default.",
|
|
1246
1279
|
parameters: Type.Object({
|
|
1247
1280
|
run_id: Type.Optional(Type.String({ description: "Specific run ID to query" })),
|
|
1281
|
+
view: Type.Optional(Type.Unsafe({
|
|
1282
|
+
type: "string",
|
|
1283
|
+
enum: ["lifecycle", "full"],
|
|
1284
|
+
description: "Response detail (default full for compatibility)"
|
|
1285
|
+
})),
|
|
1248
1286
|
supervisor_id: Type.Optional(Type.String({ description: "Reuse an existing supervisor by socket path" }))
|
|
1249
1287
|
}),
|
|
1250
1288
|
async execute(_toolCallId, params) {
|
|
1251
1289
|
const result = await deps.statusTool({
|
|
1252
1290
|
runId: params.run_id,
|
|
1253
|
-
supervisorId: params.supervisor_id
|
|
1291
|
+
supervisorId: params.supervisor_id,
|
|
1292
|
+
view: params.view
|
|
1254
1293
|
});
|
|
1255
1294
|
return {
|
|
1256
1295
|
content: [{
|
|
@@ -1261,6 +1300,44 @@ function createExtension(deps = defaultDeps) {
|
|
|
1261
1300
|
};
|
|
1262
1301
|
}
|
|
1263
1302
|
});
|
|
1303
|
+
pi.registerTool({
|
|
1304
|
+
name: "avenor_result",
|
|
1305
|
+
label: "Avenor Result",
|
|
1306
|
+
description: "Wait for a run to finish and return its bounded final output without transcript or event details.",
|
|
1307
|
+
parameters: Type.Object({
|
|
1308
|
+
run_id: Type.String({ description: "Run ID to await" }),
|
|
1309
|
+
wait: Type.Optional(Type.Boolean({ description: "Wait for a terminal result (default true)" })),
|
|
1310
|
+
timeout: Type.Optional(Type.String({ description: "Maximum time to wait (e.g. 30s, 5m)" })),
|
|
1311
|
+
supervisor_id: Type.Optional(Type.String({ description: "Reuse an existing supervisor by socket path" }))
|
|
1312
|
+
}),
|
|
1313
|
+
async execute(_toolCallId, params, signal) {
|
|
1314
|
+
const tracked = trackedRuns.get(params.run_id);
|
|
1315
|
+
const previousBlocking = tracked?.blocking;
|
|
1316
|
+
if (tracked) tracked.blocking = true;
|
|
1317
|
+
let completed = false;
|
|
1318
|
+
try {
|
|
1319
|
+
const result = await deps.resultTool({
|
|
1320
|
+
runId: params.run_id,
|
|
1321
|
+
supervisorId: params.supervisor_id,
|
|
1322
|
+
wait: params.wait,
|
|
1323
|
+
timeout: params.timeout,
|
|
1324
|
+
signal
|
|
1325
|
+
});
|
|
1326
|
+
completed = result.ready;
|
|
1327
|
+
if (completed) trackedRuns.delete(params.run_id);
|
|
1328
|
+
else if (tracked && result.status === "waiting") tracked.permissionNotified = true;
|
|
1329
|
+
return {
|
|
1330
|
+
content: [{
|
|
1331
|
+
type: "text",
|
|
1332
|
+
text: JSON.stringify(result, null, 2)
|
|
1333
|
+
}],
|
|
1334
|
+
details: result
|
|
1335
|
+
};
|
|
1336
|
+
} finally {
|
|
1337
|
+
if (!completed && tracked && trackedRuns.get(params.run_id) === tracked) tracked.blocking = previousBlocking ?? false;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1264
1341
|
pi.registerTool({
|
|
1265
1342
|
name: "avenor_inspect",
|
|
1266
1343
|
label: "Avenor Inspect",
|
|
@@ -1422,11 +1499,11 @@ function createExtension(deps = defaultDeps) {
|
|
|
1422
1499
|
}
|
|
1423
1500
|
});
|
|
1424
1501
|
pi.registerCommand("avenor-watch", {
|
|
1425
|
-
description: "Open a live run inspector
|
|
1502
|
+
description: "Open a live run inspector by run ID, label, or agent name",
|
|
1426
1503
|
getArgumentCompletions: (prefix) => {
|
|
1427
|
-
const items = Array.from(trackedRuns.values()).filter((run) => run.label.startsWith(prefix) || run.runId.startsWith(prefix)).map((run) => ({
|
|
1504
|
+
const items = Array.from(trackedRuns.values()).filter((run) => run.label.startsWith(prefix) || run.agent.startsWith(prefix) || run.runId.startsWith(prefix)).map((run) => ({
|
|
1428
1505
|
value: run.runId,
|
|
1429
|
-
label: `${run.label} (${run.runId.slice(0, 8)})`
|
|
1506
|
+
label: `${sanitizeText(run.label)} (${sanitizeText(run.agent)}, ${sanitizeText(run.runId).slice(0, 8)})`
|
|
1430
1507
|
}));
|
|
1431
1508
|
return items.length > 0 ? items : null;
|
|
1432
1509
|
},
|
|
@@ -1448,9 +1525,9 @@ function createExtension(deps = defaultDeps) {
|
|
|
1448
1525
|
pi.registerCommand("avenor-cancel", {
|
|
1449
1526
|
description: "Cancel a running avenor sub-agent",
|
|
1450
1527
|
getArgumentCompletions: (prefix) => {
|
|
1451
|
-
const items = Array.from(trackedRuns.values()).filter((run) => run.label.startsWith(prefix) || run.runId.startsWith(prefix)).map((run) => ({
|
|
1528
|
+
const items = Array.from(trackedRuns.values()).filter((run) => run.label.startsWith(prefix) || run.agent.startsWith(prefix) || run.runId.startsWith(prefix)).map((run) => ({
|
|
1452
1529
|
value: run.runId,
|
|
1453
|
-
label: `${run.label} (${run.runId.slice(0, 8)})`
|
|
1530
|
+
label: `${sanitizeText(run.label)} (${sanitizeText(run.agent)}, ${sanitizeText(run.runId).slice(0, 8)})`
|
|
1454
1531
|
}));
|
|
1455
1532
|
return items.length > 0 ? items : null;
|
|
1456
1533
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dougbots/avenor-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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.5.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@earendil-works/pi-coding-agent": "*",
|