@dougbots/avenor-pi 0.3.5 → 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.
Files changed (2) hide show
  1. package/dist/index.js +98 -25
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -108,31 +108,79 @@ function summarizeEvent(evt) {
108
108
  async function src_default(pi) {
109
109
  const trackedRuns = /* @__PURE__ */ new Map();
110
110
  let pollingActive = false;
111
+ let pollingGeneration = 0;
112
+ let pollInFlight = null;
111
113
  let sessionCtx = null;
112
114
  async function pollRuns() {
113
- let raw;
115
+ const liveMap = /* @__PURE__ */ new Map();
114
116
  try {
115
- raw = await statusTool({});
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);
116
120
  } catch (err) {
117
- console.error("avenor pollRuns: statusTool failed", err);
118
- return [];
121
+ console.error("avenor pollRuns: failed to list singleton runs", err);
119
122
  }
120
- const results = Array.isArray(raw) ? raw : [raw];
121
123
  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
- });
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
+ }
135
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
+ });
136
184
  return entries;
137
185
  }
138
186
  function renderStatusLines(entries) {
@@ -160,17 +208,24 @@ async function src_default(pi) {
160
208
  function startPolling() {
161
209
  if (pollingActive) return;
162
210
  pollingActive = true;
211
+ const generation = ++pollingGeneration;
163
212
  const tick = async () => {
213
+ if (!pollingActive || generation !== pollingGeneration) return;
164
214
  const prevStatuses = /* @__PURE__ */ new Map();
165
215
  for (const [id, run] of trackedRuns) prevStatuses.set(id, run.lastStatus?.status);
166
- const entries = await pollRuns();
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;
167
222
  updateWidget(entries);
168
223
  for (const entry of entries) {
169
224
  const run = trackedRuns.get(entry.runId);
170
225
  if (!run) continue;
171
226
  if (TERMINAL_STATUSES.has(entry.status)) {
172
227
  const prevStatus = prevStatuses.get(entry.runId);
173
- if (prevStatus && !TERMINAL_STATUSES.has(prevStatus)) {
228
+ if (!prevStatus || !TERMINAL_STATUSES.has(prevStatus)) {
174
229
  sessionCtx?.ui.notify(`Sub-agent "${entry.label}" finished: ${entry.status}`, entry.status === "done" ? "info" : "warning");
175
230
  if (!run.blocking) try {
176
231
  pi.sendUserMessage(buildCompletionText({
@@ -195,20 +250,35 @@ async function src_default(pi) {
195
250
  }
196
251
  } else if (!entry.pendingPermission && run.permissionNotified) run.permissionNotified = false;
197
252
  }
198
- if (entries.some((e) => !TERMINAL_STATUSES.has(e.status))) setTimeout(tick, POLL_INTERVAL_MS);
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);
199
259
  else {
200
260
  pollingActive = false;
201
261
  sessionCtx?.ui.setStatus("avenor", "");
202
262
  sessionCtx?.ui.setWidget("avenor-status", void 0);
203
263
  }
204
264
  };
205
- 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);
206
276
  }
207
277
  pi.on("session_start", async (_event, ctx) => {
208
278
  sessionCtx = ctx;
209
279
  });
210
280
  pi.on("session_shutdown", async (_event, _ctx) => {
211
- pollingActive = false;
281
+ await stopPolling();
212
282
  sessionCtx = null;
213
283
  });
214
284
  pi.on("before_agent_start", async (event, _ctx) => {
@@ -285,6 +355,7 @@ async function src_default(pi) {
285
355
  agent: params.agent,
286
356
  label,
287
357
  supervisorId: result.supervisor_id,
358
+ runtimeId: result.runtime_id,
288
359
  startTime: Date.now(),
289
360
  blocking: wait
290
361
  });
@@ -337,7 +408,7 @@ async function src_default(pi) {
337
408
  });
338
409
  else {
339
410
  let lastAction = "";
340
- if (!result.supervisor_id) try {
411
+ if (!params.supervisor_id) try {
341
412
  const eventsResult = await eventsTool({
342
413
  runId: result.run_id,
343
414
  limit: 3
@@ -537,6 +608,8 @@ async function src_default(pi) {
537
608
  force: Type.Optional(Type.Boolean({ description: "Force shutdown instead of graceful" }))
538
609
  }),
539
610
  async execute(_toolCallId, params) {
611
+ await stopPolling();
612
+ trackedRuns.clear();
540
613
  const result = await shutdownTool({
541
614
  supervisorId: params.supervisor_id,
542
615
  force: params.force
@@ -658,7 +731,7 @@ async function src_default(pi) {
658
731
  if (!await ctx.ui.confirm("Cancel run", `Cancel "${label}" (${runId.slice(0, 8)})?`)) return;
659
732
  try {
660
733
  const { Supervisor } = await import("@dougbots/avenor-core");
661
- 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);
662
735
  ctx.ui.notify(`Cancelled "${label}"`, "info");
663
736
  } catch (err) {
664
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.5",
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.5"
38
+ "@dougbots/avenor-core": "^0.3.6"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@earendil-works/pi-coding-agent": "*",