@henryqw/pi-subagent 4.1.2 → 6.0.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/CONTEXT.md +16 -10
- package/README.md +33 -12
- package/dist/ephemeral.js +158 -55
- package/dist/index.d.ts +10 -5
- package/dist/index.js +18 -27
- package/dist/review-evidence.d.ts +17 -0
- package/dist/review-evidence.js +229 -0
- package/dist/worktree.d.ts +16 -0
- package/dist/worktree.js +49 -31
- package/docs/adr/001-composable-ephemeral-execution.md +11 -5
- package/docs/adr/002-package-owned-delegate-flow-orchestration.md +22 -0
- package/docs/orchestration.md +48 -19
- package/examples/roles/implementer.md +6 -4
- package/examples/roles/reviewer.md +8 -3
- package/examples/roles/scout.md +2 -0
- package/examples/roles/synthesizer.md +2 -0
- package/extensions/delegate-flow.ts +889 -0
- package/extensions/delegation.ts +9 -0
- package/extensions/result-transport.ts +20 -9
- package/extensions/role-tools.ts +11 -6
- package/extensions/subagent.ts +117 -44
- package/package.json +2 -2
- package/skills/pi-subagent-delegated-development/SKILL.md +15 -37
package/CONTEXT.md
CHANGED
|
@@ -2,18 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
Provide validated built-in and user Roles, shared task-model Pi launch policy, generic managed Herdr Subagent hosting,
|
|
5
|
+
Provide validated built-in and user Roles, shared task-model Pi launch policy, generic managed Herdr Subagent hosting, generic `delegate_task` delegation, and package-owned `delegate_flow` Git orchestration. Main plans and orchestrates; `delegate_task` remains a flat bounded delegation tool, while Flow uses the effective Implementer and only invokes its effective Reviewer for explicit judgment criteria after authoritative validation. The bundled Main-side Skill adds no runtime behavior or changes generic fallback.
|
|
6
6
|
|
|
7
7
|
## Domain glossary
|
|
8
8
|
|
|
9
|
-
- **Main**: Pi session
|
|
9
|
+
- **Main**: Pi session that plans and orchestrates delegated work.
|
|
10
10
|
- **Subagent**: isolated Pi child process handling one task.
|
|
11
|
-
- **Role**: package-shipped built-in or user-owned Markdown definition of a reusable responsibility, with a name, description, system instructions,
|
|
12
|
-
- **Model Class**: `fast`, `balanced`, `frontier`, or `fav`,
|
|
11
|
+
- **Role**: package-shipped built-in or user-owned Markdown definition of a reusable responsibility, with a name, description, system instructions, required `tools`, `extensions`, and `skills` arrays, and optional isolation.
|
|
12
|
+
- **Model Class**: `fast`, `balanced`, `frontier`, or `fav`, resolved through shared task-model settings; Main normally selects `fast`, may select `balanced` upfront for obvious complexity, and leaves direct model/thinking overrides to explicit user requests.
|
|
13
13
|
- **Route**: configured model and thinking-level pair selected from a shared Model Class profile; the primary route precedes its optional fallback.
|
|
14
14
|
- **Delegated Task**: one bounded work request sent from Main to one Role.
|
|
15
|
-
- **Workflow**: orchestration of one or more Delegated Tasks; `delegate_task` owns its selected mode, while library callers compose executor runs in JavaScript.
|
|
15
|
+
- **Workflow**: generic orchestration of one or more Delegated Tasks; `delegate_task` owns its selected mode, while library callers compose executor runs in JavaScript.
|
|
16
16
|
- **Workflow Mode**: `delegate_task` tool policy selected per call for `single`, `parallel`, or `chain` execution; not a Role property or executor API.
|
|
17
|
+
- **Flow**: package-owned, memory-only Git implementation and integration workflow started by `delegate_flow`.
|
|
18
|
+
- **Unit Worktree**: one Flow-owned worktree and branch for one Flow unit; it is reused for rebase, validation, optional exact review, and one repair.
|
|
19
|
+
- **Review Packet**: exact `{base, tip, patchPath}` evidence delivered to the Flow Reviewer only for a unit's explicit `review` criterion.
|
|
17
20
|
- **Resource Policy**: Role ownership of base tools, extensions, and Skill names, plus explicit caller additions of tools, extensions, and environment through `createRoleLaunch`.
|
|
18
21
|
- **Pi Launch**: reusable `{env,args}` policy for one Role, resolved model route, explicit caller resources, and project trust.
|
|
19
22
|
- **Ephemeral Executor**: mechanism that receives a prepared Pi Launch, runs one bounded Delegated Task in one no-session child process, and returns its result without discovering resources or composing a Workflow.
|
|
@@ -21,12 +24,15 @@ Provide validated built-in and user Roles, shared task-model Pi launch policy, g
|
|
|
21
24
|
|
|
22
25
|
## Invariants
|
|
23
26
|
|
|
24
|
-
- One Delegated Task creates one ephemeral child process and no saved session. Timeout behavior: `deadline = min(last recognized Pi JSON event + idle timeout, child start + maximum runtime)` (recognized Pi events renew; raw bytes do not; max always terminates). Configurable via the `timeout` object in `~/.pi/agent/config/pi-subagent/pi-subagent.json` (`idleMinutes`, `maxMinutes`; defaults 10/30).
|
|
27
|
+
- One Delegated Task creates one ephemeral child process and no saved session. Timeout behavior: `deadline = min(last recognized Pi JSON event + idle timeout, child start + maximum runtime)` (recognized Pi events renew; raw bytes do not; max always terminates). After direct Pi exits, inherited stdout/stderr drain until EOF unless an escaped descendant holds them past short inactivity or a one-second hard deadline. Configurable via the `timeout` object in `~/.pi/agent/config/pi-subagent/pi-subagent.json` (`idleMinutes`, `maxMinutes`; defaults 10/30).
|
|
25
28
|
- Up to five active ephemeral `delegate_task` children run per Main by default, configurable via `maxSubagents` in `~/.pi/agent/config/pi-subagent/pi-subagent.json` or the `PI_SUBAGENT_MAX_SUBAGENTS` environment variable; excess calls wait FIFO. Queued calls do not start a child or consume child timeout. Managed Herdr workers are unaffected.
|
|
26
|
-
- Ambient child extensions and Skills stay disabled
|
|
27
|
-
- Role Skill names resolve through Main's effective Pi Skill registry; unavailable names warn and skip without blocking delegation.
|
|
28
|
-
- Main
|
|
29
|
+
- Ambient child extensions and Skills stay disabled. Every Role requires `tools`, `extensions`, and `skills` YAML arrays, and every launch installs the Role tool policy. `tools: []` activates no base built-ins but does activate all tools from explicitly selected trusted extension bundles and explicit caller tool additions; `skills: []` selects no separately named Role Skills but trusted selected extension Skills still load; `extensions: []` selects no Role extension bundle. A Role/caller explicitly selected extension is a trusted atomic capability bundle: all tools it registers and all Skills supplied through its Pi package metadata or dynamic `resources_discover` load alongside separately named Role Skills. This intentionally includes the extension's executable lifecycle/prompt behavior; pi-subagent does not infer or externally narrow undocumented dependencies, and loading an extension is not sandboxing. Scope children by selecting fewer trusted extensions; finer granularity requires separate entry points/configuration or an upstream split. Explicit Role/caller tool names still verify against the final filtered registry, while parent-only recursive orchestration tools remain excluded.
|
|
30
|
+
- Role Skill names resolve through Main's effective Pi Skill registry; unavailable names warn and skip without blocking delegation. Explicit Role/caller tool names verify against the final filtered child registry after explicit provider `session_start` handlers, and unavailable names fail before the first turn.
|
|
31
|
+
- Main policy populates direct `model` and `thinking` only for explicit user overrides; otherwise it selects only `modelClass` (`fast` normally, `balanced` upfront for obvious complexity). This has no provenance tracking or runtime enforcement. An omitted class uses the shared `pi-subagent/delegateTask` assignment; library callers select a Role plus their own shared task ID.
|
|
29
32
|
- The selected profile resolves primary then fallback only before launch when a route, model, or thinking level is unavailable. If neither route is usable, launch rejects with `Run /task-models`; a started child is never retried by this package.
|
|
30
|
-
- User Role Markdown files and Subagent JSON config live only in the user `config/pi-subagent` directory; model routes live in shared `config/pi-task-models.json`. Package-shipped built-in Roles (`implementer`, `reviewer`) resolve from the package's own `examples/roles/` Markdown through the same parser; a same-named user file explicitly overrides a built-in
|
|
33
|
+
- User Role Markdown files and Subagent JSON config live only in the user `config/pi-subagent` directory; model routes live in shared `config/pi-task-models.json`. Package-shipped built-in Roles (`implementer`, `reviewer`) resolve from the package's own `examples/roles/` Markdown through the same parser; a same-named user file explicitly overrides a built-in for `delegate_task` and `delegate_flow`.
|
|
34
|
+
- `delegate_flow` accepts 1–8 independent units with direct validation commands, optional `modelClass`, and optional non-empty `review` judgment text. Omitted classes use the shared `pi-subagent/delegateTask` assignment; each unit's current class resolves through its shared profile route for its Implementer and, when `review` exists, Reviewer. At Flow start it always resolves/freezes the effective Implementer and resolves/freezes a Reviewer only when at least one requested unit has `review`. One active Flow creates every Unit Worktree before launching Implementers in parallel, then processes settled units in declared order. For each unit Flow rebases in place when earlier units advanced Main, inspects committed Git state, and runs declared validation. Validation is authoritative for objective verification: units without `review` fast-forward their exact validated tip; units with `review` send the exact Review Packet to the Reviewer in the same worktree and require exact `PASS` before the same guarded `git merge --ff-only` path.
|
|
35
|
+
- Flow is memory-only. Only a post-rebase commit drop produces a no-op (`base === tip`); it validates, skips Reviewer and merge, then cleans up ordinarily. Initial zero-commit implementations block. Implementer, validation, or reviewer blocks allow one `delegate_flow_continue({ guidance, modelClass? })` repair in the same worktree; omission retains the Unit's current class and presence replaces it for that repair. A second block is terminal. Rebase and evidence/Reviewer/infrastructure failures retain worktrees. A reported fast-forward failure retains its worktree unless Main is clean at the exact integrated tip, which completes with the merge diagnostic as a warning. Cleanup uses non-forced worktree removal and branch deletion; cleanup refusal is a completion warning.
|
|
36
|
+
- Flow has no graph, saved recovery, automatic retry, aggregate review, or post-merge validation. It never changes generic `delegate_task` Role resolution, isolation, non-Git fallback, or ordinary direct plan/file review.
|
|
31
37
|
- Numbered Codex routes prefer Main's active account slot and explicitly load the multi-Codex child extension.
|
|
32
38
|
- Generic Herdr host functions validate workspace ownership and provisioning identity while callers retain domain state, prompts, and lifecycle decisions.
|
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# `@henryqw/pi-subagent`
|
|
2
2
|
|
|
3
|
-
Delegate bounded work to isolated Pi child processes.
|
|
3
|
+
Delegate bounded work to isolated Pi child processes. Main plans and orchestrates; generic `delegate_task` selects one flat single, parallel, or chain mode. Package-owned `delegate_flow` runs a fixed Git implementation-and-verification Flow. Package authors can reuse the same Role launch policy and active-Pi executor from JavaScript.
|
|
4
4
|
|
|
5
5
|
## Why
|
|
6
6
|
|
|
7
7
|
- **Created for**: Pi users who need to delegate bounded work to isolated child Pi processes without losing Main's context.
|
|
8
|
-
- **Advantage**:
|
|
8
|
+
- **Advantage**: Generic bounded delegation plus a deterministic package-owned Git Flow, with reusable Role launch policies for package authors.
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -26,7 +26,9 @@ pi install npm:@henryqw/pi-subagent
|
|
|
26
26
|
|
|
27
27
|
| Surface | Type | Purpose |
|
|
28
28
|
| --- | --- | --- |
|
|
29
|
-
| `delegate_task` | tool |
|
|
29
|
+
| `delegate_task` | tool | Generic bounded delegation in one single, parallel, or chain mode. |
|
|
30
|
+
| `delegate_flow` | tool | Package-owned parallel implementation and declared-order Git integration for 1–8 independent units. |
|
|
31
|
+
| `delegate_flow_continue` | tool | Repair the blocked Flow unit once in its existing worktree. |
|
|
30
32
|
|
|
31
33
|
### `delegate_task`
|
|
32
34
|
|
|
@@ -43,15 +45,34 @@ Select exactly one shape:
|
|
|
43
45
|
{ chain: [{ role, task, model?, modelClass?, thinking? }], background? }
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
`model` is `provider/modelId` and overrides `modelClass`. `modelClass` is `fast`, `balanced`, `frontier`, or `fav`; omission uses the shared `pi-subagent/delegateTask` assignment. `background` applies to the entire selected mode and is never a per-delegation field.
|
|
48
|
+
`model` is `provider/modelId` and overrides `modelClass`. Main populates `model` and `thinking` only for an explicit user override; otherwise it chooses only `modelClass`—`fast` normally, or `balanced` upfront for obviously complex work. This is Main policy only: the runtime records no provenance and does not enforce it. `modelClass` is `fast`, `balanced`, `frontier`, or `fav`; omission uses the shared `pi-subagent/delegateTask` assignment. `background` applies to the entire selected mode and is never a per-delegation field.
|
|
47
49
|
|
|
48
50
|
Parallel mode starts entries concurrently, waits for every entry, and reports them in input order. Chain mode is sequential and fail-fast; every literal `{previous}` receives only the immediately preceding successful assistant output. Foreground failures throw after retaining bounded sibling and recovery evidence. One tool call has one aggregate 50 KiB Main-visible transport cap, not 50 KiB per child.
|
|
49
51
|
|
|
50
52
|
Background workflows are session-scoped. Session shutdown or reload aborts them and may deliver only recoverable-work evidence or no follow-up message.
|
|
51
53
|
|
|
54
|
+
The transient status widget owns live progress: spinner or terminal state, wrapped task summary, model, thinking level, tokens, and duration; at capacity, it evicts the oldest terminal row so new active work remains visible, and terminal rows otherwise clear on the next real user input. The final `delegate_task` block is deliberately minimal: bounded final summaries, role attribution for parallel/chain, and only retained-worktree recovery paths. It has no expanded view.
|
|
55
|
+
|
|
52
56
|
Each delegation resolves its own Role, resources, route, and optional worktree request. When available, `isolation: worktree` gives each entry a deterministic separate worktree; non-Git or unborn-`HEAD` contexts may use Main's cwd. Siblings and chain steps never implicitly share one created worktree.
|
|
53
57
|
|
|
54
|
-
See [Orchestration, isolation, and the public API](./docs/orchestration.md) for
|
|
58
|
+
See [Orchestration, isolation, and the public API](./docs/orchestration.md) for generic delegation, Flow behavior, and JavaScript composition examples.
|
|
59
|
+
|
|
60
|
+
### `delegate_flow`
|
|
61
|
+
|
|
62
|
+
Use Flow only for independent, commuting Git changes. It accepts 1–8 uniquely identified units, each with a bounded task, optional `modelClass`, direct command/argument validation gate, and optional non-empty `review` judgment criterion:
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
delegate_flow({ units: [{ id, task, modelClass?, validation: [{ command, args }], review? }] })
|
|
66
|
+
delegate_flow_continue({ guidance, modelClass? })
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Objective verification is authoritative. Flow always inspects committed Git state and runs declared validation. A unit without `review` skips review evidence and Reviewer launch, then fast-forwards its exact validated tip through the existing guarded `git merge --ff-only` path. Add `review` only for an explicit judgment that automation cannot establish; that unit retains the exact `{base, tip, patchPath}` protocol and requires exact `PASS` before the same integration path.
|
|
70
|
+
|
|
71
|
+
One memory-only Flow may be active. At start it resolves/freezes the effective `implementer` Role, including a same-named user override, and resolves/freezes the effective `reviewer` only if at least one requested unit has `review`. Omitted `modelClass` uses the shared `pi-subagent/delegateTask` assignment; a selected class resolves through its shared profile model-and-thinking route for the unit's Implementer and, when applicable, Reviewer. It creates one Unit Worktree per unit, runs Implementers in parallel, then processes settled results in declared order. It removes the worktree and branch non-forcibly after integration; a refusal is a completion warning with the retained worktree path and/or branch.
|
|
72
|
+
|
|
73
|
+
A rebase that drops all unit commits is a no-op: Flow validates it, skips Reviewer and merge, then cleans up ordinarily. Implementer, validation, or review blocks can be repaired once through `delegate_flow_continue` in the same worktree. Omitted continuation `modelClass` retains the blocked unit's current class; a supplied class replaces it for that one repair. Rebase and infrastructure failures are terminal. A reported fast-forward failure completes with its diagnostic as a warning only when Git left Main clean at the exact integrated tip; otherwise it is terminal and retains the affected worktree. Flow has no graph, saved recovery, automatic retry, aggregate review, or post-merge gate.
|
|
74
|
+
|
|
75
|
+
`delegate_task` remains generic with its ordinary isolation behavior. Flow uses the package-shipped Implementer by default and the package-shipped Reviewer only when a unit requests review; same-named user Roles remain supported overrides.
|
|
55
76
|
|
|
56
77
|
## Config
|
|
57
78
|
|
|
@@ -77,10 +98,10 @@ Each Role `.md` file in the same directory accepts these frontmatter fields:
|
|
|
77
98
|
| --- | --- | --- | --- |
|
|
78
99
|
| `name` | Yes | Non-empty text; unique across roles | — |
|
|
79
100
|
| `description` | Yes | Non-empty text | — |
|
|
80
|
-
| `tools` |
|
|
101
|
+
| `tools` | Yes | YAML array of non-empty tool names | `[]` activates no base built-ins; trusted extension tools and caller additions still activate |
|
|
81
102
|
| `isolation` | No | `worktree` | None |
|
|
82
|
-
| `extensions` |
|
|
83
|
-
| `skills` |
|
|
103
|
+
| `extensions` | Yes | YAML array of absolute paths, `~/…`, `file://`, or package sources (`npm:`, `git:`, `github:`, `https?:`, `ssh:`) | `[]` selects no Role extension bundle |
|
|
104
|
+
| `skills` | Yes | YAML array of non-empty Skill names | `[]` selects no separately named Role Skills; trusted extension Skills still load |
|
|
84
105
|
| body | Yes | System-prompt Markdown after the frontmatter | — |
|
|
85
106
|
|
|
86
107
|
An unreadable or invalid Role file fails role loading fast; duplicate role names are rejected.
|
|
@@ -90,7 +111,7 @@ An unreadable or invalid Role file fails role loading fast; duplicate role names
|
|
|
90
111
|
The package ships two working built-in Roles, always available without any configuration:
|
|
91
112
|
|
|
92
113
|
- `implementer`: focused edits requesting worktree isolation; commits completed scoped changes locally and never pushes or opens PRs without authorization
|
|
93
|
-
- `reviewer`: read-only correctness review
|
|
114
|
+
- `reviewer`: read-only correctness review of supplied plans/files, or—only when a Flow unit declares `review`—of Flow's exact `{base, tip, patchPath}` packet in its Unit Worktree; never edits or commits
|
|
94
115
|
|
|
95
116
|
A same-named Markdown file in `~/.pi/agent/config/pi-subagent/` explicitly overrides the built-in default.
|
|
96
117
|
|
|
@@ -112,12 +133,12 @@ The package never installs or writes Role configuration. Sample names are not bu
|
|
|
112
133
|
|
|
113
134
|
## Skill
|
|
114
135
|
|
|
115
|
-
The bundled [`pi-subagent-delegated-development`](./skills/pi-subagent-delegated-development/SKILL.md) Skill is
|
|
136
|
+
The bundled [`pi-subagent-delegated-development`](./skills/pi-subagent-delegated-development/SKILL.md) Skill is Main-side planner/orchestrator policy only. `delegate_flow` owns its fixed Git mechanics and validation authority; the Skill adds no runtime code, configuration, or Role installation. Generic orchestration remains outside the executor under [ADR 001](./docs/adr/001-composable-ephemeral-execution.md).
|
|
116
137
|
|
|
117
|
-
A Role owns
|
|
138
|
+
A Role explicitly owns base tools, extensions, named Skills, instructions, and optional `isolation: worktree`. Every launch installs its Role tool policy: `tools: []` activates no base built-ins, while trusted selected extension tools and explicit caller tool additions still activate. `skills: []` selects no separately named Role Skills, while trusted selected extension Skills still load; `extensions: []` selects no Role extension bundle. Ambient extension and Skill discovery is disabled in children. Selecting an extension explicitly is selecting a trusted atomic capability bundle, not just a provider path: every tool it registers and every Skill supplied through its Pi package metadata or dynamic `resources_discover` loads alongside separately named Role Skills. This is intentional because an extension may depend on its own tools, Skills, lifecycle, and prompt behavior; loading it permits that executable behavior and is not sandboxing. To scope a child, select fewer trusted extensions. Finer-grained selection requires separate extension entry points/configuration or an upstream split—pi-subagent does not infer or externally narrow undocumented dependencies. Parent-only recursive orchestration tools stay excluded. Explicit Role or caller tool names are verified against the child’s final filtered active registry after provider extensions finish `session_start`; all unavailable names fail before the first model turn with provider-extension guidance, while unavailable named Skills warn and skip.
|
|
118
139
|
|
|
119
140
|
## Library API
|
|
120
141
|
|
|
121
|
-
The package root exports Role loading and launch resolution, `createEphemeralSubagentExecutor`, worktree helpers, and generic managed Herdr lifecycle helpers. The ephemeral executor is for code already running inside active Pi; it does not provide standalone Node.js Pi discovery or launch support.
|
|
142
|
+
The package root exports Role loading and launch resolution, `createEphemeralSubagentExecutor`, worktree helpers, and generic managed Herdr lifecycle helpers. The ephemeral executor is for code already running inside active Pi; it does not provide standalone Node.js Pi discovery or launch support. After Pi itself exits, it drains inherited stdout/stderr normally but destroys streams still held by escaped descendants after a short inactivity deadline or one-second hard deadline, so they cannot retain a pool permit.
|
|
122
143
|
|
|
123
144
|
Use [`docs/orchestration.md`](./docs/orchestration.md#public-role-and-executor-api) for exact API behavior and a post-permit `prepare` example using `resolveRoleLaunch` against the latest Pi context.
|
package/dist/ephemeral.js
CHANGED
|
@@ -5,6 +5,8 @@ import { StringDecoder } from "node:string_decoder";
|
|
|
5
5
|
const MAX_OUTPUT_BYTES = 50 * 1024;
|
|
6
6
|
const MAX_JSON_EVENT_BYTES = 1024 * 1024;
|
|
7
7
|
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
8
|
+
const POST_EXIT_STDIO_IDLE_MS = 250;
|
|
9
|
+
const POST_EXIT_STDIO_HARD_MS = 1_000;
|
|
8
10
|
const PI_JSON_EVENTS = {
|
|
9
11
|
agent_start: true,
|
|
10
12
|
agent_end: true,
|
|
@@ -373,10 +375,95 @@ async function runPi(prepared, input, timeoutPolicy, invocation) {
|
|
|
373
375
|
const accumulatedUsage = () => addUsage(completedUsage, currentUsage);
|
|
374
376
|
let deadlineTimer;
|
|
375
377
|
let killTimer;
|
|
378
|
+
let postExitIdleTimer;
|
|
379
|
+
let postExitHardTimer;
|
|
380
|
+
let callbackDrainTimer;
|
|
381
|
+
let stdoutEnded = false;
|
|
382
|
+
let stderrEnded = false;
|
|
383
|
+
let settled = false;
|
|
384
|
+
let cleaned = false;
|
|
376
385
|
let callbackFailure;
|
|
377
386
|
const pendingCallbacks = new Set();
|
|
378
387
|
let signalCallbackFailure;
|
|
379
388
|
const callbackFailed = new Promise((resolve) => { signalCallbackFailure = resolve; });
|
|
389
|
+
let onStdoutData;
|
|
390
|
+
let onStderrData;
|
|
391
|
+
let onStdoutEnd;
|
|
392
|
+
let onStderrEnd;
|
|
393
|
+
let onStdoutClose;
|
|
394
|
+
let onStderrClose;
|
|
395
|
+
let onChildError;
|
|
396
|
+
let onChildExit;
|
|
397
|
+
let onChildClose;
|
|
398
|
+
const clearTimer = (timer) => {
|
|
399
|
+
if (timer)
|
|
400
|
+
clearTimeout(timer);
|
|
401
|
+
};
|
|
402
|
+
const clearLifecycle = () => {
|
|
403
|
+
if (cleaned)
|
|
404
|
+
return;
|
|
405
|
+
cleaned = true;
|
|
406
|
+
clearTimer(deadlineTimer);
|
|
407
|
+
clearTimer(killTimer);
|
|
408
|
+
clearTimer(postExitIdleTimer);
|
|
409
|
+
clearTimer(postExitHardTimer);
|
|
410
|
+
clearTimer(callbackDrainTimer);
|
|
411
|
+
input.signal?.removeEventListener("abort", abort);
|
|
412
|
+
if (onStdoutData)
|
|
413
|
+
child.stdout.off("data", onStdoutData);
|
|
414
|
+
if (onStderrData)
|
|
415
|
+
child.stderr.off("data", onStderrData);
|
|
416
|
+
if (onStdoutEnd)
|
|
417
|
+
child.stdout.off("end", onStdoutEnd);
|
|
418
|
+
if (onStderrEnd)
|
|
419
|
+
child.stderr.off("end", onStderrEnd);
|
|
420
|
+
if (onStdoutClose)
|
|
421
|
+
child.stdout.off("close", onStdoutClose);
|
|
422
|
+
if (onStderrClose)
|
|
423
|
+
child.stderr.off("close", onStderrClose);
|
|
424
|
+
if (onChildError)
|
|
425
|
+
child.off("error", onChildError);
|
|
426
|
+
if (onChildExit)
|
|
427
|
+
child.off("exit", onChildExit);
|
|
428
|
+
if (onChildClose)
|
|
429
|
+
child.off("close", onChildClose);
|
|
430
|
+
};
|
|
431
|
+
const complete = (code) => {
|
|
432
|
+
if (settled)
|
|
433
|
+
return;
|
|
434
|
+
settled = true;
|
|
435
|
+
clearLifecycle();
|
|
436
|
+
if (callbackFailure) {
|
|
437
|
+
reject(new EphemeralSubagentError("callback", callbackFailure.message, callbackFailure.cause, accumulatedUsage()));
|
|
438
|
+
}
|
|
439
|
+
else if (aborted)
|
|
440
|
+
reject(abortError(input.signal, input.signal?.reason, accumulatedUsage()));
|
|
441
|
+
else if (timedOutAfterMs !== undefined) {
|
|
442
|
+
const message = timeoutReason === "maximum"
|
|
443
|
+
? `Subagent reached its maximum runtime after ${formatDuration(timedOutAfterMs)}.`
|
|
444
|
+
: `Subagent timed out after ${formatDuration(timeoutPolicy.idleMs)} without a recognized Pi event.`;
|
|
445
|
+
reject(new EphemeralSubagentError("timeout", message, new Error(message), accumulatedUsage()));
|
|
446
|
+
}
|
|
447
|
+
else if (protocolError) {
|
|
448
|
+
reject(new EphemeralSubagentError("protocol", protocolError.message, protocolError, accumulatedUsage()));
|
|
449
|
+
}
|
|
450
|
+
else if (spawnError) {
|
|
451
|
+
reject(new EphemeralSubagentError("spawn", spawnError.message, spawnError));
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
const exitCode = code ?? 1;
|
|
455
|
+
const outcome = exitCode !== 0 || stopReason === "error" || stopReason === "aborted" ? "failure" : "success";
|
|
456
|
+
resolve({
|
|
457
|
+
outcome,
|
|
458
|
+
exitCode,
|
|
459
|
+
output,
|
|
460
|
+
stderr: boundedText(stderr),
|
|
461
|
+
stopReason,
|
|
462
|
+
errorMessage,
|
|
463
|
+
usage: addUsage(completedUsage, currentUsage),
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
};
|
|
380
467
|
const failCallback = (name, cause) => {
|
|
381
468
|
if (callbackFailure)
|
|
382
469
|
return;
|
|
@@ -507,7 +594,32 @@ async function runPi(prepared, input, timeoutPolicy, invocation) {
|
|
|
507
594
|
child.kill(force ? "SIGKILL" : "SIGTERM");
|
|
508
595
|
}
|
|
509
596
|
}
|
|
510
|
-
|
|
597
|
+
// `close` waits for stdio EOF, which an escaped descendant can retain after Pi exits.
|
|
598
|
+
const clearPostExitTimers = () => {
|
|
599
|
+
clearTimer(postExitIdleTimer);
|
|
600
|
+
clearTimer(postExitHardTimer);
|
|
601
|
+
postExitIdleTimer = undefined;
|
|
602
|
+
postExitHardTimer = undefined;
|
|
603
|
+
};
|
|
604
|
+
const destroyOpenStdio = () => {
|
|
605
|
+
if (!stdoutEnded)
|
|
606
|
+
child.stdout.destroy();
|
|
607
|
+
if (!stderrEnded)
|
|
608
|
+
child.stderr.destroy();
|
|
609
|
+
};
|
|
610
|
+
const armPostExitIdleDeadline = () => {
|
|
611
|
+
if (!childExited || (stdoutEnded && stderrEnded))
|
|
612
|
+
return;
|
|
613
|
+
clearTimer(postExitIdleTimer);
|
|
614
|
+
postExitIdleTimer = setTimeout(destroyOpenStdio, POST_EXIT_STDIO_IDLE_MS);
|
|
615
|
+
postExitIdleTimer.unref();
|
|
616
|
+
};
|
|
617
|
+
const endStdio = () => {
|
|
618
|
+
if (stdoutEnded && stderrEnded)
|
|
619
|
+
clearPostExitTimers();
|
|
620
|
+
};
|
|
621
|
+
onStdoutData = (data) => {
|
|
622
|
+
armPostExitIdleDeadline();
|
|
511
623
|
if (callbackFailure || protocolError)
|
|
512
624
|
return;
|
|
513
625
|
let offset = 0;
|
|
@@ -549,11 +661,27 @@ async function runPi(prepared, input, timeoutPolicy, invocation) {
|
|
|
549
661
|
ignoreLine = false;
|
|
550
662
|
offset = newline + 1;
|
|
551
663
|
}
|
|
552
|
-
}
|
|
553
|
-
|
|
664
|
+
};
|
|
665
|
+
onStderrData = (data) => {
|
|
666
|
+
armPostExitIdleDeadline();
|
|
554
667
|
appendBounded(stderr, data);
|
|
555
|
-
}
|
|
556
|
-
|
|
668
|
+
};
|
|
669
|
+
onStdoutEnd = () => {
|
|
670
|
+
stdoutEnded = true;
|
|
671
|
+
endStdio();
|
|
672
|
+
};
|
|
673
|
+
onStderrEnd = () => {
|
|
674
|
+
stderrEnded = true;
|
|
675
|
+
endStdio();
|
|
676
|
+
};
|
|
677
|
+
onStdoutClose = onStdoutEnd;
|
|
678
|
+
onStderrClose = onStderrEnd;
|
|
679
|
+
child.stdout.on("data", onStdoutData);
|
|
680
|
+
child.stderr.on("data", onStderrData);
|
|
681
|
+
child.stdout.on("end", onStdoutEnd);
|
|
682
|
+
child.stderr.on("end", onStderrEnd);
|
|
683
|
+
child.stdout.on("close", onStdoutClose);
|
|
684
|
+
child.stderr.on("close", onStderrClose);
|
|
557
685
|
function stop(force = false) {
|
|
558
686
|
if (force) {
|
|
559
687
|
void killTree(true);
|
|
@@ -586,66 +714,41 @@ async function runPi(prepared, input, timeoutPolicy, invocation) {
|
|
|
586
714
|
timeoutReason = reason;
|
|
587
715
|
stop();
|
|
588
716
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
child.once("exit", () => {
|
|
717
|
+
onChildError = (error) => {
|
|
718
|
+
spawnError = error;
|
|
719
|
+
if (input.signal?.aborted)
|
|
720
|
+
aborted = true;
|
|
721
|
+
};
|
|
722
|
+
onChildExit = () => {
|
|
596
723
|
childExited = true;
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
input.signal?.removeEventListener("abort", abort);
|
|
724
|
+
clearTimer(deadlineTimer);
|
|
725
|
+
clearTimer(killTimer);
|
|
600
726
|
void killTree(true);
|
|
601
|
-
|
|
602
|
-
|
|
727
|
+
armPostExitIdleDeadline();
|
|
728
|
+
postExitHardTimer = setTimeout(destroyOpenStdio, POST_EXIT_STDIO_HARD_MS);
|
|
729
|
+
postExitHardTimer.unref();
|
|
730
|
+
};
|
|
731
|
+
onChildClose = async (code) => {
|
|
732
|
+
if (settled)
|
|
733
|
+
return;
|
|
603
734
|
if (!callbackFailure && !protocolError && lineBytes)
|
|
604
735
|
processLine(lineParts.join(""));
|
|
605
736
|
await killTree(true);
|
|
606
737
|
// A caller callback that never settles must not hold `run()` or its permit
|
|
607
738
|
// past child exit; bound the drain by what remains of the maximum runtime.
|
|
608
|
-
|
|
739
|
+
callbackDrainTimer = setTimeout(() => {
|
|
609
740
|
callbackFailure ??= new EphemeralSubagentError("callback", "Subagent callback did not settle before the post-exit drain deadline.");
|
|
610
741
|
signalCallbackFailure();
|
|
611
742
|
}, Math.min(5_000, Math.max(0, maxDeadline - Date.now())));
|
|
612
743
|
await Promise.race([Promise.all(pendingCallbacks), callbackFailed]);
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
else if (aborted)
|
|
623
|
-
reject(abortError(input.signal, input.signal?.reason, accumulatedUsage()));
|
|
624
|
-
else if (timedOutAfterMs !== undefined) {
|
|
625
|
-
const message = timeoutReason === "maximum"
|
|
626
|
-
? `Subagent reached its maximum runtime after ${formatDuration(timedOutAfterMs)}.`
|
|
627
|
-
: `Subagent timed out after ${formatDuration(timeoutPolicy.idleMs)} without a recognized Pi event.`;
|
|
628
|
-
reject(new EphemeralSubagentError("timeout", message, new Error(message), accumulatedUsage()));
|
|
629
|
-
}
|
|
630
|
-
else if (protocolError) {
|
|
631
|
-
reject(new EphemeralSubagentError("protocol", protocolError.message, protocolError, accumulatedUsage()));
|
|
632
|
-
}
|
|
633
|
-
else if (spawnError) {
|
|
634
|
-
reject(new EphemeralSubagentError("spawn", spawnError.message, spawnError));
|
|
635
|
-
}
|
|
636
|
-
else {
|
|
637
|
-
const exitCode = code ?? 1;
|
|
638
|
-
const outcome = exitCode !== 0 || stopReason === "error" || stopReason === "aborted" ? "failure" : "success";
|
|
639
|
-
resolve({
|
|
640
|
-
outcome,
|
|
641
|
-
exitCode,
|
|
642
|
-
output,
|
|
643
|
-
stderr: boundedText(stderr),
|
|
644
|
-
stopReason,
|
|
645
|
-
errorMessage,
|
|
646
|
-
usage: addUsage(completedUsage, currentUsage),
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
});
|
|
744
|
+
complete(code);
|
|
745
|
+
};
|
|
746
|
+
child.once("error", onChildError);
|
|
747
|
+
child.once("exit", onChildExit);
|
|
748
|
+
child.once("close", onChildClose);
|
|
749
|
+
scheduleDeadline();
|
|
750
|
+
input.signal?.addEventListener("abort", abort, { once: true });
|
|
751
|
+
if (input.signal?.aborted)
|
|
752
|
+
abort();
|
|
650
753
|
});
|
|
651
754
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,13 +2,15 @@ import { type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-cod
|
|
|
2
2
|
import { type HerdrExecutor } from "@henryqw/pi-herdr";
|
|
3
3
|
import { type AvailableModel, type ProfileName, type ResolvedTaskRoute, type ThinkingLevel } from "@henryqw/pi-task-models";
|
|
4
4
|
export { addUsage, capEphemeralSubagentOutput, createEphemeralSubagentExecutor, EphemeralSubagentError, formatDuration, type EphemeralSubagentErrorCode, type EphemeralSubagentExecutor, type EphemeralSubagentExecutorOptions, type EphemeralSubagentResult, type EphemeralSubagentRunInput, type EphemeralSubagentTimeout, } from "./ephemeral.ts";
|
|
5
|
-
export { createChildWorktree, finalizeChildWorktree, worktreeContextNote, type WorktreeInfo, type WorktreePayload, } from "./worktree.ts";
|
|
5
|
+
export { createChildWorktree, finalizeChildWorktree, inspectIndexFlags, inspectWorktreeDirty, WorktreeSetupError, worktreeContextNote, type WorktreeDirtyInspection, type WorktreeInfo, type WorktreePayload, } from "./worktree.ts";
|
|
6
|
+
export { prepareExactReviewEvidence, REVIEW_MAX_PATCH_BYTES, REVIEW_MAX_PATHS, type PreparedReviewEvidence, type PrepareExactReviewEvidenceInput, } from "./review-evidence.ts";
|
|
6
7
|
export declare const ROLE_TOOL_POLICY_FLAG = "pi-subagent-role-tools";
|
|
7
|
-
export declare const
|
|
8
|
+
export declare const CHILD_EXCLUDED_TOOL_NAMES: readonly ["delegate_task", "delegate_flow", "delegate_flow_continue", "ask_question"];
|
|
9
|
+
export declare const CHILD_EXCLUDED_TOOLS: string;
|
|
8
10
|
export interface Role {
|
|
9
11
|
name: string;
|
|
10
12
|
description: string;
|
|
11
|
-
tools
|
|
13
|
+
tools: string[];
|
|
12
14
|
isolation?: string;
|
|
13
15
|
extensions: string[];
|
|
14
16
|
skills: string[];
|
|
@@ -38,6 +40,9 @@ export interface ResolvedRoleSkills {
|
|
|
38
40
|
paths: string[];
|
|
39
41
|
missing: string[];
|
|
40
42
|
}
|
|
43
|
+
declare const BUILTIN_ROLE_NAMES: readonly ["implementer", "reviewer"];
|
|
44
|
+
export type BuiltinRoleName = (typeof BUILTIN_ROLE_NAMES)[number];
|
|
45
|
+
export declare function loadBuiltinRole(name: BuiltinRoleName): Role;
|
|
41
46
|
/**
|
|
42
47
|
* Validated built-in implementer/reviewer Roles plus valid user roles from
|
|
43
48
|
* `config/pi-subagent`. A user role with a built-in name overrides the default;
|
|
@@ -47,8 +52,8 @@ export interface ResolvedRoleSkills {
|
|
|
47
52
|
export declare function loadRoles(agentDir?: string): Role[];
|
|
48
53
|
export declare function resolveTaskRoute(ctx: ExtensionContext, profileName: ProfileName, agentDir?: string, thinking?: ThinkingLevel): ResolvedTaskRoute;
|
|
49
54
|
export declare function resolveRoleSkills(pi: Pick<ExtensionAPI, "getCommands">, role: Role): ResolvedRoleSkills;
|
|
50
|
-
export declare function createRoleLaunch(pi: Pick<ExtensionAPI, "
|
|
51
|
-
export declare function resolveRoleLaunch(pi: Pick<ExtensionAPI, "
|
|
55
|
+
export declare function createRoleLaunch(pi: Pick<ExtensionAPI, "getCommands">, ctx: Pick<ExtensionContext, "isProjectTrusted">, input: CreateRoleLaunchInput): ResolvedRoleLaunch;
|
|
56
|
+
export declare function resolveRoleLaunch(pi: Pick<ExtensionAPI, "getCommands">, ctx: ExtensionContext, input: ResolveRoleLaunchInput): ResolvedRoleLaunch;
|
|
52
57
|
export interface ManagedSubagentHost {
|
|
53
58
|
cwd: string;
|
|
54
59
|
workspaceId: string;
|
package/dist/index.js
CHANGED
|
@@ -6,12 +6,14 @@ import { getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
|
6
6
|
import { createHerdrClient, herdrCommandFailure, hasHerdrErrorCode, startPiAgent } from "@henryqw/pi-herdr";
|
|
7
7
|
import { modelReference, orderedProfileRoutes, readTaskModelsConfig, resolveConfiguredTaskRoute, resolveTaskModelRoute, } from "@henryqw/pi-task-models";
|
|
8
8
|
export { addUsage, capEphemeralSubagentOutput, createEphemeralSubagentExecutor, EphemeralSubagentError, formatDuration, } from "./ephemeral.js";
|
|
9
|
-
export { createChildWorktree, finalizeChildWorktree, worktreeContextNote, } from "./worktree.js";
|
|
9
|
+
export { createChildWorktree, finalizeChildWorktree, inspectIndexFlags, inspectWorktreeDirty, WorktreeSetupError, worktreeContextNote, } from "./worktree.js";
|
|
10
|
+
export { prepareExactReviewEvidence, REVIEW_MAX_PATCH_BYTES, REVIEW_MAX_PATHS, } from "./review-evidence.js";
|
|
10
11
|
const CODEX_ALIAS = /^openai-codex-(?:[2-9]|[1-9]\d+)$/;
|
|
11
12
|
const MULTI_CODEX_EXTENSION = fileURLToPath(import.meta.resolve("@henryqw/pi-multi-codex/extensions/multi-codex.ts"));
|
|
12
13
|
const ROLE_TOOLS_EXTENSION = fileURLToPath(new URL("../extensions/role-tools.ts", import.meta.url));
|
|
13
14
|
export const ROLE_TOOL_POLICY_FLAG = "pi-subagent-role-tools";
|
|
14
|
-
export const
|
|
15
|
+
export const CHILD_EXCLUDED_TOOL_NAMES = ["delegate_task", "delegate_flow", "delegate_flow_continue", "ask_question"];
|
|
16
|
+
export const CHILD_EXCLUDED_TOOLS = CHILD_EXCLUDED_TOOL_NAMES.join(",");
|
|
15
17
|
const CHILD_IDENTITY_POLICY = "You are a delegated Pi Subagent, not Main. Execute the assigned Role and task directly. Main-only delegation rules do not apply. Recursive delegation is unavailable; do not seek or invoke delegation tools.";
|
|
16
18
|
const cleanText = (value, field, source) => {
|
|
17
19
|
if (typeof value !== "string" || !value.trim() || value.includes("\0")) {
|
|
@@ -19,17 +21,13 @@ const cleanText = (value, field, source) => {
|
|
|
19
21
|
}
|
|
20
22
|
return value.trim();
|
|
21
23
|
};
|
|
22
|
-
const stringList = (value, field, source
|
|
23
|
-
if (value === undefined)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return [];
|
|
27
|
-
}
|
|
28
|
-
const values = typeof value === "string" ? value.split(",") : value;
|
|
29
|
-
if (!Array.isArray(values) || values.some((item) => typeof item !== "string" || !item.trim() || item.includes("\0"))) {
|
|
24
|
+
const stringList = (value, field, source) => {
|
|
25
|
+
if (value === undefined)
|
|
26
|
+
throw new Error(`${source}: ${field} is required.`);
|
|
27
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || !item.trim() || item.includes("\0"))) {
|
|
30
28
|
throw new Error(`${source}: ${field} must be an array of strings.`);
|
|
31
29
|
}
|
|
32
|
-
return
|
|
30
|
+
return value.map((item) => item.trim());
|
|
33
31
|
};
|
|
34
32
|
function validateExtension(extension, source) {
|
|
35
33
|
const value = cleanText(extension, "extension", source);
|
|
@@ -45,7 +43,7 @@ function extensionList(value, source) {
|
|
|
45
43
|
}
|
|
46
44
|
// Built-in Roles required by the bundled pi-subagent-delegated-development Skill;
|
|
47
45
|
// resolved from the package-shipped Markdown relative to this module.
|
|
48
|
-
const
|
|
46
|
+
const BUILTIN_ROLE_NAMES = ["implementer", "reviewer"];
|
|
49
47
|
/** Single-file Role parser shared by built-in and user roles. */
|
|
50
48
|
function parseRoleFile(file, raw) {
|
|
51
49
|
let parsed;
|
|
@@ -62,7 +60,7 @@ function parseRoleFile(file, raw) {
|
|
|
62
60
|
return {
|
|
63
61
|
name: cleanText(frontmatter.name, "name", file),
|
|
64
62
|
description: cleanText(frontmatter.description, "description", file),
|
|
65
|
-
tools:
|
|
63
|
+
tools: stringList(frontmatter.tools, "tools", file),
|
|
66
64
|
isolation,
|
|
67
65
|
extensions: extensionList(frontmatter.extensions, file),
|
|
68
66
|
skills: stringList(frontmatter.skills, "skills", file),
|
|
@@ -79,8 +77,11 @@ function readRoleFile(file) {
|
|
|
79
77
|
throw new Error(`${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
80
78
|
}
|
|
81
79
|
}
|
|
80
|
+
export function loadBuiltinRole(name) {
|
|
81
|
+
return readRoleFile(fileURLToPath(new URL(`../examples/roles/${name}.md`, import.meta.url)));
|
|
82
|
+
}
|
|
82
83
|
function builtinRoles() {
|
|
83
|
-
return
|
|
84
|
+
return BUILTIN_ROLE_NAMES.map(loadBuiltinRole);
|
|
84
85
|
}
|
|
85
86
|
/**
|
|
86
87
|
* Validated built-in implementer/reviewer Roles plus valid user roles from
|
|
@@ -151,21 +152,12 @@ export function resolveRoleSkills(pi, role) {
|
|
|
151
152
|
export function createRoleLaunch(pi, ctx, input) {
|
|
152
153
|
const role = input.role;
|
|
153
154
|
const skills = resolveRoleSkills(pi, role);
|
|
154
|
-
|
|
155
|
-
if (baseTools === undefined && input.tools !== undefined) {
|
|
156
|
-
const builtins = new Set(pi.getAllTools()
|
|
157
|
-
.filter((tool) => tool.sourceInfo.source === "builtin")
|
|
158
|
-
.map((tool) => tool.name));
|
|
159
|
-
baseTools = pi.getActiveTools().filter((tool) => builtins.has(tool));
|
|
160
|
-
}
|
|
161
|
-
const tools = baseTools === undefined
|
|
162
|
-
? undefined
|
|
163
|
-
: [...new Set([...baseTools, ...(input.tools ?? [])].map((tool) => cleanText(tool, "tool", `Role ${role.name}`)))];
|
|
155
|
+
const tools = [...new Set([...role.tools, ...(input.tools ?? [])].map((tool) => cleanText(tool, "tool", `Role ${role.name}`)))];
|
|
164
156
|
const extensions = [
|
|
165
157
|
...role.extensions,
|
|
166
158
|
...(input.extensions ?? []),
|
|
167
159
|
...(CODEX_ALIAS.test(input.route.model.provider) ? [MULTI_CODEX_EXTENSION] : []),
|
|
168
|
-
|
|
160
|
+
ROLE_TOOLS_EXTENSION,
|
|
169
161
|
].map((extension) => validateExtension(extension, `Role ${role.name}`));
|
|
170
162
|
const env = Object.fromEntries(Object.entries(input.env ?? {}).map(([key, value]) => {
|
|
171
163
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
|
|
@@ -179,8 +171,7 @@ export function createRoleLaunch(pi, ctx, input) {
|
|
|
179
171
|
args.push("--extension", extension);
|
|
180
172
|
for (const skill of skills.paths)
|
|
181
173
|
args.push("--skill", skill);
|
|
182
|
-
|
|
183
|
-
args.push(`--${ROLE_TOOL_POLICY_FLAG}`, JSON.stringify(tools));
|
|
174
|
+
args.push(`--${ROLE_TOOL_POLICY_FLAG}`, JSON.stringify(tools));
|
|
184
175
|
args.push("--model", modelReference(input.route.model));
|
|
185
176
|
if (input.route.thinkingLevel)
|
|
186
177
|
args.push("--thinking", input.route.thinkingLevel);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const REVIEW_MAX_PATHS = 1000;
|
|
2
|
+
export declare const REVIEW_MAX_PATCH_BYTES: number;
|
|
3
|
+
export interface PrepareExactReviewEvidenceInput {
|
|
4
|
+
base: string;
|
|
5
|
+
tip: string;
|
|
6
|
+
worktree: string;
|
|
7
|
+
}
|
|
8
|
+
export interface PreparedReviewEvidence {
|
|
9
|
+
base: string;
|
|
10
|
+
tip: string;
|
|
11
|
+
worktree: string;
|
|
12
|
+
changedPaths: string[];
|
|
13
|
+
patchPath: string;
|
|
14
|
+
cleanup: () => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/** Prepares the one exact private patch Flow supplies to its Reviewer. */
|
|
17
|
+
export declare function prepareExactReviewEvidence(request: PrepareExactReviewEvidenceInput, signal?: AbortSignal): Promise<PreparedReviewEvidence>;
|