@dougbots/avenor-pi 0.12.0 → 0.13.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 +2 -1
- package/dist/index.js +213 -45
- 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, workflowControllerStatusTool, workflowEventsTool, workflowGateTool, workflowInspectTool, workflowReadyTool, workflowStatusTool, workflowWaitTool } from "@dougbots/avenor-core";
|
|
1
|
+
import { InspectResult, StatusResult, Supervisor, answerPermissionTool, brokerReceiveTool, brokerReplyTool, dial, eventsTool, followUpTool, inspectTool, killTool, 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
|
|
@@ -128,6 +128,7 @@ interface ExtensionDeps {
|
|
|
128
128
|
brokerReceiveTool: typeof brokerReceiveTool;
|
|
129
129
|
brokerReplyTool: typeof brokerReplyTool;
|
|
130
130
|
shutdownTool: typeof shutdownTool;
|
|
131
|
+
killTool: typeof killTool;
|
|
131
132
|
workflowStatusTool: typeof workflowStatusTool;
|
|
132
133
|
workflowWaitTool: typeof workflowWaitTool;
|
|
133
134
|
workflowInspectTool: typeof workflowInspectTool;
|
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, workflowControllerStatusTool, workflowEventsTool, workflowGateTool, workflowInspectTool, workflowReadyTool, workflowStatusTool, workflowWaitTool } from "@dougbots/avenor-core";
|
|
4
|
+
import { Supervisor, THINKING_LEVELS, answerPermissionTool, askTool, brokerReceiveTool, brokerReplyTool, cancelTool, dial, eventsTool, extractEventText, followUpTool, inspectTool, killTool, 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
|
|
@@ -1393,6 +1393,9 @@ function renderWorkflowControllerStatusResult(result, options, theme, args) {
|
|
|
1393
1393
|
}
|
|
1394
1394
|
//#endregion
|
|
1395
1395
|
//#region src/index.ts
|
|
1396
|
+
function asStatusResult(raw) {
|
|
1397
|
+
return raw;
|
|
1398
|
+
}
|
|
1396
1399
|
const POLL_INTERVAL_MS = 3e3;
|
|
1397
1400
|
const INSPECT_LIMIT = 128;
|
|
1398
1401
|
const COMPLETION_OUTPUT_CHARS = 600;
|
|
@@ -1412,6 +1415,7 @@ const defaultDeps = {
|
|
|
1412
1415
|
brokerReceiveTool,
|
|
1413
1416
|
brokerReplyTool,
|
|
1414
1417
|
shutdownTool,
|
|
1418
|
+
killTool,
|
|
1415
1419
|
workflowStatusTool,
|
|
1416
1420
|
workflowWaitTool,
|
|
1417
1421
|
workflowInspectTool,
|
|
@@ -1597,6 +1601,88 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
1597
1601
|
let pollingErrorCount = 0;
|
|
1598
1602
|
const pollingErrors = [];
|
|
1599
1603
|
let lastStatusEntries = [];
|
|
1604
|
+
const pendingWaits = /* @__PURE__ */ new Set();
|
|
1605
|
+
function interruptPendingWaits(stop, excludeRunKey) {
|
|
1606
|
+
for (const wait of [...pendingWaits]) {
|
|
1607
|
+
if (wait.runKey === excludeRunKey) continue;
|
|
1608
|
+
pendingWaits.delete(wait);
|
|
1609
|
+
wait.interrupt(stop);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
function registerPendingWait(runKey, interrupt) {
|
|
1613
|
+
const wait = {
|
|
1614
|
+
runKey,
|
|
1615
|
+
interrupt
|
|
1616
|
+
};
|
|
1617
|
+
pendingWaits.add(wait);
|
|
1618
|
+
return wait;
|
|
1619
|
+
}
|
|
1620
|
+
function createInterruptibleWait(runKey, signal) {
|
|
1621
|
+
const controller = new AbortController();
|
|
1622
|
+
let stopped;
|
|
1623
|
+
const onSignalAbort = () => controller.abort();
|
|
1624
|
+
if (signal?.aborted) controller.abort();
|
|
1625
|
+
else signal?.addEventListener("abort", onSignalAbort);
|
|
1626
|
+
const pending = registerPendingWait(runKey, (stop) => {
|
|
1627
|
+
if (controller.signal.aborted) return;
|
|
1628
|
+
stopped = stop;
|
|
1629
|
+
controller.abort();
|
|
1630
|
+
});
|
|
1631
|
+
return {
|
|
1632
|
+
signal: controller.signal,
|
|
1633
|
+
get stopped() {
|
|
1634
|
+
return stopped;
|
|
1635
|
+
},
|
|
1636
|
+
dispose() {
|
|
1637
|
+
pendingWaits.delete(pending);
|
|
1638
|
+
signal?.removeEventListener("abort", onSignalAbort);
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
/** Build the early return for a wait interrupted by a sibling stopping
|
|
1643
|
+
* point. When the awaited run itself has since reached a stopping point
|
|
1644
|
+
* (possible when the caller omitted supervisor_id and the registry key
|
|
1645
|
+
* diverged), deliver its real state instead of an interruption notice. */
|
|
1646
|
+
async function resultAfterInterrupt(params, stop, tracked) {
|
|
1647
|
+
const runId = tracked?.runId ?? params.run_id;
|
|
1648
|
+
const supervisorId = tracked?.supervisorId ?? params.supervisor_id;
|
|
1649
|
+
let current;
|
|
1650
|
+
try {
|
|
1651
|
+
current = asStatusResult(await deps.statusTool({
|
|
1652
|
+
runId,
|
|
1653
|
+
supervisorId,
|
|
1654
|
+
view: "full"
|
|
1655
|
+
}));
|
|
1656
|
+
} catch {}
|
|
1657
|
+
if (current !== void 0 && (isTerminalStatus(current.status) || current.status === "waiting")) try {
|
|
1658
|
+
return {
|
|
1659
|
+
...await deps.resultTool({
|
|
1660
|
+
runId,
|
|
1661
|
+
supervisorId,
|
|
1662
|
+
wait: true,
|
|
1663
|
+
timeout: params.timeout,
|
|
1664
|
+
signal: params.signal
|
|
1665
|
+
}),
|
|
1666
|
+
interrupted_by: {
|
|
1667
|
+
run_id: stop.runId,
|
|
1668
|
+
label: stop.label,
|
|
1669
|
+
status: stop.status
|
|
1670
|
+
}
|
|
1671
|
+
};
|
|
1672
|
+
} catch {}
|
|
1673
|
+
return {
|
|
1674
|
+
run_id: current?.run_id ?? runId,
|
|
1675
|
+
label: current?.label ?? runId,
|
|
1676
|
+
status: current?.status ?? "running",
|
|
1677
|
+
ready: current !== void 0 && isTerminalStatus(current.status),
|
|
1678
|
+
...current?.pending_permission && { pending_permission: current.pending_permission },
|
|
1679
|
+
interrupted_by: {
|
|
1680
|
+
run_id: stop.runId,
|
|
1681
|
+
label: stop.label,
|
|
1682
|
+
status: stop.status
|
|
1683
|
+
}
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1600
1686
|
const subAgentEnv = options.subAgentEnv !== void 0 ? options.subAgentEnv : readSubAgentEnv();
|
|
1601
1687
|
const isSubAgentBrokerMode = subAgentEnv !== null;
|
|
1602
1688
|
let subAgentPollTimer = null;
|
|
@@ -1714,7 +1800,7 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
1714
1800
|
const liveMap = /* @__PURE__ */ new Map();
|
|
1715
1801
|
try {
|
|
1716
1802
|
const allLive = await deps.statusTool({});
|
|
1717
|
-
const liveList =
|
|
1803
|
+
const liveList = "runs" in allLive ? allLive.runs : [allLive];
|
|
1718
1804
|
for (const result of liveList) liveMap.set(result.run_id, result);
|
|
1719
1805
|
} catch (err) {
|
|
1720
1806
|
recordPollingError("singleton-list", "failed to list singleton runs", err);
|
|
@@ -1744,11 +1830,10 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
1744
1830
|
continue;
|
|
1745
1831
|
}
|
|
1746
1832
|
try {
|
|
1747
|
-
const
|
|
1833
|
+
const result = asStatusResult(await deps.statusTool({
|
|
1748
1834
|
runId: run.runId,
|
|
1749
1835
|
supervisorId: run.supervisorId
|
|
1750
|
-
});
|
|
1751
|
-
const result = Array.isArray(raw) ? raw[0] : raw;
|
|
1836
|
+
}));
|
|
1752
1837
|
if (!result) throw new Error("status response was empty");
|
|
1753
1838
|
run.lastStatus = result;
|
|
1754
1839
|
run.agent = result.agent ?? run.agent;
|
|
@@ -1831,7 +1916,10 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
1831
1916
|
const tick = async () => {
|
|
1832
1917
|
if (!pollingActive || generation !== pollingGeneration) return;
|
|
1833
1918
|
const prevStatuses = /* @__PURE__ */ new Map();
|
|
1834
|
-
for (const [id, run] of trackedRuns) prevStatuses.set(id,
|
|
1919
|
+
for (const [id, run] of trackedRuns) prevStatuses.set(id, {
|
|
1920
|
+
status: run.lastStatus?.status,
|
|
1921
|
+
pendingPermission: !!run.lastStatus?.pending_permission
|
|
1922
|
+
});
|
|
1835
1923
|
const currentPoll = pollRuns();
|
|
1836
1924
|
pollInFlight = currentPoll;
|
|
1837
1925
|
const entries = await currentPoll.finally(() => {
|
|
@@ -1844,13 +1932,19 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
1844
1932
|
const entryKey = trackedRunKey(entry.supervisorId, entry.runId);
|
|
1845
1933
|
const run = trackedRuns.get(entryKey);
|
|
1846
1934
|
if (!run) continue;
|
|
1935
|
+
const prevStatus = prevStatuses.get(entryKey);
|
|
1936
|
+
const wasStopped = prevStatus?.status !== void 0 && isTerminalStatus(prevStatus.status) || !!prevStatus?.pendingPermission;
|
|
1937
|
+
if ((entry.pendingPermission || isTerminalStatus(entry.status)) && !wasStopped) interruptPendingWaits({
|
|
1938
|
+
runId: entry.runId,
|
|
1939
|
+
label: entry.label,
|
|
1940
|
+
status: isTerminalStatus(entry.status) ? entry.status : "waiting"
|
|
1941
|
+
}, entryKey);
|
|
1847
1942
|
if (isTerminalStatus(entry.status)) {
|
|
1848
1943
|
if (decideCompletion(run) === "skip") {
|
|
1849
1944
|
if (run.consumed) trackedRuns.delete(entryKey);
|
|
1850
1945
|
continue;
|
|
1851
1946
|
}
|
|
1852
|
-
|
|
1853
|
-
if (!(prevStatus && isTerminalStatus(prevStatus))) sessionCtx?.ui.notify(`Sub-agent "${entry.label}" finished: ${entry.status}`, entry.status === "done" ? "info" : "warning");
|
|
1947
|
+
if (!(prevStatus?.status !== void 0 && isTerminalStatus(prevStatus.status))) sessionCtx?.ui.notify(`Sub-agent "${entry.label}" finished: ${entry.status}`, entry.status === "done" ? "info" : "warning");
|
|
1854
1948
|
const finalOutput = await resolveFinalOutput(run, run.lastStatus);
|
|
1855
1949
|
try {
|
|
1856
1950
|
pi.sendUserMessage(buildCompletionText({
|
|
@@ -1970,11 +2064,10 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
1970
2064
|
supervisorId: run.supervisorId
|
|
1971
2065
|
});
|
|
1972
2066
|
try {
|
|
1973
|
-
const
|
|
2067
|
+
const status = asStatusResult(await deps.statusTool({
|
|
1974
2068
|
runId: result.run_id,
|
|
1975
2069
|
supervisorId: run.supervisorId
|
|
1976
|
-
});
|
|
1977
|
-
const status = Array.isArray(rawStatus) ? rawStatus[0] : rawStatus;
|
|
2070
|
+
}));
|
|
1978
2071
|
upsertTrackedRun({
|
|
1979
2072
|
runId: result.run_id,
|
|
1980
2073
|
label: result.label,
|
|
@@ -2052,12 +2145,11 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2052
2145
|
await observer?.close().catch(() => {});
|
|
2053
2146
|
if (outcome === "aborted") return { aborted: true };
|
|
2054
2147
|
try {
|
|
2055
|
-
const raw = await deps.statusTool({
|
|
2056
|
-
runId: run.runId,
|
|
2057
|
-
supervisorId: run.supervisorId
|
|
2058
|
-
});
|
|
2059
2148
|
return {
|
|
2060
|
-
status:
|
|
2149
|
+
status: asStatusResult(await deps.statusTool({
|
|
2150
|
+
runId: run.runId,
|
|
2151
|
+
supervisorId: run.supervisorId
|
|
2152
|
+
})),
|
|
2061
2153
|
aborted: false
|
|
2062
2154
|
};
|
|
2063
2155
|
} catch {
|
|
@@ -2070,11 +2162,10 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2070
2162
|
if (!firstPoll) await sleep(pollIntervalMs);
|
|
2071
2163
|
firstPoll = false;
|
|
2072
2164
|
try {
|
|
2073
|
-
const
|
|
2165
|
+
const status = asStatusResult(await deps.statusTool({
|
|
2074
2166
|
runId: run.runId,
|
|
2075
2167
|
supervisorId: run.supervisorId
|
|
2076
|
-
});
|
|
2077
|
-
const status = Array.isArray(raw) ? raw[0] : raw;
|
|
2168
|
+
}));
|
|
2078
2169
|
if (!status) continue;
|
|
2079
2170
|
const text = status.pending_permission ? `[blocked] ${run.label ?? run.runId} — permission: ${status.pending_permission.description}` : `${run.label ?? run.runId}${status.phase_label ? ` (${status.phase_label})` : ""}`;
|
|
2080
2171
|
onUpdate?.({
|
|
@@ -2116,6 +2207,14 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2116
2207
|
stopSubAgentPoll();
|
|
2117
2208
|
hostPendingAsks.clear();
|
|
2118
2209
|
subAgentPendingAsks.clear();
|
|
2210
|
+
for (const wait of [...pendingWaits]) {
|
|
2211
|
+
pendingWaits.delete(wait);
|
|
2212
|
+
wait.interrupt({
|
|
2213
|
+
runId: "",
|
|
2214
|
+
status: "shutdown",
|
|
2215
|
+
synthetic: true
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2119
2218
|
sessionCtx = null;
|
|
2120
2219
|
});
|
|
2121
2220
|
pi.on("before_agent_start", async () => {
|
|
@@ -2144,12 +2243,12 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2144
2243
|
pi.registerTool({
|
|
2145
2244
|
name: "avenor_spawn",
|
|
2146
2245
|
label: "Avenor Spawn",
|
|
2147
|
-
description: `Dispatch an agent run via avenor. Optional thinking accepts ${[...THINKING_LEVELS].slice(0, -1).join(", ")}, or ${THINKING_LEVELS[THINKING_LEVELS.length - 1]}; unsupported backends reject explicit values. Blocks by default, showing live progress. Set wait=false for fire-and-forget.`,
|
|
2246
|
+
description: `Dispatch an agent run via avenor. Optional thinking accepts ${[...THINKING_LEVELS].slice(0, -1).join(", ")}, or ${THINKING_LEVELS[THINKING_LEVELS.length - 1]}; unsupported backends reject explicit values. Blocks by default, showing live progress. Set wait=false for fire-and-forget. With several blocking spawns in parallel, the first stopping point (permission or completion) interrupts the other waits.`,
|
|
2148
2247
|
promptSnippet: "Dispatch sub-agent runs via avenor (spawn, status, result, inspect, events, follow-up, shutdown)",
|
|
2149
2248
|
promptGuidelines: [
|
|
2150
2249
|
"Use avenor_spawn to delegate well-defined tasks to sub-agents.",
|
|
2151
2250
|
"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.",
|
|
2251
|
+
"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. When awaiting multiple runs, the first stopping point (permission or completion) interrupts the other waits.",
|
|
2153
2252
|
"Use avenor_inspect to review a bounded transcript and tool activity from a run.",
|
|
2154
2253
|
"Use avenor_events only when you specifically need raw event payloads.",
|
|
2155
2254
|
"Use avenor_follow_up to iterate on a completed run.",
|
|
@@ -2251,21 +2350,56 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2251
2350
|
run_id: result.run_id
|
|
2252
2351
|
}
|
|
2253
2352
|
});
|
|
2254
|
-
const
|
|
2353
|
+
const blockingWait = createInterruptibleWait(trackedRunKey(supervisorId, result.run_id), signal);
|
|
2354
|
+
let waitResult;
|
|
2355
|
+
try {
|
|
2356
|
+
waitResult = await waitForRun(runRef, blockingWait.signal, onUpdate);
|
|
2357
|
+
} finally {
|
|
2358
|
+
blockingWait.dispose();
|
|
2359
|
+
}
|
|
2360
|
+
let waitStatus = waitResult.status;
|
|
2255
2361
|
if (waitResult.aborted) {
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
}
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2362
|
+
const stop = blockingWait.stopped;
|
|
2363
|
+
if (stop && !stop.synthetic) {
|
|
2364
|
+
const probed = asStatusResult(await deps.statusTool({
|
|
2365
|
+
runId: result.run_id,
|
|
2366
|
+
supervisorId
|
|
2367
|
+
}).catch(() => void 0));
|
|
2368
|
+
if (probed && (isTerminalStatus(probed.status) || probed.status === "waiting")) waitStatus = probed;
|
|
2369
|
+
else {
|
|
2370
|
+
const tracked = getTrackedRun(result.run_id, supervisorId);
|
|
2371
|
+
if (tracked) tracked.blocking = false;
|
|
2372
|
+
return {
|
|
2373
|
+
content: [{
|
|
2374
|
+
type: "text",
|
|
2375
|
+
text: `Monitoring of "${label}" (run_id: ${result.run_id}) was interrupted: "${stop.label ?? stop.runId}" reached ${stop.status === "waiting" ? "a permission request" : stop.status}. Handle that first; this run's final result is delivered automatically.`
|
|
2376
|
+
}],
|
|
2377
|
+
details: {
|
|
2378
|
+
run_id: result.run_id,
|
|
2379
|
+
interrupted_by: {
|
|
2380
|
+
run_id: stop.runId,
|
|
2381
|
+
label: stop.label,
|
|
2382
|
+
status: stop.status
|
|
2383
|
+
},
|
|
2384
|
+
...spawnIdentityMetadata(hostParams)
|
|
2385
|
+
}
|
|
2386
|
+
};
|
|
2265
2387
|
}
|
|
2266
|
-
}
|
|
2388
|
+
} else {
|
|
2389
|
+
trackedRuns.delete(trackedRunKey(supervisorId, result.run_id));
|
|
2390
|
+
return {
|
|
2391
|
+
content: [{
|
|
2392
|
+
type: "text",
|
|
2393
|
+
text: `Monitoring of "${label}" (run_id: ${result.run_id}) was interrupted. Use avenor_status or avenor_inspect to check.`
|
|
2394
|
+
}],
|
|
2395
|
+
details: {
|
|
2396
|
+
run_id: result.run_id,
|
|
2397
|
+
...spawnIdentityMetadata(hostParams)
|
|
2398
|
+
}
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2267
2401
|
}
|
|
2268
|
-
const status =
|
|
2402
|
+
const status = waitStatus;
|
|
2269
2403
|
if (!status) return {
|
|
2270
2404
|
content: [{
|
|
2271
2405
|
type: "text",
|
|
@@ -2404,7 +2538,7 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2404
2538
|
pi.registerTool({
|
|
2405
2539
|
name: "avenor_result",
|
|
2406
2540
|
label: "Avenor Result",
|
|
2407
|
-
description:
|
|
2541
|
+
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. When several runs are pending, the first stopping point (a permission request or completion on any run) interrupts other blocking waits so they can be handled; re-issue the interrupted wait afterwards.`,
|
|
2408
2542
|
parameters: Type.Object({
|
|
2409
2543
|
run_id: Type.String({ description: "Run ID or label" }),
|
|
2410
2544
|
wait: Type.Optional(Type.Boolean({ description: "Wait for a terminal result (default true); false retrieves the current state without blocking" })),
|
|
@@ -2416,14 +2550,21 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2416
2550
|
const previousBlocking = tracked?.blocking;
|
|
2417
2551
|
if (tracked) tracked.blocking = true;
|
|
2418
2552
|
let completed = false;
|
|
2553
|
+
const blockingWait = params.wait ?? true ? createInterruptibleWait(trackedRunKey(tracked?.supervisorId ?? params.supervisor_id, tracked?.runId ?? params.run_id), signal) : void 0;
|
|
2419
2554
|
try {
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2555
|
+
let result;
|
|
2556
|
+
try {
|
|
2557
|
+
result = await deps.resultTool({
|
|
2558
|
+
runId: params.run_id,
|
|
2559
|
+
supervisorId: params.supervisor_id,
|
|
2560
|
+
wait: params.wait,
|
|
2561
|
+
timeout: params.timeout,
|
|
2562
|
+
signal: blockingWait?.signal ?? signal
|
|
2563
|
+
});
|
|
2564
|
+
} catch (err) {
|
|
2565
|
+
if (!blockingWait?.stopped || blockingWait.stopped.synthetic) throw err;
|
|
2566
|
+
result = await resultAfterInterrupt(params, blockingWait.stopped, tracked);
|
|
2567
|
+
}
|
|
2427
2568
|
completed = result.ready;
|
|
2428
2569
|
if (completed) {
|
|
2429
2570
|
if (tracked) tracked.consumed = true;
|
|
@@ -2445,6 +2586,7 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2445
2586
|
details: result
|
|
2446
2587
|
};
|
|
2447
2588
|
} finally {
|
|
2589
|
+
blockingWait?.dispose();
|
|
2448
2590
|
if (!completed && tracked && getTrackedRun(params.run_id, params.supervisor_id) === tracked) tracked.blocking = previousBlocking ?? false;
|
|
2449
2591
|
}
|
|
2450
2592
|
},
|
|
@@ -2548,11 +2690,10 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2548
2690
|
supervisorId: params.supervisor_id
|
|
2549
2691
|
});
|
|
2550
2692
|
try {
|
|
2551
|
-
const
|
|
2693
|
+
const status = asStatusResult(await deps.statusTool({
|
|
2552
2694
|
runId: result.run_id,
|
|
2553
2695
|
supervisorId: params.supervisor_id
|
|
2554
|
-
});
|
|
2555
|
-
const status = Array.isArray(rawStatus) ? rawStatus[0] : rawStatus;
|
|
2696
|
+
}));
|
|
2556
2697
|
upsertTrackedRun({
|
|
2557
2698
|
runId: result.run_id,
|
|
2558
2699
|
label: result.label,
|
|
@@ -2589,16 +2730,18 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2589
2730
|
pi.registerTool({
|
|
2590
2731
|
name: "avenor_ask",
|
|
2591
2732
|
label: "Avenor Ask",
|
|
2592
|
-
description: "Ask another agent run a question and block until it replies.",
|
|
2733
|
+
description: "Ask another agent run a question and block until it replies. An optional timeout_ms bounds the wait; a failed ask reports its message_id and whether it is still pending (withdrawable via avenor_cancel) or already withdrawn.",
|
|
2593
2734
|
parameters: Type.Object({
|
|
2594
2735
|
to_run_id: Type.String({ description: "Target run ID" }),
|
|
2595
2736
|
message: Type.String({ description: "Question to ask the other agent" }),
|
|
2737
|
+
timeout_ms: Type.Optional(Type.Integer({ description: "Optional timeout in milliseconds for the ask" })),
|
|
2596
2738
|
supervisor_id: Type.Optional(Type.String({ description: "Reuse an existing supervisor by socket path" }))
|
|
2597
2739
|
}),
|
|
2598
2740
|
async execute(_toolCallId, params) {
|
|
2599
2741
|
return askTool({
|
|
2600
2742
|
toRunId: params.to_run_id,
|
|
2601
2743
|
message: params.message,
|
|
2744
|
+
timeoutMs: params.timeout_ms,
|
|
2602
2745
|
supervisorId: params.supervisor_id
|
|
2603
2746
|
});
|
|
2604
2747
|
}
|
|
@@ -2789,6 +2932,31 @@ function createExtension(deps = defaultDeps, options = {}) {
|
|
|
2789
2932
|
}, theme, context.args);
|
|
2790
2933
|
}
|
|
2791
2934
|
});
|
|
2935
|
+
pi.registerTool({
|
|
2936
|
+
name: "avenor_kill",
|
|
2937
|
+
label: "Avenor Kill",
|
|
2938
|
+
description: "Terminate a run without shutting down the supervisor. Other runs and the supervisor itself keep running.",
|
|
2939
|
+
parameters: Type.Object({
|
|
2940
|
+
run_id: Type.String({
|
|
2941
|
+
minLength: 1,
|
|
2942
|
+
description: "Run ID or label to terminate"
|
|
2943
|
+
}),
|
|
2944
|
+
supervisor_id: Type.Optional(Type.String({ description: "Reuse an existing supervisor by socket path" }))
|
|
2945
|
+
}),
|
|
2946
|
+
async execute(_toolCallId, params) {
|
|
2947
|
+
const result = await deps.killTool({
|
|
2948
|
+
runId: params.run_id,
|
|
2949
|
+
supervisorId: params.supervisor_id
|
|
2950
|
+
});
|
|
2951
|
+
return {
|
|
2952
|
+
content: [{
|
|
2953
|
+
type: "text",
|
|
2954
|
+
text: JSON.stringify(result, null, 2)
|
|
2955
|
+
}],
|
|
2956
|
+
details: result
|
|
2957
|
+
};
|
|
2958
|
+
}
|
|
2959
|
+
});
|
|
2792
2960
|
pi.registerTool({
|
|
2793
2961
|
name: "avenor_workflow_status",
|
|
2794
2962
|
label: "Avenor Workflow Status",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dougbots/avenor-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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.13.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@earendil-works/pi-coding-agent": ">=0.74.0",
|