@dougbots/avenor-pi 0.3.4 → 0.3.6
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.js +154 -29
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -90,6 +90,14 @@ function buildCompletionText(run, result) {
|
|
|
90
90
|
lines.push(`\nCall \`avenor_events\` with \`run_id: "${run.runId}"\` to review the output, or \`avenor_follow_up\` to iterate.`);
|
|
91
91
|
return lines.join("\n");
|
|
92
92
|
}
|
|
93
|
+
function buildWaitingText(run, status) {
|
|
94
|
+
const perm = status.pending_permission;
|
|
95
|
+
const lines = [`Sub-agent "${run.label}" (${run.agent}) is waiting for input.`];
|
|
96
|
+
if (perm) lines.push(`Permission request: ${perm.description}`, `To answer: call \`avenor_answer_permission\` with run_id "${run.runId}", option_id "allow_once" | "allow_always" | "deny"`);
|
|
97
|
+
else if (status.phase_label) lines.push(`Question: ${status.phase_label}`);
|
|
98
|
+
if (!perm) lines.push(`Use \`avenor_follow_up\` with run_id "${run.runId}" to respond, or \`avenor_status\` to re-check.`);
|
|
99
|
+
return lines.join("\n");
|
|
100
|
+
}
|
|
93
101
|
function summarizeEvent(evt) {
|
|
94
102
|
const type = String(evt.type ?? evt.event ?? "");
|
|
95
103
|
if (type === "tool.call" || type === "tool.use") return `called ${String(evt.tool ?? evt.name ?? type)}`;
|
|
@@ -100,31 +108,79 @@ function summarizeEvent(evt) {
|
|
|
100
108
|
async function src_default(pi) {
|
|
101
109
|
const trackedRuns = /* @__PURE__ */ new Map();
|
|
102
110
|
let pollingActive = false;
|
|
111
|
+
let pollingGeneration = 0;
|
|
112
|
+
let pollInFlight = null;
|
|
103
113
|
let sessionCtx = null;
|
|
104
114
|
async function pollRuns() {
|
|
105
|
-
|
|
115
|
+
const liveMap = /* @__PURE__ */ new Map();
|
|
106
116
|
try {
|
|
107
|
-
|
|
117
|
+
const allLive = await statusTool({});
|
|
118
|
+
const liveList = Array.isArray(allLive) ? allLive : [allLive];
|
|
119
|
+
for (const result of liveList) liveMap.set(result.run_id, result);
|
|
108
120
|
} catch (err) {
|
|
109
|
-
console.error("avenor pollRuns:
|
|
110
|
-
return [];
|
|
121
|
+
console.error("avenor pollRuns: failed to list singleton runs", err);
|
|
111
122
|
}
|
|
112
|
-
const results = Array.isArray(raw) ? raw : [raw];
|
|
113
123
|
const entries = [];
|
|
114
|
-
for (const
|
|
115
|
-
const
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
for (const run of trackedRuns.values()) {
|
|
125
|
+
const live = liveMap.get(run.runId);
|
|
126
|
+
if (live) {
|
|
127
|
+
liveMap.delete(live.run_id);
|
|
128
|
+
run.lastStatus = live;
|
|
129
|
+
entries.push({
|
|
130
|
+
runId: run.runId,
|
|
131
|
+
label: run.label,
|
|
132
|
+
status: live.status,
|
|
133
|
+
phase: live.phase,
|
|
134
|
+
phaseLabel: live.phase_label,
|
|
135
|
+
agent: run.agent,
|
|
136
|
+
pendingPermission: !!live.pending_permission,
|
|
137
|
+
permissionDescription: live.pending_permission?.description
|
|
138
|
+
});
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const raw = await statusTool({
|
|
143
|
+
runId: run.runId,
|
|
144
|
+
supervisorId: run.supervisorId
|
|
145
|
+
});
|
|
146
|
+
const result = Array.isArray(raw) ? raw[0] : raw;
|
|
147
|
+
if (!result) throw new Error("status response was empty");
|
|
148
|
+
run.lastStatus = result;
|
|
149
|
+
entries.push({
|
|
150
|
+
runId: run.runId,
|
|
151
|
+
label: run.label,
|
|
152
|
+
status: result.status,
|
|
153
|
+
phase: result.phase,
|
|
154
|
+
phaseLabel: result.phase_label,
|
|
155
|
+
agent: run.agent,
|
|
156
|
+
pendingPermission: !!result.pending_permission,
|
|
157
|
+
permissionDescription: result.pending_permission?.description
|
|
158
|
+
});
|
|
159
|
+
} catch (err) {
|
|
160
|
+
console.error(`avenor pollRuns: statusTool failed for ${run.runId}`, err);
|
|
161
|
+
const previous = run.lastStatus;
|
|
162
|
+
entries.push({
|
|
163
|
+
runId: run.runId,
|
|
164
|
+
label: run.label,
|
|
165
|
+
status: previous?.status ?? "unavailable",
|
|
166
|
+
phase: previous?.phase,
|
|
167
|
+
phaseLabel: previous?.phase_label,
|
|
168
|
+
agent: run.agent,
|
|
169
|
+
pendingPermission: !!previous?.pending_permission,
|
|
170
|
+
permissionDescription: previous?.pending_permission?.description
|
|
171
|
+
});
|
|
172
|
+
}
|
|
127
173
|
}
|
|
174
|
+
for (const live of liveMap.values()) entries.push({
|
|
175
|
+
runId: live.run_id,
|
|
176
|
+
label: live.label,
|
|
177
|
+
status: live.status,
|
|
178
|
+
phase: live.phase,
|
|
179
|
+
phaseLabel: live.phase_label,
|
|
180
|
+
agent: "unknown",
|
|
181
|
+
pendingPermission: !!live.pending_permission,
|
|
182
|
+
permissionDescription: live.pending_permission?.description
|
|
183
|
+
});
|
|
128
184
|
return entries;
|
|
129
185
|
}
|
|
130
186
|
function renderStatusLines(entries) {
|
|
@@ -152,32 +208,77 @@ async function src_default(pi) {
|
|
|
152
208
|
function startPolling() {
|
|
153
209
|
if (pollingActive) return;
|
|
154
210
|
pollingActive = true;
|
|
211
|
+
const generation = ++pollingGeneration;
|
|
155
212
|
const tick = async () => {
|
|
213
|
+
if (!pollingActive || generation !== pollingGeneration) return;
|
|
156
214
|
const prevStatuses = /* @__PURE__ */ new Map();
|
|
157
215
|
for (const [id, run] of trackedRuns) prevStatuses.set(id, run.lastStatus?.status);
|
|
158
|
-
const
|
|
216
|
+
const currentPoll = pollRuns();
|
|
217
|
+
pollInFlight = currentPoll;
|
|
218
|
+
const entries = await currentPoll.finally(() => {
|
|
219
|
+
if (pollInFlight === currentPoll) pollInFlight = null;
|
|
220
|
+
});
|
|
221
|
+
if (!pollingActive || generation !== pollingGeneration) return;
|
|
159
222
|
updateWidget(entries);
|
|
160
|
-
for (const entry of entries)
|
|
161
|
-
|
|
223
|
+
for (const entry of entries) {
|
|
224
|
+
const run = trackedRuns.get(entry.runId);
|
|
225
|
+
if (!run) continue;
|
|
226
|
+
if (TERMINAL_STATUSES.has(entry.status)) {
|
|
162
227
|
const prevStatus = prevStatuses.get(entry.runId);
|
|
163
|
-
if (prevStatus
|
|
228
|
+
if (!prevStatus || !TERMINAL_STATUSES.has(prevStatus)) {
|
|
229
|
+
sessionCtx?.ui.notify(`Sub-agent "${entry.label}" finished: ${entry.status}`, entry.status === "done" ? "info" : "warning");
|
|
230
|
+
if (!run.blocking) try {
|
|
231
|
+
pi.sendUserMessage(buildCompletionText({
|
|
232
|
+
runId: run.runId,
|
|
233
|
+
label: run.label
|
|
234
|
+
}, run.lastStatus), { deliverAs: "followUp" });
|
|
235
|
+
} catch (err) {
|
|
236
|
+
console.error("avenor tick: sendUserMessage failed", err);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
164
239
|
trackedRuns.delete(entry.runId);
|
|
165
|
-
}
|
|
240
|
+
} else if (entry.pendingPermission && !run.blocking && !run.permissionNotified) {
|
|
241
|
+
run.permissionNotified = true;
|
|
242
|
+
try {
|
|
243
|
+
pi.sendUserMessage(buildWaitingText({
|
|
244
|
+
runId: run.runId,
|
|
245
|
+
label: run.label,
|
|
246
|
+
agent: run.agent
|
|
247
|
+
}, run.lastStatus), { deliverAs: "followUp" });
|
|
248
|
+
} catch (err) {
|
|
249
|
+
console.error("avenor tick: sendUserMessage for permission failed", err);
|
|
250
|
+
}
|
|
251
|
+
} else if (!entry.pendingPermission && run.permissionNotified) run.permissionNotified = false;
|
|
166
252
|
}
|
|
167
|
-
if (entries.some((e) => !TERMINAL_STATUSES.has(e.status))) setTimeout(
|
|
253
|
+
if (entries.some((e) => !TERMINAL_STATUSES.has(e.status)) && pollingActive && generation === pollingGeneration) setTimeout(() => {
|
|
254
|
+
tick().catch((err) => {
|
|
255
|
+
pollingActive = false;
|
|
256
|
+
console.error("avenor polling loop failed", err);
|
|
257
|
+
});
|
|
258
|
+
}, POLL_INTERVAL_MS);
|
|
168
259
|
else {
|
|
169
260
|
pollingActive = false;
|
|
170
261
|
sessionCtx?.ui.setStatus("avenor", "");
|
|
171
262
|
sessionCtx?.ui.setWidget("avenor-status", void 0);
|
|
172
263
|
}
|
|
173
264
|
};
|
|
174
|
-
tick()
|
|
265
|
+
tick().catch((err) => {
|
|
266
|
+
pollingActive = false;
|
|
267
|
+
console.error("avenor polling loop failed", err);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
async function stopPolling() {
|
|
271
|
+
pollingActive = false;
|
|
272
|
+
pollingGeneration++;
|
|
273
|
+
await pollInFlight?.catch(() => {});
|
|
274
|
+
sessionCtx?.ui.setStatus("avenor", "");
|
|
275
|
+
sessionCtx?.ui.setWidget("avenor-status", void 0);
|
|
175
276
|
}
|
|
176
277
|
pi.on("session_start", async (_event, ctx) => {
|
|
177
278
|
sessionCtx = ctx;
|
|
178
279
|
});
|
|
179
280
|
pi.on("session_shutdown", async (_event, _ctx) => {
|
|
180
|
-
|
|
281
|
+
await stopPolling();
|
|
181
282
|
sessionCtx = null;
|
|
182
283
|
});
|
|
183
284
|
pi.on("before_agent_start", async (event, _ctx) => {
|
|
@@ -254,7 +355,9 @@ async function src_default(pi) {
|
|
|
254
355
|
agent: params.agent,
|
|
255
356
|
label,
|
|
256
357
|
supervisorId: result.supervisor_id,
|
|
257
|
-
|
|
358
|
+
runtimeId: result.runtime_id,
|
|
359
|
+
startTime: Date.now(),
|
|
360
|
+
blocking: wait
|
|
258
361
|
});
|
|
259
362
|
startPolling();
|
|
260
363
|
if (!wait) return {
|
|
@@ -305,7 +408,7 @@ async function src_default(pi) {
|
|
|
305
408
|
});
|
|
306
409
|
else {
|
|
307
410
|
let lastAction = "";
|
|
308
|
-
if (!
|
|
411
|
+
if (!params.supervisor_id) try {
|
|
309
412
|
const eventsResult = await eventsTool({
|
|
310
413
|
runId: result.run_id,
|
|
311
414
|
limit: 3
|
|
@@ -327,6 +430,26 @@ async function src_default(pi) {
|
|
|
327
430
|
}
|
|
328
431
|
});
|
|
329
432
|
}
|
|
433
|
+
if (status.status === "waiting") {
|
|
434
|
+
const tracked = trackedRuns.get(result.run_id);
|
|
435
|
+
if (tracked) tracked.permissionNotified = true;
|
|
436
|
+
return {
|
|
437
|
+
content: [{
|
|
438
|
+
type: "text",
|
|
439
|
+
text: buildWaitingText({
|
|
440
|
+
runId: result.run_id,
|
|
441
|
+
label,
|
|
442
|
+
agent: params.agent
|
|
443
|
+
}, status)
|
|
444
|
+
}],
|
|
445
|
+
details: {
|
|
446
|
+
status: "waiting",
|
|
447
|
+
run_id: result.run_id,
|
|
448
|
+
session_id: status.session_id,
|
|
449
|
+
...status.pending_permission && { pending_permission: status.pending_permission }
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
}
|
|
330
453
|
if (TERMINAL_STATUSES.has(status.status)) {
|
|
331
454
|
trackedRuns.delete(result.run_id);
|
|
332
455
|
return {
|
|
@@ -485,6 +608,8 @@ async function src_default(pi) {
|
|
|
485
608
|
force: Type.Optional(Type.Boolean({ description: "Force shutdown instead of graceful" }))
|
|
486
609
|
}),
|
|
487
610
|
async execute(_toolCallId, params) {
|
|
611
|
+
await stopPolling();
|
|
612
|
+
trackedRuns.clear();
|
|
488
613
|
const result = await shutdownTool({
|
|
489
614
|
supervisorId: params.supervisor_id,
|
|
490
615
|
force: params.force
|
|
@@ -606,7 +731,7 @@ async function src_default(pi) {
|
|
|
606
731
|
if (!await ctx.ui.confirm("Cancel run", `Cancel "${label}" (${runId.slice(0, 8)})?`)) return;
|
|
607
732
|
try {
|
|
608
733
|
const { Supervisor } = await import("@dougbots/avenor-core");
|
|
609
|
-
await (await Supervisor.get()).getClient().cancel(run?.lastStatus?.runtime_id ?? runId);
|
|
734
|
+
await (await Supervisor.get()).getClient().cancel(run?.lastStatus?.runtime_id ?? run?.runtimeId ?? runId);
|
|
610
735
|
ctx.ui.notify(`Cancelled "${label}"`, "info");
|
|
611
736
|
} catch (err) {
|
|
612
737
|
ctx.ui.notify(`Failed to cancel: ${err instanceof Error ? err.message : String(err)}`, "error");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dougbots/avenor-pi",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
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.3.
|
|
38
|
+
"@dougbots/avenor-core": "^0.3.6"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@earendil-works/pi-coding-agent": "*",
|