@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.
- package/README.md +58 -49
- package/dist/child-communication-bridge.ts +7 -5
- package/dist/child-communication-bridge.ts.map +2 -2
- package/dist/child-readiness-probe.ts +68 -0
- package/dist/child-readiness-probe.ts.map +7 -0
- package/dist/chunks/chunk-3FCHXUKZ.ts +98 -0
- package/dist/chunks/chunk-3FCHXUKZ.ts.map +7 -0
- package/dist/chunks/{chunk-7JQOB375.ts → chunk-YMVSJ5GE.ts} +84 -132
- package/dist/chunks/{chunk-7JQOB375.ts.map → chunk-YMVSJ5GE.ts.map} +4 -4
- package/dist/chunks/chunk-ZOAFWLDJ.ts +130 -0
- package/dist/chunks/chunk-ZOAFWLDJ.ts.map +7 -0
- package/dist/index.ts +1195 -81
- package/dist/index.ts.map +4 -4
- package/docs/attachments.md +37 -0
- package/docs/design-principles.md +8 -2
- package/docs/messaging.md +23 -0
- package/docs/tools.md +22 -56
- package/package.json +8 -5
- package/src/attachment-utils.ts +104 -0
- package/src/broker-credentials.ts +106 -28
- package/src/child-communication-bridge.ts +3 -3
- package/src/child-communication-tools.ts +2 -24
- package/src/child-readiness-probe.ts +72 -0
- package/src/process.ts +391 -47
- package/src/resource-attachments.ts +724 -0
- package/src/runtime.ts +18 -1
- package/src/skill-attachments.ts +302 -0
- package/src/timeout.ts +24 -0
- package/src/tools.ts +96 -18
- package/src/types.ts +4 -0
package/README.md
CHANGED
|
@@ -7,8 +7,10 @@ Pi Subagents runs Pi jobs in separate child processes and supports authenticated
|
|
|
7
7
|
## ✨ Features
|
|
8
8
|
|
|
9
9
|
- Runs each job in an isolated Pi child process and returns its job ID immediately.
|
|
10
|
-
- Uses the task to define the child's specialization and
|
|
10
|
+
- Uses the task to define the child's specialization and explicit tools, skills, and extensions to define its capabilities.
|
|
11
11
|
- Defaults work tools to `read`, `grep`, `find`, and `ls`.
|
|
12
|
+
- Can attach validated local skills and trusted local extensions to one job without enabling automatic discovery.
|
|
13
|
+
- Verifies requested extension tools before sending the task to the child model.
|
|
12
14
|
- Inherits the main agent's effective model and uses its thinking level by default.
|
|
13
15
|
- Gives the main agent and every child a context-specific `subagent_send` contract for bidirectional requests and responses.
|
|
14
16
|
- Gives every child `subagent_wait` for an answer to a child-originated request.
|
|
@@ -68,8 +70,8 @@ Use messaging only when needed:
|
|
|
68
70
|
|
|
69
71
|
Completion messages follow Pi's global tool-output expansion state and the `app.tools.expand` binding (`Ctrl+O` by default).
|
|
70
72
|
|
|
71
|
-
In TUI mode, the above-editor widget shows each queued or running job's ID, state, elapsed time, timeout, and selected
|
|
72
|
-
|
|
73
|
+
In TUI mode, the above-editor widget shows each queued or running job's ID, state, elapsed time, timeout, and selected core and extension tool names.
|
|
74
|
+
It omits fixed communication tools, attachment paths, and resource totals, disappears when no jobs remain active, and clears when the session ends.
|
|
73
75
|
|
|
74
76
|
## 🛠️ Tools
|
|
75
77
|
|
|
@@ -77,7 +79,7 @@ The main Pi session exposes five fixed tools:
|
|
|
77
79
|
|
|
78
80
|
| Tool | Parameters | Purpose |
|
|
79
81
|
| --- | --- | --- |
|
|
80
|
-
| `subagent_spawn` | `task`, optional `tools`, `thinkingLevel`, `timeout` | Start one subagent job and return its `jobId`. |
|
|
82
|
+
| `subagent_spawn` | `task`, optional `tools`, `skills`, `extensions`, `thinkingLevel`, `timeout` | Start one subagent job and return its `jobId`. |
|
|
81
83
|
| `subagent_inspect` | none | List privacy-filtered retained-job metadata. |
|
|
82
84
|
| `subagent_cancel` | `jobId` | Idempotently cancel one queued or running job. |
|
|
83
85
|
| `subagent_wait` | `jobId`, optional `timeout` | Wait for a job or return early for an incoming child message. |
|
|
@@ -107,9 +109,9 @@ Tasks are limited to 50 KiB of UTF-8 text.
|
|
|
107
109
|
Requests and responses are limited to 48 KiB and 1,992 lines so their protocol envelopes fit Pi's 50 KiB and 2,000-line model-text bounds without truncating accepted content.
|
|
108
110
|
Each job may have up to four unresolved or answered-but-not-consumed requests across both directions.
|
|
109
111
|
The terminal states are `completed`, `partial`, `failed`, `timed_out`, and `cancelled`.
|
|
110
|
-
`subagent_inspect` never returns complete task text, child output, prompts, selected tools, context, credentials, environment variables, requests, responses, or secrets.
|
|
112
|
+
`subagent_inspect` never returns complete task text, child output, prompts, selected tools, attachment paths or totals, context, credentials, environment variables, requests, responses, or secrets.
|
|
111
113
|
|
|
112
|
-
See [`docs/tools.md`](./docs/tools.md) for the concise schema reference.
|
|
114
|
+
See [`docs/tools.md`](./docs/tools.md) for the concise schema reference, [`docs/attachments.md`](./docs/attachments.md) for attachment validation, and [`docs/messaging.md`](./docs/messaging.md) for messaging behavior.
|
|
113
115
|
|
|
114
116
|
## ⚙️ Job configuration
|
|
115
117
|
|
|
@@ -118,50 +120,50 @@ For reusable delegation policy, you can create your own project skill under `.pi
|
|
|
118
120
|
Choose its name, trigger, tool policy, task format, and verification workflow for your use case.
|
|
119
121
|
The package intentionally registers and publishes no skill; the repository-only [`using-pi-subagents` example](https://github.com/narumiruna/pi-extensions/tree/main/packages/pi-subagents/skills/using-pi-subagents) is an optional starting point.
|
|
120
122
|
|
|
121
|
-
|
|
123
|
+
`subagent_spawn` accepts this additive configuration:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
{
|
|
127
|
+
task: string;
|
|
128
|
+
tools?: Array<"read" | "bash" | "powershell" | "edit" | "write" | "grep" | "find" | "ls">;
|
|
129
|
+
skills?: string[];
|
|
130
|
+
extensions?: Array<{ path: string; tools: string[] }>;
|
|
131
|
+
thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
132
|
+
timeout?: number;
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The optional `tools` list selects Pi core work tools.
|
|
137
|
+
Omitting it selects `read`, `grep`, `find`, and `ls`, while an empty list gives the child no core work tools.
|
|
138
|
+
The runtime always adds `subagent_send` and child `subagent_wait` and removes duplicate names.
|
|
139
|
+
Adding `edit` or `write` lets the child modify files, while `bash` or `powershell` grants unrestricted command execution.
|
|
122
140
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
- Omitting `tools` selects `read`, `grep`, `find`, and `ls`.
|
|
126
|
-
- Passing an empty list gives the child no work tools.
|
|
127
|
-
- The runtime always adds `subagent_send` and child `subagent_wait` and removes duplicate names.
|
|
141
|
+
The optional `skills` list attaches local Pi skills through progressive disclosure without injecting their full bodies or adding tools.
|
|
142
|
+
Every attached skill path must contain at least one loadable skill, every non-ignored declared skill must load successfully, and skill names must be unique across attachments.
|
|
128
143
|
|
|
129
|
-
|
|
130
|
-
|
|
144
|
+
Each `extensions` entry loads a trusted local extension file or directory and selects its initial extension tools.
|
|
145
|
+
An empty `tools` list loads provider or lifecycle behavior without initially exposing extension tools.
|
|
146
|
+
The parent checks that each requested tool comes from its specified attachment before the child model receives the task.
|
|
147
|
+
Attached extensions run with the child's user permissions and are not a sandbox.
|
|
131
148
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
149
|
+
Attachments must be existing local paths, not npm, Git, or URL sources.
|
|
150
|
+
Project-local attachments and resources resolved from their packages require project trust, including paths that symlink into the project.
|
|
151
|
+
A job accepts up to 16 skill paths, 16 extension entries, and 64 selected work tools.
|
|
152
|
+
The child inherits the main agent's effective model and thinking level by default; there is no per-job model override.
|
|
153
|
+
Parent-only extension providers require an attached extension that registers them in the child.
|
|
154
|
+
Process-local runtime API keys, including a parent-only `--api-key`, are unavailable to the child; use stored or inherited environment credentials.
|
|
135
155
|
|
|
136
|
-
|
|
137
|
-
Spawn also rejects process-local runtime API keys, including a parent-only `--api-key` value.
|
|
138
|
-
Use stored or environment credentials that child processes can read.
|
|
139
|
-
The extension does not expose a per-job model override.
|
|
156
|
+
See [Attachment behavior](./docs/attachments.md) for detailed path checks, package preflight, tool ownership, and resource limits.
|
|
140
157
|
|
|
141
158
|
## 🔄 Messaging, lifecycle, and retention
|
|
142
159
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
The
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
A main-originated request to a queued job waits for RPC readiness before delivery is accepted.
|
|
151
|
-
After RPC accepts the steering message, the runtime interrupts active child response waits so the queued request can reach the child model.
|
|
152
|
-
Caller cancellation before RPC delivery starts rolls the request back.
|
|
153
|
-
Once RPC delivery starts, cancellation stops only the caller's wait; the request may still arrive and remains answerable until delivery fails or the job terminates.
|
|
154
|
-
The interrupted child-originated requests remain active and retryable.
|
|
155
|
-
|
|
156
|
-
The first accepted `subagent_send` response wins.
|
|
157
|
-
Repeated responses acknowledge the existing answer without replacing it.
|
|
158
|
-
A child may retry `subagent_wait` after a wait timeout because the underlying request remains active.
|
|
159
|
-
|
|
160
|
-
A new job starts as `queued`, transitions to `running`, and reaches exactly one terminal state.
|
|
161
|
-
The runtime retains up to 32 recent terminal records for up to 24 hours within the current extension session.
|
|
162
|
-
Inspection reports older records removed by retention bounds through `omitted.jobs`.
|
|
163
|
-
Cancelling or terminalizing a job revokes its token and rejects pending child waits before stale output can replace the terminal state.
|
|
164
|
-
Session replacement and shutdown cancel active work, suppress stale completion delivery, revoke credentials, close sockets, and stop the broker.
|
|
160
|
+
A main-originated request to a queued job waits for Pi RPC readiness before delivery. After delivery starts, cancellation stops only the caller's wait; the request may still arrive. An interrupted child-originated request remains active and can be waited on again.
|
|
161
|
+
|
|
162
|
+
The first accepted `subagent_send` response wins; repeated responses do not replace it. The execution timeout starts only after Pi accepts the RPC prompt. Jobs reach exactly one terminal state, and inspection retains up to 32 terminal records for up to 24 hours within the current session.
|
|
163
|
+
|
|
164
|
+
The broker uses loopback connections and per-job credentials passed through a private pipe, not the child's initial environment or command line. Cancelling a job or replacing the session revokes those credentials, stops pending work, and prevents stale completion delivery.
|
|
165
|
+
|
|
166
|
+
See [Messaging and lifecycle](./docs/messaging.md) for delivery, retry, retention, and shutdown details.
|
|
165
167
|
|
|
166
168
|
## 🔀 Migrating from 2.x
|
|
167
169
|
|
|
@@ -190,9 +192,10 @@ The default list contains no shell or file-mutation tool.
|
|
|
190
192
|
It is not a filesystem sandbox because its read tools can inspect files available to the user account.
|
|
191
193
|
Selecting `bash`, `powershell`, `edit`, or `write` permits workspace mutation with the Pi process environment and user permissions.
|
|
192
194
|
|
|
193
|
-
Every child disables session persistence
|
|
194
|
-
|
|
195
|
-
|
|
195
|
+
Every child disables session persistence and automatic discovery of extensions, skills, and prompt templates.
|
|
196
|
+
Only the communication bridge and explicitly attached resources are added back for that job.
|
|
197
|
+
Provider selection supports Pi's child-visible built-in and configured providers plus providers registered by an attached extension.
|
|
198
|
+
Credentials must be available independently to the child through Pi's stored credentials or inherited environment.
|
|
196
199
|
|
|
197
200
|
The broker accepts only loopback TCP connections with an active per-job token.
|
|
198
201
|
The token is bootstrapped through a private inherited pipe and is absent from the child's initial environment and command line.
|
|
@@ -201,14 +204,20 @@ A child request or response is visible main-agent model context, but its envelop
|
|
|
201
204
|
A child message cannot grant permission for writes, shell commands, credential access, or other privileged actions.
|
|
202
205
|
A main-agent request is visible child model context, but it cannot expand the child's selected tools or grant capabilities the child did not receive at spawn time.
|
|
203
206
|
|
|
207
|
+
An attached skill contributes model instructions and may direct already selected tools to bundled helpers.
|
|
208
|
+
An attached extension is fully privileged executable code with the child process's user permissions, can run during factory and lifecycle hooks, can alter prompts or tool behavior, and can change active tools after startup.
|
|
209
|
+
Its requested tool list controls the initial provider-visible loadout but is not an operating-system sandbox.
|
|
210
|
+
Canonical attachment paths are passed to Pi in the child command line, but parent-generated inspection, completion, and broker metadata omit them and child diagnostic errors redact requested attachment roots before publication.
|
|
211
|
+
Attached code and model output remain untrusted and can disclose paths they can access, so attach only code you trust.
|
|
212
|
+
|
|
204
213
|
Terminal controls and bidirectional controls are stripped before untrusted child text is displayed.
|
|
205
214
|
Tasks, repository context, requests, responses, and inspected file content may be sent to the selected model provider.
|
|
206
215
|
Parallel writers require disjoint ownership or workspace isolation outside this extension.
|
|
207
216
|
|
|
208
217
|
## 🚧 Limitations
|
|
209
218
|
|
|
210
|
-
-
|
|
211
|
-
-
|
|
219
|
+
- Attachments must be existing local files or directories; package, Git, URL, and automatic parent-resource inheritance are unsupported.
|
|
220
|
+
- An attached extension may recreate a parent-registered provider, but parent in-memory extension state and runtime-only API keys are not forwarded.
|
|
212
221
|
- The extension does not provide custom agents, per-job models, custom system prompts, peer-to-peer child messaging, retained conversations, user-directed follow-up work, mailboxes, Agent Teams, chains, fan-in aggregators, panels, workflow DAGs, dynamic scheduling, verification orchestration, nested subagents, or extension-owned semantic memory.
|
|
213
222
|
- Bidirectional messages use request-response coordination, not a retained conversational session.
|
|
214
223
|
- The main agent must verify child claims against the actual diff and deterministic checks.
|
|
@@ -222,7 +231,7 @@ packages/pi-subagents/
|
|
|
222
231
|
├── src/ # Authoritative implementation and helpers
|
|
223
232
|
│ ├── index.ts # Thin Pi entrypoint
|
|
224
233
|
│ └── subagents.ts # Job, broker, and child lifecycle
|
|
225
|
-
├── dist/ # Generated Jiti runtime and
|
|
234
|
+
├── dist/ # Generated Jiti runtime, child bridge, and readiness probe
|
|
226
235
|
├── scripts/build-runtime.mjs # Runtime builder
|
|
227
236
|
├── docs/ # Published reference documentation
|
|
228
237
|
├── skills/using-pi-subagents/ # Repository-only example; not published
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
2
|
// @ts-nocheck -- generated entries use .ts extensions for Pi's Jiti loader.
|
|
3
|
+
import {
|
|
4
|
+
createChildCommunicationExtension
|
|
5
|
+
} from "./chunks/chunk-ZOAFWLDJ.ts";
|
|
3
6
|
import {
|
|
4
7
|
MAX_FRAME_BYTES,
|
|
5
8
|
MAX_IDENTIFIER_LENGTH,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from "./chunks/chunk-7JQOB375.ts";
|
|
9
|
+
captureChildBootstrap
|
|
10
|
+
} from "./chunks/chunk-YMVSJ5GE.ts";
|
|
9
11
|
|
|
10
12
|
// src/child-communication-bridge.ts
|
|
11
13
|
import net from "node:net";
|
|
12
14
|
var CONNECT_TIMEOUT_MS = 2e3;
|
|
13
15
|
var SEND_RESPONSE_TIMEOUT_MS = 5e3;
|
|
14
|
-
var captured =
|
|
15
|
-
var childCommunicationBridge = captured ? createChildCommunicationExtension(createBrokerClient(captured)) : () => void 0;
|
|
16
|
+
var captured = captureChildBootstrap();
|
|
17
|
+
var childCommunicationBridge = captured ? createChildCommunicationExtension(createBrokerClient(captured.communication)) : () => void 0;
|
|
16
18
|
var child_communication_bridge_default = childCommunicationBridge;
|
|
17
19
|
function createBrokerClient(credentials) {
|
|
18
20
|
return {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/child-communication-bridge.ts"],
|
|
4
|
-
"sourcesContent": ["import net from \"node:net\";\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport {
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import net from \"node:net\";\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport { captureChildBootstrap } from \"./broker-credentials.js\";\nimport { type ChildCommunicationClient, createChildCommunicationExtension } from \"./child-communication-tools.js\";\nimport { MAX_FRAME_BYTES, MAX_IDENTIFIER_LENGTH } from \"./message-broker.js\";\nimport type { BrokerCredentials } from \"./types.js\";\n\nconst CONNECT_TIMEOUT_MS = 2_000;\nconst SEND_RESPONSE_TIMEOUT_MS = 5_000;\n\nconst captured = captureChildBootstrap();\n\nconst childCommunicationBridge: ExtensionFactory = captured\n ? createChildCommunicationExtension(createBrokerClient(captured.communication))\n : () => undefined;\n\nexport default childCommunicationBridge;\n\nexport function createBrokerClient(credentials: BrokerCredentials): ChildCommunicationClient {\n return {\n async send(params, signal) {\n const response = await requestBroker(\n credentials,\n { type: \"send\", token: credentials.token, ...params },\n signal,\n SEND_RESPONSE_TIMEOUT_MS,\n );\n if (response.ok !== true) throw brokerError(response);\n if (\n typeof response.requestId !== \"string\" ||\n !response.requestId ||\n response.requestId.length > MAX_IDENTIFIER_LENGTH ||\n typeof response.accepted !== \"boolean\" ||\n typeof response.duplicate !== \"boolean\"\n ) {\n throw new Error(\"Subagent broker returned an invalid send acknowledgement.\");\n }\n return {\n requestId: response.requestId,\n accepted: response.accepted,\n duplicate: response.duplicate,\n };\n },\n async wait(requestId, timeoutMs, signal) {\n const response = await requestBroker(\n credentials,\n {\n type: \"wait\",\n token: credentials.token,\n requestId,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n },\n signal,\n );\n if (response.ok !== true) throw brokerError(response);\n if (typeof response.response !== \"string\") {\n throw new Error(\"Subagent broker returned an invalid plain-text response.\");\n }\n return response.response;\n },\n };\n}\n\nfunction requestBroker(\n credentials: BrokerCredentials,\n request: Record<string, unknown>,\n signal?: AbortSignal,\n responseTimeoutMs?: number,\n): Promise<Record<string, unknown>> {\n if (signal?.aborted) return Promise.reject(abortError(\"Subagent broker request was cancelled.\"));\n return new Promise((resolve, reject) => {\n const socket = net.createConnection({ host: credentials.host, port: credentials.port });\n let response = Buffer.alloc(0);\n let settled = false;\n let responseTimer: NodeJS.Timeout | undefined;\n const connectTimer = setTimeout(\n () => finish(new Error(\"Subagent broker connection timed out.\")),\n CONNECT_TIMEOUT_MS,\n );\n connectTimer.unref();\n const onAbort = () => finish(abortError(\"Subagent broker request was cancelled.\"));\n const finish = (error?: Error, value?: Record<string, unknown>) => {\n if (settled) return;\n settled = true;\n clearTimeout(connectTimer);\n if (responseTimer) clearTimeout(responseTimer);\n signal?.removeEventListener(\"abort\", onAbort);\n socket.destroy();\n if (error) reject(error);\n else resolve(value ?? {});\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n socket.once(\"connect\", () => {\n clearTimeout(connectTimer);\n if (responseTimeoutMs !== undefined) {\n responseTimer = setTimeout(() => finish(new Error(\"Subagent broker response timed out.\")), responseTimeoutMs);\n responseTimer.unref();\n }\n socket.write(`${JSON.stringify(request)}\\n`);\n });\n socket.on(\"data\", (chunk: Buffer) => {\n if (settled) return;\n response = Buffer.concat([response, chunk]);\n if (response.byteLength > MAX_FRAME_BYTES) {\n finish(new Error(\"Subagent broker response exceeded its size limit.\"));\n return;\n }\n const newline = response.indexOf(0x0a);\n if (newline < 0) return;\n const trailing = response\n .subarray(newline + 1)\n .toString(\"utf8\")\n .trim();\n if (trailing) {\n finish(new Error(\"Subagent broker returned more than one response frame.\"));\n return;\n }\n try {\n const parsed = JSON.parse(response.subarray(0, newline).toString(\"utf8\")) as unknown;\n if (!isRecord(parsed)) throw new Error();\n finish(undefined, parsed);\n } catch {\n finish(new Error(\"Subagent broker returned malformed JSON.\"));\n }\n });\n socket.once(\"error\", (error) => finish(error));\n socket.once(\"close\", () => {\n if (!settled) finish(new Error(\"Subagent broker closed without a response.\"));\n });\n });\n}\n\nfunction brokerError(response: Record<string, unknown>): Error {\n return new Error(typeof response.error === \"string\" ? response.error : \"Subagent broker rejected the request.\");\n}\n\nfunction abortError(message: string): Error {\n const error = new Error(message);\n error.name = \"AbortError\";\n return error;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
|
+
// @ts-nocheck -- generated entries use .ts extensions for Pi's Jiti loader.
|
|
3
|
+
import {
|
|
4
|
+
toolSourceId
|
|
5
|
+
} from "./chunks/chunk-3FCHXUKZ.ts";
|
|
6
|
+
import {
|
|
7
|
+
MAX_READINESS_FRAME_BYTES,
|
|
8
|
+
takeCapturedReadiness
|
|
9
|
+
} from "./chunks/chunk-YMVSJ5GE.ts";
|
|
10
|
+
|
|
11
|
+
// src/child-readiness-probe.ts
|
|
12
|
+
import * as fs from "node:fs";
|
|
13
|
+
var captured = takeCapturedReadiness();
|
|
14
|
+
var childReadinessProbe = captured ? createChildReadinessProbe(captured) : () => void 0;
|
|
15
|
+
var child_readiness_probe_default = childReadinessProbe;
|
|
16
|
+
function createChildReadinessProbe(readiness, writeFrame = writeReadinessFrame, close = closeDescriptor) {
|
|
17
|
+
return (pi) => {
|
|
18
|
+
let settled = false;
|
|
19
|
+
const publish = (value) => {
|
|
20
|
+
if (settled) return;
|
|
21
|
+
settled = true;
|
|
22
|
+
const frame = `${JSON.stringify(value)}
|
|
23
|
+
`;
|
|
24
|
+
if (Buffer.byteLength(frame, "utf8") > MAX_READINESS_FRAME_BYTES) {
|
|
25
|
+
close(readiness.fd);
|
|
26
|
+
throw new Error("Pi-subagents readiness frame exceeded its size limit.");
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
writeFrame(readiness.fd, frame);
|
|
30
|
+
} finally {
|
|
31
|
+
close(readiness.fd);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
pi.on("resources_discover", () => {
|
|
35
|
+
const active = new Set(pi.getActiveTools());
|
|
36
|
+
const missing = readiness.expectedTools.filter((tool) => !active.has(tool));
|
|
37
|
+
if (missing.length > 0) {
|
|
38
|
+
publish({ ok: false, error: `Unavailable subagent tools: ${missing.join(", ")}.` });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const sources = new Map(pi.getAllTools().map((tool) => [tool.name, tool.sourceInfo.path]));
|
|
42
|
+
publish({
|
|
43
|
+
ok: true,
|
|
44
|
+
sources: readiness.expectedTools.map((tool) => {
|
|
45
|
+
const source = sources.get(tool);
|
|
46
|
+
return source ? toolSourceId(source) : "";
|
|
47
|
+
})
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
pi.on("session_shutdown", () => {
|
|
51
|
+
publish({ ok: false, error: "Subagent child shut down before readiness completed." });
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function writeReadinessFrame(fd, frame) {
|
|
56
|
+
fs.writeFileSync(fd, frame, "utf8");
|
|
57
|
+
}
|
|
58
|
+
function closeDescriptor(fd) {
|
|
59
|
+
try {
|
|
60
|
+
fs.closeSync(fd);
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
createChildReadinessProbe,
|
|
66
|
+
child_readiness_probe_default as default
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=child-readiness-probe.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/child-readiness-probe.ts"],
|
|
4
|
+
"sourcesContent": ["import * as fs from \"node:fs\";\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport { toolSourceId } from \"./attachment-utils.js\";\nimport { MAX_READINESS_FRAME_BYTES, takeCapturedReadiness } from \"./broker-credentials.js\";\n\nexport interface ChildReadinessState {\n fd: number;\n expectedTools: string[];\n}\n\nconst captured = takeCapturedReadiness();\n\nconst childReadinessProbe: ExtensionFactory = captured ? createChildReadinessProbe(captured) : () => undefined;\n\nexport default childReadinessProbe;\n\nexport function createChildReadinessProbe(\n readiness: ChildReadinessState,\n writeFrame: (fd: number, frame: string) => void = writeReadinessFrame,\n close: (fd: number) => void = closeDescriptor,\n): ExtensionFactory {\n return (pi) => {\n let settled = false;\n const publish = (value: { ok: true; sources: string[] } | { ok: false; error: string }) => {\n if (settled) return;\n settled = true;\n const frame = `${JSON.stringify(value)}\\n`;\n if (Buffer.byteLength(frame, \"utf8\") > MAX_READINESS_FRAME_BYTES) {\n close(readiness.fd);\n throw new Error(\"Pi-subagents readiness frame exceeded its size limit.\");\n }\n try {\n writeFrame(readiness.fd, frame);\n } finally {\n close(readiness.fd);\n }\n };\n\n pi.on(\"resources_discover\", () => {\n const active = new Set(pi.getActiveTools());\n const missing = readiness.expectedTools.filter((tool) => !active.has(tool));\n if (missing.length > 0) {\n publish({ ok: false, error: `Unavailable subagent tools: ${missing.join(\", \")}.` });\n return;\n }\n const sources = new Map(pi.getAllTools().map((tool) => [tool.name, tool.sourceInfo.path]));\n publish({\n ok: true,\n sources: readiness.expectedTools.map((tool) => {\n const source = sources.get(tool);\n return source ? toolSourceId(source) : \"\";\n }),\n });\n });\n\n pi.on(\"session_shutdown\", () => {\n publish({ ok: false, error: \"Subagent child shut down before readiness completed.\" });\n });\n };\n}\n\nfunction writeReadinessFrame(fd: number, frame: string): void {\n fs.writeFileSync(fd, frame, \"utf8\");\n}\n\nfunction closeDescriptor(fd: number): void {\n try {\n fs.closeSync(fd);\n } catch {\n // The descriptor may already be closed after a failed write.\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;AAAA,YAAY,QAAQ;AAUpB,IAAM,WAAW,sBAAsB;AAEvC,IAAM,sBAAwC,WAAW,0BAA0B,QAAQ,IAAI,MAAM;AAErG,IAAO,gCAAQ;AAER,SAAS,0BACd,WACA,aAAkD,qBAClD,QAA8B,iBACZ;AAClB,SAAO,CAAC,OAAO;AACb,QAAI,UAAU;AACd,UAAM,UAAU,CAAC,UAA0E;AACzF,UAAI,QAAS;AACb,gBAAU;AACV,YAAM,QAAQ,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA;AACtC,UAAI,OAAO,WAAW,OAAO,MAAM,IAAI,2BAA2B;AAChE,cAAM,UAAU,EAAE;AAClB,cAAM,IAAI,MAAM,uDAAuD;AAAA,MACzE;AACA,UAAI;AACF,mBAAW,UAAU,IAAI,KAAK;AAAA,MAChC,UAAE;AACA,cAAM,UAAU,EAAE;AAAA,MACpB;AAAA,IACF;AAEA,OAAG,GAAG,sBAAsB,MAAM;AAChC,YAAM,SAAS,IAAI,IAAI,GAAG,eAAe,CAAC;AAC1C,YAAM,UAAU,UAAU,cAAc,OAAO,CAAC,SAAS,CAAC,OAAO,IAAI,IAAI,CAAC;AAC1E,UAAI,QAAQ,SAAS,GAAG;AACtB,gBAAQ,EAAE,IAAI,OAAO,OAAO,+BAA+B,QAAQ,KAAK,IAAI,CAAC,IAAI,CAAC;AAClF;AAAA,MACF;AACA,YAAM,UAAU,IAAI,IAAI,GAAG,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,MAAM,KAAK,WAAW,IAAI,CAAC,CAAC;AACzF,cAAQ;AAAA,QACN,IAAI;AAAA,QACJ,SAAS,UAAU,cAAc,IAAI,CAAC,SAAS;AAC7C,gBAAM,SAAS,QAAQ,IAAI,IAAI;AAC/B,iBAAO,SAAS,aAAa,MAAM,IAAI;AAAA,QACzC,CAAC;AAAA,MACH,CAAC;AAAA,IACH,CAAC;AAED,OAAG,GAAG,oBAAoB,MAAM;AAC9B,cAAQ,EAAE,IAAI,OAAO,OAAO,uDAAuD,CAAC;AAAA,IACtF,CAAC;AAAA,EACH;AACF;AAEA,SAAS,oBAAoB,IAAY,OAAqB;AAC5D,EAAG,iBAAc,IAAI,OAAO,MAAM;AACpC;AAEA,SAAS,gBAAgB,IAAkB;AACzC,MAAI;AACF,IAAG,aAAU,EAAE;AAAA,EACjB,QAAQ;AAAA,EAER;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
|
+
// @ts-nocheck -- generated entries use .ts extensions for Pi's Jiti loader.
|
|
3
|
+
import {
|
|
4
|
+
sanitizeTerminalText
|
|
5
|
+
} from "./chunk-YMVSJ5GE.ts";
|
|
6
|
+
|
|
7
|
+
// src/attachment-utils.ts
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
import { realpathSync } from "node:fs";
|
|
10
|
+
import { readFile, stat } from "node:fs/promises";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
var ATTACHMENT_IGNORE_FILE_NAMES = [".gitignore", ".ignore", ".fdignore"];
|
|
13
|
+
function throwInvalidAttachmentIgnoreFile() {
|
|
14
|
+
throw new Error("Subagent attachment ignore files must be regular files.");
|
|
15
|
+
}
|
|
16
|
+
function throwIfAttachmentAborted(signal) {
|
|
17
|
+
if (!signal?.aborted) return;
|
|
18
|
+
const error = new Error("Subagent attachment validation was cancelled.");
|
|
19
|
+
error.name = "AbortError";
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
function isAbortError(error) {
|
|
23
|
+
return error instanceof Error && error.name === "AbortError";
|
|
24
|
+
}
|
|
25
|
+
function prefixIgnorePattern(line, prefix) {
|
|
26
|
+
const trimmed = line.trim();
|
|
27
|
+
if (!trimmed || trimmed.startsWith("#") && !trimmed.startsWith("\\#")) return null;
|
|
28
|
+
let pattern = line;
|
|
29
|
+
let negated = false;
|
|
30
|
+
if (pattern.startsWith("!")) {
|
|
31
|
+
negated = true;
|
|
32
|
+
pattern = pattern.slice(1);
|
|
33
|
+
} else if (pattern.startsWith("\\!")) {
|
|
34
|
+
pattern = pattern.slice(1);
|
|
35
|
+
}
|
|
36
|
+
if (pattern.startsWith("/")) pattern = pattern.slice(1);
|
|
37
|
+
const prefixed = prefix ? `${prefix}${pattern}` : pattern;
|
|
38
|
+
return negated ? `!${prefixed}` : prefixed;
|
|
39
|
+
}
|
|
40
|
+
async function addAttachmentIgnoreRules(ignoreMatcher, directory, rootDirectory, signal, accountForFile) {
|
|
41
|
+
const relativeDirectory = path.relative(rootDirectory, directory);
|
|
42
|
+
const prefix = relativeDirectory ? `${toPosixPath(relativeDirectory)}/` : "";
|
|
43
|
+
for (const filename of ATTACHMENT_IGNORE_FILE_NAMES) {
|
|
44
|
+
throwIfAttachmentAborted(signal);
|
|
45
|
+
const ignorePath = path.join(directory, filename);
|
|
46
|
+
let ignoreStats;
|
|
47
|
+
try {
|
|
48
|
+
ignoreStats = await stat(ignorePath);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (isAbortError(error)) throw error;
|
|
51
|
+
throwIfAttachmentAborted(signal);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (!ignoreStats.isFile()) throwInvalidAttachmentIgnoreFile();
|
|
55
|
+
accountForFile(ignorePath, ignoreStats.size);
|
|
56
|
+
try {
|
|
57
|
+
const content = await readFile(ignorePath, { encoding: "utf8", signal });
|
|
58
|
+
throwIfAttachmentAborted(signal);
|
|
59
|
+
const patterns = content.split(/\r?\n/u).map((line) => prefixIgnorePattern(line, prefix)).filter((line) => line !== null);
|
|
60
|
+
if (patterns.length > 0) ignoreMatcher.add(patterns);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (isAbortError(error)) throw error;
|
|
63
|
+
throwIfAttachmentAborted(signal);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function toPosixPath(value) {
|
|
68
|
+
return value.split(path.sep).join("/");
|
|
69
|
+
}
|
|
70
|
+
function toolSourceId(value) {
|
|
71
|
+
return createHash("sha256").update(value).digest("hex");
|
|
72
|
+
}
|
|
73
|
+
function realpath(value, label) {
|
|
74
|
+
try {
|
|
75
|
+
return realpathSync(value);
|
|
76
|
+
} catch {
|
|
77
|
+
throw new Error(`${label} path does not exist: ${sanitizeTerminalText(value).slice(0, 512)}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function isWithin(root, candidate) {
|
|
81
|
+
const relative2 = path.relative(root, candidate);
|
|
82
|
+
return relative2 === "" || !relative2.startsWith(`..${path.sep}`) && relative2 !== ".." && !path.isAbsolute(relative2);
|
|
83
|
+
}
|
|
84
|
+
function isRecord(value) {
|
|
85
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export {
|
|
89
|
+
throwIfAttachmentAborted,
|
|
90
|
+
isAbortError,
|
|
91
|
+
addAttachmentIgnoreRules,
|
|
92
|
+
toPosixPath,
|
|
93
|
+
toolSourceId,
|
|
94
|
+
realpath,
|
|
95
|
+
isWithin,
|
|
96
|
+
isRecord
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=chunk-3FCHXUKZ.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/attachment-utils.ts"],
|
|
4
|
+
"sourcesContent": ["import { createHash } from \"node:crypto\";\nimport { realpathSync } from \"node:fs\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type ignore from \"ignore\";\nimport { sanitizeTerminalText } from \"./message-broker.js\";\n\nconst ATTACHMENT_IGNORE_FILE_NAMES = [\".gitignore\", \".ignore\", \".fdignore\"];\n\nexport type IgnoreMatcher = ReturnType<typeof ignore>;\n\nfunction throwInvalidAttachmentIgnoreFile(): never {\n throw new Error(\"Subagent attachment ignore files must be regular files.\");\n}\n\nexport function throwIfAttachmentAborted(signal: AbortSignal | undefined): void {\n if (!signal?.aborted) return;\n const error = new Error(\"Subagent attachment validation was cancelled.\");\n error.name = \"AbortError\";\n throw error;\n}\n\nexport function isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === \"AbortError\";\n}\n\nfunction prefixIgnorePattern(line: string, prefix: string): string | null {\n const trimmed = line.trim();\n if (!trimmed || (trimmed.startsWith(\"#\") && !trimmed.startsWith(\"\\\\#\"))) return null;\n let pattern = line;\n let negated = false;\n if (pattern.startsWith(\"!\")) {\n negated = true;\n pattern = pattern.slice(1);\n } else if (pattern.startsWith(\"\\\\!\")) {\n pattern = pattern.slice(1);\n }\n if (pattern.startsWith(\"/\")) pattern = pattern.slice(1);\n const prefixed = prefix ? `${prefix}${pattern}` : pattern;\n return negated ? `!${prefixed}` : prefixed;\n}\n\nexport async function addAttachmentIgnoreRules(\n ignoreMatcher: IgnoreMatcher,\n directory: string,\n rootDirectory: string,\n signal: AbortSignal | undefined,\n accountForFile: (ignorePath: string, bytes: number) => void,\n): Promise<void> {\n const relativeDirectory = path.relative(rootDirectory, directory);\n const prefix = relativeDirectory ? `${toPosixPath(relativeDirectory)}/` : \"\";\n for (const filename of ATTACHMENT_IGNORE_FILE_NAMES) {\n throwIfAttachmentAborted(signal);\n const ignorePath = path.join(directory, filename);\n let ignoreStats: Awaited<ReturnType<typeof stat>>;\n try {\n ignoreStats = await stat(ignorePath);\n } catch (error) {\n if (isAbortError(error)) throw error;\n throwIfAttachmentAborted(signal);\n continue;\n }\n if (!ignoreStats.isFile()) throwInvalidAttachmentIgnoreFile();\n accountForFile(ignorePath, ignoreStats.size);\n try {\n const content = await readFile(ignorePath, { encoding: \"utf8\", signal });\n throwIfAttachmentAborted(signal);\n const patterns = content\n .split(/\\r?\\n/u)\n .map((line) => prefixIgnorePattern(line, prefix))\n .filter((line): line is string => line !== null);\n if (patterns.length > 0) ignoreMatcher.add(patterns);\n } catch (error) {\n if (isAbortError(error)) throw error;\n throwIfAttachmentAborted(signal);\n // Pi ignores unreadable ignore files.\n }\n }\n}\n\nexport function toPosixPath(value: string): string {\n return value.split(path.sep).join(\"/\");\n}\n\nexport function toolSourceId(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nexport function realpath(value: string, label: string): string {\n try {\n return realpathSync(value);\n } catch {\n throw new Error(`${label} path does not exist: ${sanitizeTerminalText(value).slice(0, 512)}`);\n }\n}\n\nexport function isWithin(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative === \"\" || (!relative.startsWith(`..${path.sep}`) && relative !== \"..\" && !path.isAbsolute(relative));\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["relative"]
|
|
7
|
+
}
|