@narumitw/pi-subagents 3.0.2 → 3.1.1

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.
@@ -0,0 +1,72 @@
1
+ import * as fs from "node:fs";
2
+ import type { ExtensionFactory } from "@earendil-works/pi-coding-agent";
3
+ import { toolSourceId } from "./attachment-utils.js";
4
+ import { MAX_READINESS_FRAME_BYTES, takeCapturedReadiness } from "./broker-credentials.js";
5
+
6
+ export interface ChildReadinessState {
7
+ fd: number;
8
+ expectedTools: string[];
9
+ }
10
+
11
+ const captured = takeCapturedReadiness();
12
+
13
+ const childReadinessProbe: ExtensionFactory = captured ? createChildReadinessProbe(captured) : () => undefined;
14
+
15
+ export default childReadinessProbe;
16
+
17
+ export function createChildReadinessProbe(
18
+ readiness: ChildReadinessState,
19
+ writeFrame: (fd: number, frame: string) => void = writeReadinessFrame,
20
+ close: (fd: number) => void = closeDescriptor,
21
+ ): ExtensionFactory {
22
+ return (pi) => {
23
+ let settled = false;
24
+ const publish = (value: { ok: true; sources: string[] } | { ok: false; error: string }) => {
25
+ if (settled) return;
26
+ settled = true;
27
+ const frame = `${JSON.stringify(value)}\n`;
28
+ if (Buffer.byteLength(frame, "utf8") > MAX_READINESS_FRAME_BYTES) {
29
+ close(readiness.fd);
30
+ throw new Error("Pi-subagents readiness frame exceeded its size limit.");
31
+ }
32
+ try {
33
+ writeFrame(readiness.fd, frame);
34
+ } finally {
35
+ close(readiness.fd);
36
+ }
37
+ };
38
+
39
+ pi.on("resources_discover", () => {
40
+ const active = new Set(pi.getActiveTools());
41
+ const missing = readiness.expectedTools.filter((tool) => !active.has(tool));
42
+ if (missing.length > 0) {
43
+ publish({ ok: false, error: `Unavailable subagent tools: ${missing.join(", ")}.` });
44
+ return;
45
+ }
46
+ const sources = new Map(pi.getAllTools().map((tool) => [tool.name, tool.sourceInfo.path]));
47
+ publish({
48
+ ok: true,
49
+ sources: readiness.expectedTools.map((tool) => {
50
+ const source = sources.get(tool);
51
+ return source ? toolSourceId(source) : "";
52
+ }),
53
+ });
54
+ });
55
+
56
+ pi.on("session_shutdown", () => {
57
+ publish({ ok: false, error: "Subagent child shut down before readiness completed." });
58
+ });
59
+ };
60
+ }
61
+
62
+ function writeReadinessFrame(fd: number, frame: string): void {
63
+ fs.writeFileSync(fd, frame, "utf8");
64
+ }
65
+
66
+ function closeDescriptor(fd: number): void {
67
+ try {
68
+ fs.closeSync(fd);
69
+ } catch {
70
+ // The descriptor may already be closed after a failed write.
71
+ }
72
+ }