@dougbots/avenor-pi 0.7.1 → 0.8.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 CHANGED
@@ -11,6 +11,12 @@ interface RunStatusEntry {
11
11
  agent: string;
12
12
  pendingPermission?: boolean;
13
13
  permissionDescription?: string;
14
+ /** OS PID of the spawned pi process (pi backend only), for discovering child supervisors. */
15
+ pid?: number;
16
+ /** Backend type, used to determine whether to look for child supervisors. */
17
+ backend?: string;
18
+ /** Count of active (non-terminal) runs on this run's child supervisor, if discovered. */
19
+ nestedCount?: number;
14
20
  }
15
21
  //#endregion
16
22
  //#region src/index.d.ts
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
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, resultTool, shutdownTool, spawnTool, statusTool } from "@dougbots/avenor-core";
3
+ import * as path from "node:path";
4
+ import { Supervisor, answerPermissionTool, dial, eventsTool, followUpTool, inspectTool, observeRun, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool } from "@dougbots/avenor-core";
4
5
  //#region src/watch.ts
5
6
  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
7
  const TRANSCRIPT_SCROLL_STEP = 4;
@@ -653,7 +654,8 @@ function renderStatusLines(entries) {
653
654
  const emoji = statusEmoji(e.status);
654
655
  const perm = e.pendingPermission ? " 🔒" : "";
655
656
  const phase = e.phaseLabel ? ` [${e.phaseLabel}]` : "";
656
- return `${emoji} ${e.label} — ${e.status}${phase}${perm}`;
657
+ const nested = e.nestedCount ? ` +${e.nestedCount}` : "";
658
+ return `${emoji} ${e.label} — ${e.status}${phase}${perm}${nested}`;
657
659
  });
658
660
  }
659
661
  function compactWhitespace(text, limit = 100) {
@@ -756,6 +758,29 @@ function createExtension(deps = defaultDeps) {
756
758
  return;
757
759
  }
758
760
  }
761
+ /**
762
+ * Count active (non-terminal) runs on a child supervisor discovered via
763
+ * the spawned pi process's PID. The child pi process creates its own
764
+ * avenor supervisor at `avenor-mcp-<pid>.sock`; we dial it briefly to
765
+ * list runs and count those still active. Returns 0 on any failure
766
+ * (socket not yet ready, supervisor idle-shut-down, etc.).
767
+ */
768
+ async function countNestedRuns(pid) {
769
+ if (!pid) return 0;
770
+ const socketPath = path.join(socketsRoot(), `avenor-mcp-${pid}.sock`);
771
+ let client;
772
+ try {
773
+ client = await Promise.race([deps.dial(socketPath, { callTimeoutMs: 2e3 }), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("dial timeout")), 2e3))]);
774
+ return (await client.list()).filter((r) => {
775
+ const status = String(r.status ?? "running").toLowerCase();
776
+ return status === "running" || status === "idle";
777
+ }).length;
778
+ } catch {
779
+ return 0;
780
+ } finally {
781
+ client?.close();
782
+ }
783
+ }
759
784
  async function pollRuns() {
760
785
  const liveMap = /* @__PURE__ */ new Map();
761
786
  try {
@@ -779,7 +804,9 @@ function createExtension(deps = defaultDeps) {
779
804
  phaseLabel: live.phase_label,
780
805
  agent: run.agent,
781
806
  pendingPermission: !!live.pending_permission,
782
- permissionDescription: live.pending_permission?.description
807
+ permissionDescription: live.pending_permission?.description,
808
+ pid: live.pid,
809
+ backend: live.backend
783
810
  });
784
811
  continue;
785
812
  }
@@ -799,7 +826,9 @@ function createExtension(deps = defaultDeps) {
799
826
  phaseLabel: result.phase_label,
800
827
  agent: run.agent,
801
828
  pendingPermission: !!result.pending_permission,
802
- permissionDescription: result.pending_permission?.description
829
+ permissionDescription: result.pending_permission?.description,
830
+ pid: result.pid,
831
+ backend: result.backend
803
832
  });
