@narumitw/pi-subagents 3.0.1 → 3.1.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/README.md +80 -136
- package/dist/child-communication-bridge.ts +9 -12
- 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-UKTWUQRM.js → chunk-YMVSJ5GE.ts} +89 -152
- package/dist/chunks/chunk-YMVSJ5GE.ts.map +7 -0
- package/dist/chunks/chunk-ZOAFWLDJ.ts +130 -0
- package/dist/chunks/chunk-ZOAFWLDJ.ts.map +7 -0
- package/dist/index.ts +1201 -120
- 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 +56 -54
- package/src/attachment-utils.ts +104 -0
- package/src/broker-credentials.ts +132 -54
- package/src/child-communication-bridge.ts +117 -125
- package/src/child-communication-tools.ts +89 -117
- package/src/child-readiness-probe.ts +72 -0
- package/src/completion-renderer.ts +46 -48
- package/src/message-broker.ts +531 -561
- package/src/model-output.ts +25 -28
- package/src/process.ts +942 -619
- package/src/resource-attachments.ts +724 -0
- package/src/runtime.ts +471 -478
- package/src/skill-attachments.ts +302 -0
- package/src/subagents.ts +20 -23
- package/src/timeout.ts +24 -0
- package/src/tools.ts +389 -325
- package/src/types.ts +45 -58
- package/src/widget.ts +85 -91
- package/dist/chunks/chunk-UKTWUQRM.js.map +0 -7
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.
|
|
@@ -20,7 +22,6 @@ Pi Subagents runs Pi jobs in separate child processes and supports authenticated
|
|
|
20
22
|
## 📦 Install
|
|
21
23
|
|
|
22
24
|
The version 3 runtime documented here is not yet published to npm.
|
|
23
|
-
|
|
24
25
|
The npm package still contains the legacy 2.x runtime and does not provide the tools below.
|
|
25
26
|
|
|
26
27
|
Install the repository source as one Pi package:
|
|
@@ -49,40 +50,28 @@ pi --no-extensions -e ./packages/pi-subagents
|
|
|
49
50
|
```
|
|
50
51
|
|
|
51
52
|
The package entry is generated at `dist/index.ts` and loaded through Pi's Jiti runtime.
|
|
52
|
-
|
|
53
53
|
An unbuilt local package directory cannot load its declared extension entry.
|
|
54
54
|
|
|
55
55
|
Pi extensions and children with `bash`, `powershell`, `edit`, or `write` execute with your user permissions.
|
|
56
|
-
|
|
57
56
|
Review the source before installing or invoking the extension.
|
|
58
57
|
|
|
59
58
|
## 🚀 Quick start
|
|
60
59
|
|
|
61
60
|
Call `subagent_spawn` with a self-contained task and only the work tools that task needs.
|
|
62
61
|
|
|
63
|
-
The package intentionally does not register or publish a skill.
|
|
64
|
-
|
|
65
|
-
Create a project skill under `.pi/skills/<your-skill>/SKILL.md` or a global skill under `~/.pi/agent/skills/<your-skill>/SKILL.md` when you want reusable delegation policy.
|
|
66
|
-
|
|
67
|
-
Choose a name, trigger description, tool policy, task format, and verification workflow for your own use case.
|
|
68
|
-
|
|
69
|
-
The repository-only [`using-pi-subagents` example](https://github.com/narumiruna/pi-extensions/tree/main/packages/pi-subagents/skills/using-pi-subagents) demonstrates one possible design without imposing it on installed users.
|
|
70
|
-
|
|
71
62
|
The call returns a `jobId` immediately, and the job continues in the background.
|
|
72
|
-
|
|
73
63
|
Continue useful main-agent work until the result is required or a completion arrives.
|
|
74
64
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
If `subagent_wait` returns `reason: "subagent_message"`, handle the visible request or response and wait for the job again only when needed.
|
|
65
|
+
Use messaging only when needed:
|
|
78
66
|
|
|
79
|
-
|
|
67
|
+
- Call `subagent_send` with `recipient: jobId` to ask an active child a question.
|
|
68
|
+
- If `subagent_wait` returns `reason: "subagent_message"`, handle the visible request or response and wait for the job again only when needed.
|
|
69
|
+
- Answer a child-originated request by calling `subagent_send` with its `requestId`.
|
|
80
70
|
|
|
81
71
|
Completion messages follow Pi's global tool-output expansion state and the `app.tools.expand` binding (`Ctrl+O` by default).
|
|
82
72
|
|
|
83
|
-
In TUI mode, the above-editor widget shows each queued or running job's ID, state, elapsed time, timeout, and selected
|
|
84
|
-
|
|
85
|
-
The widget omits the fixed communication tools, disappears when no jobs remain active, and clears when the session ends.
|
|
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.
|
|
86
75
|
|
|
87
76
|
## 🛠️ Tools
|
|
88
77
|
|
|
@@ -90,7 +79,7 @@ The main Pi session exposes five fixed tools:
|
|
|
90
79
|
|
|
91
80
|
| Tool | Parameters | Purpose |
|
|
92
81
|
| --- | --- | --- |
|
|
93
|
-
| `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`. |
|
|
94
83
|
| `subagent_inspect` | none | List privacy-filtered retained-job metadata. |
|
|
95
84
|
| `subagent_cancel` | `jobId` | Idempotently cancel one queued or running job. |
|
|
96
85
|
| `subagent_wait` | `jobId`, optional `timeout` | Wait for a job or return early for an incoming child message. |
|
|
@@ -103,120 +92,84 @@ Every child exposes these communication tools in addition to its selected work t
|
|
|
103
92
|
| `subagent_send` | optional `requestId`, plus `message` | Omit `requestId` to send a new request to main, or provide it to answer one pending main-agent request. |
|
|
104
93
|
| `subagent_wait` | `requestId`, optional `timeout` | Wait for the main agent's plain-text response to a child-originated request. |
|
|
105
94
|
|
|
106
|
-
Main and child processes receive separate provider-visible `subagent_send` definitions for their own context
|
|
107
|
-
|
|
108
|
-
The main agent starts a request with an active job ID as `recipient` and omits `requestId`.
|
|
109
|
-
|
|
110
|
-
The main agent answers a child request with `requestId` and omits `recipient`.
|
|
95
|
+
Main and child processes receive separate provider-visible `subagent_send` definitions for their own context:
|
|
111
96
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
A child
|
|
97
|
+
- The main agent starts a request with an active job ID as `recipient` and omits `requestId`.
|
|
98
|
+
- The main agent answers a child request with `requestId` and omits `recipient`.
|
|
99
|
+
- A child starts a request to main by omitting `requestId`.
|
|
100
|
+
- A child answers a main-agent request by providing `requestId`.
|
|
115
101
|
|
|
116
102
|
Execution and wait timeouts use seconds, accept finite numbers greater than zero through 2,147,483.647, and have no default.
|
|
117
|
-
|
|
118
103
|
Omitting a job execution timeout lets the child run until it exits, is cancelled, the session shuts down, or the Pi process exits.
|
|
119
|
-
|
|
120
104
|
A wait timeout or caller cancellation stops only that wait and does not cancel its job or message request.
|
|
121
|
-
|
|
122
105
|
An incoming main-agent request interrupts an active child `subagent_wait` after RPC steering is queued so the child can receive the new request.
|
|
123
|
-
|
|
124
106
|
The interrupted child-originated request remains active and may be waited on again.
|
|
125
107
|
|
|
126
108
|
Tasks are limited to 50 KiB of UTF-8 text.
|
|
127
|
-
|
|
128
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.
|
|
129
|
-
|
|
130
110
|
Each job may have up to four unresolved or answered-but-not-consumed requests across both directions.
|
|
131
|
-
|
|
132
111
|
The terminal states are `completed`, `partial`, `failed`, `timed_out`, and `cancelled`.
|
|
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.
|
|
133
113
|
|
|
134
|
-
`
|
|
135
|
-
|
|
136
|
-
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.
|
|
137
115
|
|
|
138
116
|
## ⚙️ Job configuration
|
|
139
117
|
|
|
140
118
|
The task should state the child's role, objective, scope, constraints, and expected result.
|
|
119
|
+
For reusable delegation policy, you can create your own project skill under `.pi/skills/<your-skill>/SKILL.md` or global skill under `~/.pi/agent/skills/<your-skill>/SKILL.md`.
|
|
120
|
+
Choose its name, trigger, tool policy, task format, and verification workflow for your use case.
|
|
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.
|
|
122
|
+
|
|
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
|
+
```
|
|
141
135
|
|
|
142
|
-
The optional `tools` list
|
|
143
|
-
|
|
144
|
-
Accepted names are `read`, `bash`, `powershell`, `edit`, `write`, `grep`, `find`, and `ls`.
|
|
145
|
-
|
|
146
|
-
Unavailable or extension-only tool names are rejected before a job is queued.
|
|
147
|
-
|
|
148
|
-
Omitting `tools` selects `read`, `grep`, `find`, and `ls`.
|
|
149
|
-
|
|
150
|
-
Passing an empty list gives the child no work tools.
|
|
151
|
-
|
|
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.
|
|
152
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.
|
|
153
140
|
|
|
154
|
-
|
|
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.
|
|
155
143
|
|
|
156
|
-
|
|
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.
|
|
157
148
|
|
|
158
|
-
|
|
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.
|
|
159
155
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
The child inherits the main agent's effective provider and model when `subagent_spawn` executes.
|
|
163
|
-
|
|
164
|
-
Spawn rejects providers registered by a parent extension because child processes disable unrelated extensions.
|
|
165
|
-
|
|
166
|
-
Spawn also rejects process-local runtime API keys, including a parent-only `--api-key` value.
|
|
167
|
-
|
|
168
|
-
Use stored or environment credentials that child processes can read.
|
|
169
|
-
|
|
170
|
-
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.
|
|
171
157
|
|
|
172
158
|
## 🔄 Messaging, lifecycle, and retention
|
|
173
159
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
Each job receives one cryptographically random token bound to its job identity and session generation.
|
|
177
|
-
|
|
178
|
-
The parent passes the broker credentials once through a private inherited pipe instead of placing them in the child's initial environment or command line.
|
|
179
|
-
|
|
180
|
-
The child bridge reads and closes that descriptor before model tool execution.
|
|
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.
|
|
181
161
|
|
|
182
|
-
|
|
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.
|
|
183
163
|
|
|
184
|
-
|
|
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.
|
|
185
165
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
After RPC accepts the steering message, the runtime interrupts active child response waits so the queued request can reach the child model.
|
|
189
|
-
|
|
190
|
-
Caller cancellation before RPC delivery starts rolls the request back.
|
|
191
|
-
|
|
192
|
-
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.
|
|
193
|
-
|
|
194
|
-
The interrupted child-originated requests remain active and retryable.
|
|
195
|
-
|
|
196
|
-
The first accepted `subagent_send` response wins.
|
|
197
|
-
|
|
198
|
-
Repeated responses acknowledge the existing answer without replacing it.
|
|
199
|
-
|
|
200
|
-
A child may retry `subagent_wait` after a wait timeout because the underlying request remains active.
|
|
201
|
-
|
|
202
|
-
A new job starts as `queued`, transitions to `running`, and reaches exactly one terminal state.
|
|
203
|
-
|
|
204
|
-
The runtime retains up to 32 recent terminal records for up to 24 hours within the current extension session.
|
|
205
|
-
|
|
206
|
-
Inspection reports older records removed by retention bounds through `omitted.jobs`.
|
|
207
|
-
|
|
208
|
-
Cancelling or terminalizing a job revokes its token and rejects pending child waits before stale output can replace the terminal state.
|
|
209
|
-
|
|
210
|
-
Session replacement and shutdown cancel active work, suppress stale completion delivery, revoke credentials, close sockets, and stop the broker.
|
|
166
|
+
See [Messaging and lifecycle](./docs/messaging.md) for delivery, retry, retention, and shutdown details.
|
|
211
167
|
|
|
212
168
|
## 🔀 Migrating from 2.x
|
|
213
169
|
|
|
214
170
|
Version 3.0 replaces the previous orchestration runtime.
|
|
215
|
-
|
|
216
171
|
It does not migrate legacy settings, persisted jobs, retained conversations, or recovery state.
|
|
217
|
-
|
|
218
172
|
Finish or record any required work before upgrading, then start a fresh Pi session so stored calls do not request removed tool names.
|
|
219
|
-
|
|
220
173
|
Use these replacements where the new job model supports the previous intent:
|
|
221
174
|
|
|
222
175
|
| Previous interface | Version 3 interface |
|
|
@@ -229,73 +182,64 @@ Use these replacements where the new job model supports the previous intent:
|
|
|
229
182
|
| Running main-to-child questions | Main and child `subagent_send` |
|
|
230
183
|
|
|
231
184
|
The version 3 `subagent_send` contracts are not compatible with the legacy retained-agent follow-up tool of the same name.
|
|
232
|
-
|
|
233
185
|
The `/subagents` command, extension settings, legacy retained follow-ups, `subagent_mailbox`, `subagent_consult`, custom agent catalogs, advanced orchestration, alternate transports, trust-aware cwd policy, and extension-owned worktrees have no direct replacement.
|
|
234
|
-
|
|
235
186
|
Describe the child's specialization in `task` and grant only the required work tools through `tools`.
|
|
236
187
|
|
|
237
188
|
## 🔒 Security and privacy
|
|
238
189
|
|
|
239
190
|
The selected work tools run in the current working directory.
|
|
240
|
-
|
|
241
191
|
The default list contains no shell or file-mutation tool.
|
|
242
|
-
|
|
243
192
|
It is not a filesystem sandbox because its read tools can inspect files available to the user account.
|
|
244
|
-
|
|
245
193
|
Selecting `bash`, `powershell`, `edit`, or `write` permits workspace mutation with the Pi process environment and user permissions.
|
|
246
194
|
|
|
247
|
-
Every child disables session persistence
|
|
248
|
-
|
|
249
|
-
Provider selection
|
|
250
|
-
|
|
251
|
-
Credentials must be available independently to the child through Pi's stored credentials or its inherited environment.
|
|
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.
|
|
252
199
|
|
|
253
200
|
The broker accepts only loopback TCP connections with an active per-job token.
|
|
254
|
-
|
|
255
201
|
The token is bootstrapped through a private inherited pipe and is absent from the child's initial environment and command line.
|
|
256
202
|
|
|
257
203
|
A child request or response is visible main-agent model context, but its envelope explicitly identifies it as untrusted subagent content rather than user authorization.
|
|
258
|
-
|
|
259
204
|
A child message cannot grant permission for writes, shell commands, credential access, or other privileged actions.
|
|
260
|
-
|
|
261
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.
|
|
262
206
|
|
|
263
|
-
|
|
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.
|
|
264
212
|
|
|
213
|
+
Terminal controls and bidirectional controls are stripped before untrusted child text is displayed.
|
|
265
214
|
Tasks, repository context, requests, responses, and inspected file content may be sent to the selected model provider.
|
|
266
|
-
|
|
267
215
|
Parallel writers require disjoint ownership or workspace isolation outside this extension.
|
|
268
216
|
|
|
269
217
|
## 🚧 Limitations
|
|
270
218
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
The main agent must verify child claims against the actual diff and deterministic checks.
|
|
280
|
-
|
|
281
|
-
Child requests and responses trigger a main-agent turn, but asynchronous job completions do not wake an otherwise idle model turn automatically.
|
|
282
|
-
|
|
283
|
-
Jobs, broker requests, and retained results do not survive extension reload, session replacement, or process exit.
|
|
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.
|
|
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.
|
|
222
|
+
- Bidirectional messages use request-response coordination, not a retained conversational session.
|
|
223
|
+
- The main agent must verify child claims against the actual diff and deterministic checks.
|
|
224
|
+
- Child requests and responses trigger a main-agent turn, but asynchronous job completions do not wake an otherwise idle model turn automatically.
|
|
225
|
+
- Jobs, broker requests, and retained results do not survive extension reload, session replacement, or process exit.
|
|
284
226
|
|
|
285
227
|
## 🗂️ Package layout
|
|
286
228
|
|
|
287
229
|
```text
|
|
288
230
|
packages/pi-subagents/
|
|
289
|
-
├──
|
|
290
|
-
├──
|
|
291
|
-
|
|
292
|
-
├──
|
|
293
|
-
├──
|
|
294
|
-
├──
|
|
295
|
-
├──
|
|
296
|
-
└──
|
|
231
|
+
├── src/ # Authoritative implementation and helpers
|
|
232
|
+
│ ├── index.ts # Thin Pi entrypoint
|
|
233
|
+
│ └── subagents.ts # Job, broker, and child lifecycle
|
|
234
|
+
├── dist/ # Generated Jiti runtime, child bridge, and readiness probe
|
|
235
|
+
├── scripts/build-runtime.mjs # Runtime builder
|
|
236
|
+
├── docs/ # Published reference documentation
|
|
237
|
+
├── skills/using-pi-subagents/ # Repository-only example; not published
|
|
238
|
+
└── test/ # Behavior and lifecycle coverage
|
|
297
239
|
```
|
|
298
240
|
|
|
241
|
+
The generated runtime is built from `src/index.ts` and does not import back into `src`.
|
|
242
|
+
|
|
299
243
|
## 🔎 Keywords
|
|
300
244
|
|
|
301
245
|
Pi, subagents, delegation, subagent jobs, least privilege, main-agent messaging, cancellation, job lifecycle.
|
|
@@ -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-UKTWUQRM.js";
|
|
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 {
|
|
@@ -79,10 +81,7 @@ function requestBroker(credentials, request, signal, responseTimeoutMs) {
|
|
|
79
81
|
socket.once("connect", () => {
|
|
80
82
|
clearTimeout(connectTimer);
|
|
81
83
|
if (responseTimeoutMs !== void 0) {
|
|
82
|
-
responseTimer = setTimeout(
|
|
83
|
-
() => finish(new Error("Subagent broker response timed out.")),
|
|
84
|
-
responseTimeoutMs
|
|
85
|
-
);
|
|
84
|
+
responseTimer = setTimeout(() => finish(new Error("Subagent broker response timed out.")), responseTimeoutMs);
|
|
86
85
|
responseTimer.unref();
|
|
87
86
|
}
|
|
88
87
|
socket.write(`${JSON.stringify(request)}
|
|
@@ -117,9 +116,7 @@ function requestBroker(credentials, request, signal, responseTimeoutMs) {
|
|
|
117
116
|
});
|
|
118
117
|
}
|
|
119
118
|
function brokerError(response) {
|
|
120
|
-
return new Error(
|
|
121
|
-
typeof response.error === "string" ? response.error : "Subagent broker rejected the request."
|
|
122
|
-
);
|
|
119
|
+
return new Error(typeof response.error === "string" ? response.error : "Subagent broker rejected the request.");
|
|
123
120
|
}
|
|
124
121
|
function abortError(message) {
|
|
125
122
|
const error = new Error(message);
|
|
@@ -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
|