@dougbots/avenor-pi 0.3.5 → 0.3.7

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 CHANGED
@@ -1,6 +1,7 @@
1
1
  import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
 
3
3
  //#region src/index.d.ts
4
+ declare function statusSupervisorId(requestedSupervisorId: string | undefined, spawnedSupervisorId: string): string | undefined;
4
5
  declare function export_default(pi: ExtensionAPI): Promise<void>;
5
6
  //#endregion
6
- export { export_default as default };
7
+ export { export_default as default, statusSupervisorId };
package/dist/index.js CHANGED
@@ -80,6 +80,9 @@ function formatRunLine(entry) {
80
80
  //#endregion
81
81
  //#region src/index.ts
82
82
  const POLL_INTERVAL_MS = 3e3;
83
+ function statusSupervisorId(requestedSupervisorId, spawnedSupervisorId) {
84
+ return requestedSupervisorId ? spawnedSupervisorId : void 0;
85
+ }
83
86
  function sleep(ms) {
84
87
  return new Promise((resolve) => setTimeout(resolve, ms));
85
88
  }
@@ -108,31 +111,79 @@ function summarizeEvent(evt) {
108
111
  async function src_default(pi) {
109
112
  const trackedRuns = /* @__PURE__ */ new Map();
110
113
  let pollingActive = false;
114
+ let pollingGeneration = 0;
115
+ let pollInFlight = null;
111
116
  let sessionCtx = null;
112
117
  async function pollRuns() {
113
- let raw;
118
+ const liveMap = /* @__PURE__ */ new Map();
114
119
  try {
115
- raw = await statusTool({});
120
+ const allLive = await statusTool({});
121
+ const liveList = Array.isArray(allLive) ? allLive : [allLive];
122
+ for (const result of liveList) liveMap.set(result.run_id, result);
116
123
  } catch (err) {
117
- console.error("avenor pollRuns: statusTool failed", err);
118
- return [];
124
+ console.error("avenor pollRuns: failed to list singleton runs", err);
119
125
  }
120
- const results = Array.isArray(raw) ? raw : [raw];
121
126
  const entries = [];
122
- for (const r of results) {
123
- const run = trackedRuns.get(r.run_id);
124
- if (run) run.lastStatus = r;
125
- entries.push({
126
- runId: r.run_id,
127
- label: r.label,
128
- status: r.status,
129
- phase: r.phase,
130
- phaseLabel: r.phase_label,
131
- agent: trackedRuns.get(r.run_id)?.agent ?? "unknown",
132
- pendingPermission: !!r.pending_permission,
133
- permissionDescription: r.pending_permission?.description
134
- });
127
+ for (const run of trackedRuns.values()) {
128
+ const live = liveMap.get(run.runId);
129
+ if (live) {
130
+ liveMap.delete(live.run_id);
131
+ run.lastStatus = live;
132
+ entries.push({
133
+ runId: run.runId,
134
+ label: run.label,
135
+ status: live.status,
136
+ phase: live.phase,
137
+ phaseLabel: live.phase_label,
138
+ agent: run.agent,
139
+ pendingPermission: !!live.pending_permission,
140
+ permissionDescription: live.pending_permission?.description
141
+ });
142
+ continue;
143
+ }
144
+ try {
145
+ const raw = await statusTool({
146
+ runId: run.runId,
147
+ supervisorId: run.supervisorId
148
+ });
149
+ const result = Array.isArray(raw) ? raw[0] : raw;
150
+ if (!result) throw new Error("status response was empty");
151
+ run.lastStatus = result;
152
+ entries.push({
153
+ runId: run.runId,
154
+ label: run.label,
155
+ status: result.status,
156
+ phase: result.phase,
157
+ phaseLabel: result.phase_label,
158
+ agent: run.agent,
159
+ pendingPermission: !!result.pending_permission,
160
+ permissionDescription: result.pending_permission?.description
161
+ });
162
+ } catch (err) {
163
+ console.error(`avenor pollRuns: statusTool failed for ${run.runId}`, err);
164
+ const previous = run.lastStatus;
165
+ entries.push({
166
+ runId: run.runId,
167
+ label: run.label,
168
+ status: previous?.status ?? "unavailable",
169
+ phase: previous?.phase,
170
+ phaseLabel: previous?.phase_label,
171
+ agent: run.agent,
172
+ pendingPermission: !!previous?.pending_permission,
173
+ permissionDescription: previous?.pending_permission?.description
174
+ });
175
+ }
135
176
  }
177
+ for (const live of liveMap.values()) entries.push({
178
+ runId: live.run_id,
179
+ label: live.label,
180
+ status: live.status,
181
+ phase: live.phase,
182
+ phaseLabel: live.phase_label,
183
+ agent: "unknown",
184
+ pendingPermission: !!live.pending_permission,
185
+ permissionDescription: live.pending_permission?.description
186
+ });
136
187
  return entries;
137
188
  }
138
189
  function renderStatusLines(entries) {
@@ -160,17 +211,24 @@ async function src_default(pi) {
160
211
  function startPolling() {
161
212
  if (pollingActive) return;
162
213
  pollingActive = true;
214
+ const generation = ++pollingGeneration;
163
215
  const tick = async () => {
216
+ if (!pollingActive || generation !== pollingGeneration) return;
164
217
  const prevStatuses = /* @__PURE__ */ new Map();
165
218
  for (const [id, run] of trackedRuns) prevStatuses.set(id, run.lastStatus?.status);
166
- const entries = await pollRuns();
219
+ const currentPoll = pollRuns();
220
+ pollInFlight = currentPoll;
221
+ const entries = await currentPoll.finally(() => {
222
+ if (pollInFlight === currentPoll) pollInFlight = null;
223
+ });
224
+ if (!pollingActive || generation !== pollingGeneration) return;
167
225
  updateWidget(entries);
168
226
  for (const entry of entries) {
169
227
  const run = trackedRuns.get(entry.runId);
170
228
  if (!run) continue;
171
229
  if (TERMINAL_STATUSES.has(entry.status)) {
172
230
  const prevStatus = prevStatuses.get(entry.runId);
173
- if (prevStatus && !TERMINAL_STATUSES.has(prevStatus)) {
231
+ if (!prevStatus || !TERMINAL_STATUSES.has(prevStatus)) {
174
232
  sessionCtx?.ui.notify(`Sub-agent "${entry.label}" finished: ${entry.status}`, entry.status === "done" ? "info" : "warning");
175
233
  if (!run.blocking) try {
176
234
  pi.sendUserMessage(buildCompletionText({
@@ -195,20 +253,35 @@ async function src_default(pi) {
195
253
  }
196
254
  } else if (!entry.pendingPermission && run.permissionNotified) run.permissionNotified = false;
197
255
  }
198
- if (entries.some((e) => !TERMINAL_STATUSES.has(e.status))) setTimeout(tick, POLL_INTERVAL_MS);
256
+ if (entries.some((e) => !TERMINAL_STATUSES.has(e.status)) && pollingActive && generation === pollingGeneration) setTimeout(() => {
257
+ tick().catch((err) => {
258
+ pollingActive = false;
259
+ console.error("avenor polling loop failed", err);
260
+ });
261
+ }, POLL_INTERVAL_MS);
199
262
  else {
200
263
  pollingActive = false;
201
264
  sessionCtx?.ui.setStatus("avenor", "");
202
265
  sessionCtx?.ui.setWidget("avenor-status", void 0);
203
266
  }
204
267
  };
205
- tick();
268
+ tick().catch((err) => {
269
+ pollingActive = false;
270
+ console.error("avenor polling loop failed", err);
271
+ });
272
+ }
273
+ async function stopPolling() {
274
+ pollingActive = false;
275
+ pollingGeneration++;
276
+ await pollInFlight?.catch(() => {});
277
+ sessionCtx?.ui.setStatus("avenor", "");
278
+ sessionCtx?.ui.setWidget("avenor-status", void 0);
206
279
  }
207
280
  pi.on("session_start", async (_event, ctx) => {
208
281
  sessionCtx = ctx;
209
282
  });
210
283
  pi.on("session_shutdown", async (_event, _ctx) => {
211
- pollingActive = false;
284
+ await stopPolling();
212
285
  sessionCtx = null;
213
286
  });
214
287
  pi.on("before_agent_start", async (event, _ctx) => {
@@ -280,11 +353,13 @@ async function src_default(pi) {
280
353
  serverUrl: params.server_url,
281
354
  supervisorId: params.supervisor_id
282
355
  });
356
+ const supervisorId = statusSupervisorId(params.supervisor_id, result.supervisor_id);
283
357
  trackedRuns.set(result.run_id, {
284
358
  runId: result.run_id,
285
359
  agent: params.agent,
286
360
  label,
287
- supervisorId: result.supervisor_id,
361
+ supervisorId,
362
+ runtimeId: result.runtime_id,
288
363
  startTime: Date.now(),
289
364
  blocking: wait
290
365
  });
@@ -317,7 +392,7 @@ async function src_default(pi) {
317
392
  try {
318
393
  raw = await statusTool({
319
394
  runId: result.run_id,
320
- supervisorId: result.supervisor_id
395
+ supervisorId
321
396
  });
322
397
  } catch (err) {
323
398
  console.error("avenor spawn poll: statusTool failed", err);
@@ -337,7 +412,7 @@ async function src_default(pi) {
337
412
  });
338
413
  else {
339
414
  let lastAction = "";
340
- if (!result.supervisor_id) try {
415
+ if (!params.supervisor_id) try {
341
416
  const eventsResult = await eventsTool({
342
417
  runId: result.run_id,
343
418
  limit: 3
@@ -537,6 +612,8 @@ async function src_default(pi) {
537
612
  force: Type.Optional(Type.Boolean({ description: "Force shutdown instead of graceful" }))
538
613
  }),
539
614
  async execute(_toolCallId, params) {
615
+ await stopPolling();
616
+ trackedRuns.clear();
540
617
  const result = await shutdownTool({
541
618
  supervisorId: params.supervisor_id,
542
619
  force: params.force
@@ -658,7 +735,7 @@ async function src_default(pi) {
658
735
  if (!await ctx.ui.confirm("Cancel run", `Cancel "${label}" (${runId.slice(0, 8)})?`)) return;
659
736
  try {
660
737
  const { Supervisor } = await import("@dougbots/avenor-core");
661
- await (await Supervisor.get()).getClient().cancel(run?.lastStatus?.runtime_id ?? runId);
738
+ await (await Supervisor.get()).getClient().cancel(run?.lastStatus?.runtime_id ?? run?.runtimeId ?? runId);
662
739
  ctx.ui.notify(`Cancelled "${label}"`, "info");
663
740
  } catch (err) {
664
741
  ctx.ui.notify(`Failed to cancel: ${err instanceof Error ? err.message : String(err)}`, "error");
@@ -667,4 +744,4 @@ async function src_default(pi) {
667
744
  });
668
745
  }
669
746
  //#endregion
670
- export { src_default as default };
747
+ export { src_default as default, statusSupervisorId };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dougbots/avenor-pi",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
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.5"
38
+ "@dougbots/avenor-core": "^0.3.7"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@earendil-works/pi-coding-agent": "*",