804
833
  } catch (err) {
805
834
  console.error(`avenor pollRuns: statusTool failed for ${run.runId}`, err);
@@ -812,7 +841,9 @@ function createExtension(deps = defaultDeps) {
812
841
  phaseLabel: previous?.phase_label,
813
842
  agent: run.agent,
814
843
  pendingPermission: !!previous?.pending_permission,
815
- permissionDescription: previous?.pending_permission?.description
844
+ permissionDescription: previous?.pending_permission?.description,
845
+ pid: previous?.pid,
846
+ backend: previous?.backend
816
847
  });
817
848
  }
818
849
  }
@@ -824,8 +855,16 @@ function createExtension(deps = defaultDeps) {
824
855
  phaseLabel: live.phase_label,
825
856
  agent: live.agent ?? "unknown",
826
857
  pendingPermission: !!live.pending_permission,
827
- permissionDescription: live.pending_permission?.description
858
+ permissionDescription: live.pending_permission?.description,
859
+ pid: live.pid,
860
+ backend: live.backend
828
861
  });
862
+ const nestedPids = entries.filter((e) => e.backend === "pi" && e.pid && !isTerminalStatus(e.status)).map((e) => e.pid);
863
+ if (nestedPids.length > 0) {
864
+ const nestedCounts = await Promise.all(nestedPids.map((pid) => countNestedRuns(pid)));
865
+ let idx = 0;
866
+ for (const entry of entries) if (entry.backend === "pi" && entry.pid && !isTerminalStatus(entry.status)) entry.nestedCount = nestedCounts[idx++] ?? 0;
867
+ }
829
868
  return entries;
830
869
  }
831
870
  function updateWidget(entries) {
@@ -833,7 +872,11 @@ function createExtension(deps = defaultDeps) {
833
872
  const lines = renderStatusLines(entries);
834
873
  if (lines.length > 0) {
835
874
  sessionCtx.ui.setWidget("avenor-status", lines);
836
- sessionCtx.ui.setStatus("avenor", lines.map((line) => line.slice(0, 60)).join(" "));
875
+ const statusStr = lines.map((line) => line.slice(0, 60)).join(" ");
876
+ const nestedTotal = entries.filter((e) => !isTerminalStatus(e.status)).reduce((sum, e) => sum + (e.nestedCount ?? 0), 0);
877
+ const total = lines.length + nestedTotal;
878
+ const statusWithTotal = nestedTotal > 0 ? `${statusStr} total:${total}` : statusStr;
879
+ sessionCtx.ui.setStatus("avenor", statusWithTotal);
837
880
  } else {
838
881
  sessionCtx.ui.setWidget("avenor-status", void 0);
839
882
  sessionCtx.ui.setStatus("avenor", "");
@@ -1415,14 +1458,16 @@ function createExtension(deps = defaultDeps) {
1415
1458
  run_id: Type.String({ description: "Run ID with the pending permission" }),
1416
1459
  option_id: Type.String({ description: "allow_once | allow_always | deny" }),
1417
1460
  request_id: Type.Optional(Type.String({ description: "Permission request ID (auto-discovered if omitted)" })),
1418
- supervisor_id: Type.Optional(Type.String({ description: "Reuse an existing supervisor by socket path" }))
1461
+ supervisor_id: Type.Optional(Type.String({ description: "Reuse an existing supervisor by socket path" })),
1462
+ message: Type.Optional(Type.String({ description: "Optional write-in response text. Required only when the selected option declares requiresMessage: true." }))
1419
1463
  }),
1420
1464
  async execute(_toolCallId, params) {
1421
1465
  const result = await deps.answerPermissionTool({
1422
1466
  runId: params.run_id,
1423
1467
  optionId: params.option_id,
1424
1468
  requestId: params.request_id,
1425
- supervisorId: params.supervisor_id
1469
+ supervisorId: params.supervisor_id,
1470
+ message: params.message
1426
1471
  });
1427
1472
  return {
1428
1473
  content: [{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dougbots/avenor-pi",
3
- "version": "0.7.1",
3
+ "version": "0.8.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.7.1"
38
+ "@dougbots/avenor-core": "^0.8.0"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@earendil-works/pi-coding-agent": "*",