@howarewoo/pi-subagents 0.1.1 → 0.2.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/CHANGELOG.md +50 -0
- package/README.md +268 -97
- package/docs/advanced-policies.md +4 -2
- package/docs/delegation-contract.md +57 -28
- package/docs/images/subagent-hub.svg +1 -1
- package/docs/images/subagent-profiles.svg +1 -1
- package/docs/images/subagent-widget.svg +1 -1
- package/docs/releases.md +111 -23
- package/docs/verification.md +1 -1
- package/package.json +4 -4
- package/src/child-execution.ts +459 -0
- package/src/coordinator.ts +608 -664
- package/src/delegation-types.ts +111 -0
- package/src/hub.ts +80 -300
- package/src/pi-extended-subagents.ts +83 -189
- package/src/profile-viewer.ts +109 -38
- package/src/scheduling.ts +1 -1
- package/src/subagent-widget.ts +150 -0
- package/src/supported-host.ts +26 -0
- package/src/task-input.ts +3 -2
- package/src/task-presentation.ts +196 -0
- package/src/tool-schemas.ts +165 -0
|
@@ -1,7 +1,27 @@
|
|
|
1
|
-
# Delegation contract (Pi 1.5.2)
|
|
1
|
+
# Delegation contract (minimum stable Pi 1.5.2)
|
|
2
2
|
|
|
3
3
|
Reference for operators and extension/UI consumers. Authoritative types and execution registry: [`src/coordinator.ts`](../src/coordinator.ts); profiles/grants: [`src/profiles.ts`](../src/profiles.ts); finite policy: [`src/scheduling.ts`](../src/scheduling.ts); public model/auth adapter: [`src/model-adapter.ts`](../src/model-adapter.ts); model tool/lifecycle: [`src/pi-extended-subagents.ts`](../src/pi-extended-subagents.ts). The native interfaces remain supported alongside the bounded orchestration compatibility surface below; this is not full OMP/pi-subagents API parity.
|
|
4
4
|
|
|
5
|
+
## Supported host
|
|
6
|
+
|
|
7
|
+
Delegated execution requires **stable Pi 1.5.2 or newer**, checked through the SDK's public
|
|
8
|
+
`VERSION` export. Newer stable patch, minor and major versions are allowed; standard build
|
|
9
|
+
metadata has equal precedence. **Tested host: Pi 1.5.2**, independently of the minimum;
|
|
10
|
+
allowing future stable versions is not a tested compatibility guarantee.
|
|
11
|
+
Below-minimum, prerelease, malformed and unknown versions fail closed before launch/preflight
|
|
12
|
+
content reads, child identity allocation, task-directory setup or child model requests.
|
|
13
|
+
This covers `delegate_task`, compatibility `subagent` launches, resume, followup, revive,
|
|
14
|
+
steering/model-delivering messages and direct coordinator execution. Public tool arguments
|
|
15
|
+
and results are unchanged; unsupported execution errors name the detected version and minimum stable version.
|
|
16
|
+
|
|
17
|
+
Registration is not rejected wholesale: root status, stop, cleanup and diagnostics remain
|
|
18
|
+
available where the host can load the extension. This does not promise module-load
|
|
19
|
+
compatibility with arbitrary ancient SDKs. Required `"*"` host peer ranges follow Pi's
|
|
20
|
+
packaging contract, not the execution support window; development SDK packages remain pinned.
|
|
21
|
+
This restriction is a breaking change in 0.2.0. To migrate from 0.1.x, let active tasks drain,
|
|
22
|
+
install stable Pi 1.5.2 or newer, update the extension to `@howarewoo/pi-subagents@0.2.0`
|
|
23
|
+
and restart Pi.
|
|
24
|
+
|
|
5
25
|
## Native orchestration compatibility
|
|
6
26
|
|
|
7
27
|
The model-only `subagent` tool is registered and described through Pi's public `promptSnippet`/`promptGuidelines` at startup. These describe native worker delegation to the model without requiring any external workflow package or provider-specific user reminder. With an explicit tool allowlist, include `subagent`. Load only one provider of that name: Pi uses the first registered extension, and this package does not disable, reprioritize or uninstall other providers.
|
|
@@ -9,10 +29,10 @@ The model-only `subagent` tool is registered and described through Pi's public `
|
|
|
9
29
|
- Launch: `{agent,task,cwd?,model?,limits?,async?}`. `agent` is required: `general`, `scout`, `review`, `implementation`, or explicit compatibility name `worker` for the configured `implementation` profile. Unknown/custom agent definitions are rejected, not silently mapped. Authority and prompts still resolve through the existing profiles; no ambient Markdown agent loading or role/model fallback is added.
|
|
10
30
|
- Batch: `{tasks:[{agent,task,cwd?,model?,limits?}],cwd?,async?}`. Shared cwd is the default for items lacking one. Existing whole-batch preflight, FIFO limits, ordered outcomes and global writer serialization apply.
|
|
11
31
|
- `async` defaults true; false waits for final results. Accepted calls return immediately with exact `id:"UUID@generation"`, `taskId` (original UUID), generation and normal snapshot fields/artifact references. Control handles never prefix-match or resolve to the latest generation. Native `delegate_task` retains its UUID+generation output and foreground default.
|
|
12
|
-
- Read/control: `{action:"agents"}` or `"list"`; `{action:"status"|"stop",id}`; `{action:"steer"|"resume",id,message}`. Status can inspect a historical generation; mutations refuse stale handles. Resume
|
|
32
|
+
- Read/control: `{action:"agents"}` or `"list"`; `{action:"status"|"stop",id}`; `{action:"steer"|"resume",id,message}`. Status can inspect a historical generation; mutations refuse stale handles. Resume continues a drained paused assignment in the same SDK conversation and generation. For completed idle/parked presence it still starts a fresh generation in the original workspace, requiring current narrowing checks; include needed prior conclusions for that fresh assignment. Native coordination remains available for richer inbox/wait/park behavior.
|
|
13
33
|
- Model overrides accept the native exact selection object or `provider/id[:effort]`. Omitted effort uses the parent's selected effort; overrides still require exact operator grants, availability/auth/scope and supported effort. Bare model IDs and unknown providers are not guessed. Omitting model retains ordinary parent/profile routing.
|
|
14
34
|
|
|
15
|
-
An async compatibility launch sends one
|
|
35
|
+
An async compatibility launch sends one window-outcome custom message per single/batch call after actual drain (paused or completed), using public `sendMessage(...,{triggerTurn:true,deliverAs:"followUp"})` to wake the parent. Compatibility resume does the same once for its new execution window or fresh completed-agent generation. This may cause a parent model turn and provider charges; no polling or detached worker survives parent shutdown. The original native tool's notification-only behavior is unchanged. Neither a notification nor a receipt proves the model understood the outcome.
|
|
16
36
|
|
|
17
37
|
The coordinator owns workspace allocation, exclusive writer handoff, Git/PR publication and retirement under the active workflow. Supply a complete task and accessible skill/reference paths: general inherits available skills and restricted reviewed leaf capabilities as specified below; workers never inherit parent instructions/history or context files. No scripts/workflow engine, supervisor RPC, automatic worktree lifecycle, external CLI worker, retained-run migration or blanket competitor API parity is promised.
|
|
18
38
|
|
|
@@ -69,7 +89,7 @@ Each item must supply **exactly one** of `assignment` or `taskFile`. The same al
|
|
|
69
89
|
|
|
70
90
|
`taskFile` is an explicit path relative to the selected **task workspace cwd** (root parent cwd by default), not the session/transcript directory. Absolute paths, traversal, hidden path components and `node_modules` components are rejected. The denied-component rule applies to both caller spelling and the resolved workspace-relative target before any content dispatch; an ordinary-looking symlink cannot alias hidden or `node_modules` resources. Canonical paths must stay inside that workspace; escaping symlinks fail. Nonescaping aliases to otherwise allowed ordinary files remain supported. Metadata checks require a regular file with 1–50000 bytes. The effective parent must expose genuine callable builtin `read`; the extension obtains **all content through `ctx.executeTool("read",...)`**, preserving validation, permission denials and result hooks. Node is used only for path/metadata checks. No raw filesystem content read, ambient discovery or credential/ignored-tree copying occurs. Operators should explicitly select reviewed task-text files, never secrets.
|
|
71
91
|
|
|
72
|
-
Only complete single-block text is accepted: native read truncation (including the 2000-line limit), images, empty/whitespace text, NUL, replacement characters and oversized content fail. Consequently valid UTF-8 containing a literal replacement character is conservatively rejected too. Files are instructions as literal **task text**; embedded JSON/frontmatter/tool names never grant profiles, tools, spawn roles, schemas or limits. Nested file reads require the
|
|
92
|
+
Only complete single-block text is accepted: native read truncation (including the 2000-line limit), images, empty/whitespace text, NUL, replacement characters and oversized content fail. Consequently valid UTF-8 containing a literal replacement character is conservatively rejected too. Files are instructions as literal **task text**; embedded JSON/frontmatter/tool names never grant profiles, tools, spawn roles, schemas or limits. Nested file reads require both the admitted and currently resolved read grants of the initiator and every ancestor, independently of spawn authorization, and consume the initiator's executable-tool budget in addition to the local delegation invocation. Rejected preflight may have performed permission-checked reads, but creates **no new identities/children** and consumes no spawn quota.
|
|
73
93
|
|
|
74
94
|
Preflight reads are awaited sequentially so a later failure cannot detach an earlier permission hook. Canonical path and identity/size/mtime/ctime are checked before/after reading. The accepted text is an in-memory admission snapshot: subsequent edits do not update queued tasks. These checks detect ordinary changes, not malicious filesystem races; public read has no atomic no-follow handle/content-identity interface. Trusted operator hooks may transform the read/result. This is not a sandbox or a freshness guarantee against concurrent hostile processes. Do not edit/swap task inputs during preflight.
|
|
75
95
|
|
|
@@ -85,7 +105,7 @@ Invalid submission returns actionable JSON-Pointer errors (up to 32) and permits
|
|
|
85
105
|
|
|
86
106
|
## Full output artifacts and previews
|
|
87
107
|
|
|
88
|
-
New task directories are created exclusively. After prompt resolution and descendant/tool drain, the registry enters `draining` while it publishes fixed-name artifacts alongside `agent/` and `transcripts/`:
|
|
108
|
+
New task and successor-generation directories are created exclusively with POSIX mode `0700`, as are their extension-owned `agent/` and `transcripts/` directories. The SDK manages transcript files inside that private directory. Existing records/files, parent session directories and global storage permissions are not changed; Windows privacy depends on filesystem ACLs rather than these POSIX mode bits (see [security policy](../SECURITY.md)). After prompt resolution and descendant/tool drain, the registry enters `draining` while it publishes fixed-name artifacts alongside `agent/` and `transcripts/`:
|
|
89
109
|
|
|
90
110
|
- `result.txt`: exact concatenation (no added separators or trimming) of text blocks in the **last assistant message**, including an empty result for a tool-only/aborted last message. It is not the entire conversation. Existing `result` still uses Pi's final-assistant-text helper and retains its historical whitespace trimming/fallback semantics.
|
|
91
111
|
- `structured.json`: JSON serialization of the first accepted structured value, if any; it is not reconstructed from prose or a schema. Read it as JSON to recover the exact submitted value, not original wire formatting.
|
|
@@ -109,26 +129,26 @@ Model-facing previews clip large text fields and structured JSON to 2048 charact
|
|
|
109
129
|
|
|
110
130
|
This read-only narrowing permits a general child to delegate a general grandchild if one additional slot is available. It does not enable arbitrary tools, profiles, models or ambient resources. A trusted project may only narrow operator tools, leaf tools, skills, spawn roles and policy maximums; it cannot re-enable anything removed. Untrusted project config is ignored. Project prompt/description specialization never expands executable authority. Unknown fields, malformed selected profiles, missing skills and loader diagnostics fail visibly. Custom role names remain unsupported. General supports only the restricted leaf grants below. Exact model/effort and builtin one-way prewalk policy are specified in the [advanced policy ledger](advanced-policies.md).
|
|
111
131
|
|
|
112
|
-
Descendant preflight requires an active, non-cancelling ancestor, an explicitly granted target role, allowed absolute depth and remaining direct-child spawn count. Target builtin tools, reviewed leaf tools, skills **and spawn role grants** must all be subsets of the delegating ancestor's effective grants; incompatible profiles reject rather than regain root authority. Request narrowing is inherited. Each child's admitted direct-child count is lifetime cumulative (including cancelled/failed children); rejected batches consume no spawn count.
|
|
132
|
+
Descendant preflight requires an active, non-cancelling ancestor, an explicitly granted target role, allowed absolute depth and remaining direct-child spawn count. Target builtin tools, reviewed leaf tools, skills **and spawn role grants** must all be subsets of the delegating ancestor's effective grants; incompatible profiles reject rather than regain root authority. Request narrowing is inherited. Before nested task-file content dispatch and again before child admission, current operator/trusted-project spawn grants and capability ceilings are checked for the initiator and every ancestor, in addition to their original admitted ceilings. Revoked read or spawn authority denies before the task-file content reaches the root read dispatcher; malformed policy fails closed. Each child's admitted direct-child count is lifetime cumulative (including cancelled/failed children); rejected batches consume no spawn count.
|
|
113
133
|
|
|
114
134
|
## Capacity and bounded execution
|
|
115
135
|
|
|
116
136
|
`SchedulingPolicy` values are non-negative safe integers, except concurrency/batch/deadline/requests/tools must be positive. There is no unlimited sentinel.
|
|
117
137
|
|
|
118
|
-
| Field | Default | Operator ceiling | Meaning
|
|
119
|
-
| ------------- | ------: | ---------------: |
|
|
120
|
-
| `concurrency` | 16 | 16 | Active SDK children, including setup, waiting nested parents and draining
|
|
121
|
-
| `queue` | 32 | 256 | Waiting assignments, accounting for writer/FIFO blocking as well as capacity
|
|
122
|
-
| `batch` | 16 | 64 | Assignments admitted by one call
|
|
123
|
-
| `depth` | 2 | 4 | Maximum absolute descendant depth (root = 0, child = 1, grandchild = 2); 0/1 disables nesting
|
|
124
|
-
| `spawns` | 8 | 32 | Admitted direct children per child lifetime; 0 disables spawning
|
|
125
|
-
| `deadlineMs` |
|
|
126
|
-
| `requests` | 100 | 1000 | Direct child dispatches into the parent model registry
|
|
127
|
-
| `tools` | 100 | 1000 | Executable child tool invocations, including local delegation
|
|
138
|
+
| Field | Default | Operator ceiling | Meaning |
|
|
139
|
+
| ------------- | ------: | ---------------: | ----------------------------------------------------------------------------------------------------- |
|
|
140
|
+
| `concurrency` | 16 | 16 | Active SDK children, including setup, waiting nested parents and draining |
|
|
141
|
+
| `queue` | 32 | 256 | Waiting assignments, accounting for writer/FIFO blocking as well as capacity |
|
|
142
|
+
| `batch` | 16 | 64 | Assignments admitted by one call |
|
|
143
|
+
| `depth` | 2 | 4 | Maximum absolute descendant depth (root = 0, child = 1, grandchild = 2); 0/1 disables nesting |
|
|
144
|
+
| `spawns` | 8 | 32 | Admitted direct children per child lifetime; 0 disables spawning |
|
|
145
|
+
| `deadlineMs` | 1800000 | 3600000 | Per-window elapsed time from admission/resume, including queue/setup/work drain; paused time excluded |
|
|
146
|
+
| `requests` | 100 | 1000 | Direct child dispatches into the parent model registry |
|
|
147
|
+
| `tools` | 100 | 1000 | Executable child tool invocations, including local delegation |
|
|
128
148
|
|
|
129
149
|
Operator `scheduling` can choose within ceilings; trusted project can only narrow operator values. Request `limits` can only narrow execution/depth/spawn values, not raise concurrency or queue capacity. Policy is resolved at invocation; changes affect subsequent admission/dequeue, not already running work or previously captured task limits. Reducing concurrency does not evict already admitted active children; no new setup starts until they drain below the new maximum.
|
|
130
150
|
|
|
131
|
-
One FIFO scheduler lives **inside the authoritative registry**, not in a second execution-state service. Identity allocation/order follows input order. Queue admission/dequeue is synchronous, before asynchronous SDK setup. A head writer waiting for its lease blocks later readers too: this conservative head-of-line policy prevents writer starvation. Parent model mutations already reserved at tool preflight reject a writer-containing batch before admission; otherwise sibling writers queue/serialize. Setup, running, nested waiting and draining each retain a permit until disposal.
|
|
151
|
+
One FIFO scheduler lives **inside the authoritative registry**, not in a second execution-state service. Identity allocation/order follows input order. Queue admission/dequeue is synchronous, before asynchronous SDK setup. A head writer waiting for its lease blocks later readers too: this conservative head-of-line policy prevents writer starvation. Parent model mutations already reserved at tool preflight reject a writer-containing batch before admission; otherwise sibling writers queue/serialize. Setup, running, nested waiting, pausing and draining each retain a permit until actual drain; only then may pause or disposal release it.
|
|
132
152
|
|
|
133
153
|
### Nested allocation: no lending and no overcommit
|
|
134
154
|
|
|
@@ -136,7 +156,15 @@ A parent waiting in its child-local `delegate_task` retains its permit. Nested b
|
|
|
136
156
|
|
|
137
157
|
### Limit outcomes and cancellation
|
|
138
158
|
|
|
139
|
-
|
|
159
|
+
Deadlines pause rather than cancel. `queued|setup|running → pausing → paused` stops new dispatch, aborts SDK work and waits for actual subtree drain before releasing capacity and writer leases. Snapshots expose `pauseReason` (`deadline` or `ancestor deadline`), `retainedContext`, `window`, cumulative request/tool/spawn counters, and unfinalized structured submission state. Paused is neither success nor failure; no final outcome artifacts are published. A queued assignment can pause before any session/transcript exists.
|
|
160
|
+
|
|
161
|
+
Root-only `delegate_coordinate` `{action:"resume",target:{id,generation},assignment:"Continue…"}` or `subagent` `{action:"resume",id:"UUID@generation",message:"Continue…"}` appends that message to the retained SDK conversation. UUID, generation, assignment, workspace, selected model/prewalk state, role instructions, all prior SDK messages/tool calls/results and structured submission state remain intact. Each explicit resume starts a fresh configured deadline window (still bounded by originally admitted and current limits). Cumulative requests/tools/spawns are **not** replenished. A pre-session queued pause starts the original assignment followed by the continuation when dispatched. No automatic replay, undo, process freezing, restart or cross-parent recovery is provided.
|
|
162
|
+
|
|
163
|
+
An ancestor deadline marks its entire active subtree before draining any member. Resume the exact original parent generation first, then explicitly resume selected children; resuming a parent does not resume descendants. Existing no-permit-lending, writer ancestry and admission rules still apply. Parent completion/stop retires its children, including paused sessions.
|
|
164
|
+
|
|
165
|
+
Resume rechecks operator/trusted-project limits and grants, ancestor spawn/capability ceilings, model availability/authentication, callable tools/provenance and writer/capacity admission. It never broadens original admission. A revoked retained grant rejects rather than rebuilding the SDK tool/resource graph; restore the grant or stop and launch a newly authorized identity. Policy/auth/capacity failure leaves the paused conversation intact with an actionable tool error; there is no fallback model or replacement agent. Concurrent duplicate resumes reject atomically. Stop/shutdown wins a concurrent pending resume and disposes retained sessions.
|
|
166
|
+
|
|
167
|
+
Requests/tools remain hard cancellation guards. The first committed hard-limit reason (`requests` or `tools`) is retained and produces final `status:"cancelled"`, `limitReason`, and explanatory `error`. Stop before setup cancels without SDK setup/transcript. Request guards check **before forwarding** to the parent's public registry. Counts include failed dispatches but not rejected over-limit dispatches, auth preflight or provider-internal retries/subrequests. Tool guards count entered, granted custom-tool executions, including permission-denied forwarded calls; malformed/unknown calls rejected by Pi before execution are not counted. Requests still bound loops of rejected tools.
|
|
140
168
|
|
|
141
169
|
Deadlines initiate abort; they do **not** promise a maximum drain time. Public auth/provider/permission code that ignores cancellation must return before setup/tools release their capacity/writer lease. No fake timeout races away from unabortable initialization, force-kills unrelated processes, or starts a replacement writer while old tools remain active. SDK-owned tool cancellation uses actual abort signals and public `abort()`/prompt completion. Abrupt process death is not recoverable cleanup.
|
|
142
170
|
|
|
@@ -154,15 +182,15 @@ Forwarded root builtins must be callable, genuine `builtin:<name>` tools (not ov
|
|
|
154
182
|
|
|
155
183
|
- `ChildRef = {id:string,generation:number}`. UUIDs are never reassigned to another agent; generation starts at 1 and increases on admitted reassignment. Mutating controls reject unknown/stale references; historical snapshot reads remain pinned.
|
|
156
184
|
- `ChildSnapshot`: identity, real `parentId`, optional `parentGeneration` for descendants, absolute `depth`, `status`, profile, canonical workspace `cwd`, assignment, activity, bounded progress (4096 characters), final text, actual model/effort, optional `prewalk:{target,status}` (`armed`, `handed-off`, `suppressed-exact` or `no-op`), direct `telemetry`, optional error/transcript, `limits`, `limitReason?`, `toolCalls`, `writerLease`, and `disposed`.
|
|
157
|
-
- Lifecycle: `queued → setup → running → settled|failed|cancelled
|
|
185
|
+
- Lifecycle: `queued → setup → running → settled|failed|cancelled`, plus `pausing → paused → queued` for explicit deadline continuation; cancellation of active work exposes `draining` until it actually ends. Queued work can cancel directly. `disposed` and `writerLease` are resource facts, not guessed outcomes. Parents cannot finalize/dispose before descendants/tools drain.
|
|
158
186
|
- `RegistryEvent = {type:"changed"|"sdk",child,event?:AgentSessionEvent}`. Detached snapshots; observer exceptions cannot fail execution. `subscribe` returns unsubscribe. `list()` retains identity admission order and returns current generations only; `snapshot(ref?)` reads a pinned current/historical generation or latest identity; `generations(id)` returns detached immutable history plus current generation. `agents()` separately reports current presence. Pi event bus `delegation:registry` exposes the same events, including disposal at shutdown. Consumers unsubscribe on runtime replacement.
|
|
159
187
|
- `configure(policy)` validates finite scheduler policy. `admit(StartTask[])` synchronously validates the whole batch and returns ordered identities; `start(StartTask)` is the single helper, awaiting abortable setup if dispatched immediately, otherwise acknowledging queued admission. Model tool uses `admit` for both input shapes. Every `StartTask`, including direct SDK integration, requires `assertTools(names)`, a synchronous provenance validator backed by the originating extension’s public `pi.getAllTools()` metadata. Missing validators reject at runtime. This is a trusted integration seam, not a model/operator policy option or a sandbox for arbitrary extension JavaScript.
|
|
160
|
-
- `wait(ref)` resolves the
|
|
188
|
+
- `wait(ref)` resolves the current window snapshot after drained pause or disposal, including setup failure; `stop(ref)` aborts and drains a subtree. `stopMany(refs)` marks the entire cancellation set before any sibling dequeue and then drains it; already settled results are preserved. Repeated stop is harmless. `steer(ref,text)` requires running state and uses Pi `queued|handled` semantics.
|
|
161
189
|
- `shutdown()` closes admission/dequeue immediately, cancels/drains/disposes all entries and clears observers idempotently. Bare SDK `session.dispose()` does not replace host lifecycle dispatch. `reopenTranscript(ref)` inspects a new transcript; it does not revive work.
|
|
162
190
|
|
|
163
191
|
Prompt resolution, not low-level `agent_end`, accepts final results after SDK work settles. SDK assistant errors/setup exceptions are failed; abort/limit/shutdown intent cannot become success. Failure of one admitted assignment **does not cancel unrelated batch siblings**: every input receives its own outcome. Caller foreground cancellation marks all unsettled assignments then drains their subtrees; settled outcomes remain settled. Operator stop targets only its pinned subtree.
|
|
164
192
|
|
|
165
|
-
Foreground returns one ordered
|
|
193
|
+
Foreground returns one ordered window-outcome batch after window drain (single input returns one snapshot), whether paused (`disposed:false`) or completed after disposal. Partial tool updates are observations, not window-outcome deliveries. Background returns ordered accepted snapshots/identities immediately (possibly queued/setup), and emits **one window-outcome custom message per call**, containing all ordered outcomes after window drain (paused or completed). Native delivery is notification-only; async compatibility delivery wakes the parent with `triggerTurn:true,deliverAs:"followUp"`. It does not also deliver per-item completion messages. Background no longer follows the initiating tool's signal after acceptance; stop/shutdown still own it. Old-runtime shutdown suppresses its final custom delivery; work never survives reload. Output previews use Pi 50KB/2000-line truncation. Tool `details` retain full snapshots/transcript references (`ChildSnapshot` for single, `{children:ChildSnapshot[]}` for batch); full model/tool evidence is in actual SDK JSONL; final text/structured artifacts follow the protocol above.
|
|
166
194
|
|
|
167
195
|
`/subagent-profiles` resolves current-workspace operator and trusted-project settings through the admission profile/skill validator. In TUI mode it opens a read-only list/details overlay (↑↓ select; j/k, PgUp/Dn, Home/End scroll; configured cancel/Esc closes). Narrow layouts stack; tiny layouts request resize. Reopening refreshes the snapshot. It shows prompts, descriptions, builtin and reviewed-leaf ceilings, skill paths, spawn/coordination and model/prewalk policy, not guaranteed session rights. General skill resolution uses the public command context’s base prompt skill metadata, never parent prompt text or context files; per-turn prompt changes can differ at admission. Read-only describes the viewer, not general’s tool authority. Command context cannot establish callable tools; active declarations are not proof of callability. RPC/JSON/print retain the structured `{toolPolicy, profiles}` message. Invalid config or granted skills fails resolution; snapshot/details `tools` reports each task’s admitted effective builtin set. Commands are `/subagent-profiles`, `/subagent-status [id generation]`, `/subagent-stop id generation`, `/subagent-steer id generation text`, `/subagent-hub`. Hub/widget are registry consumers: current fleet counts, ancestry/selected-generation history, readonly full transcript/tools/results and bounded pinned controls are described below. Queued/draining states and actual writer lease are shown without fabricated completion.
|
|
168
196
|
|
|
@@ -174,29 +202,30 @@ Foreground returns one ordered final batch (single input returns one snapshot).
|
|
|
174
202
|
|
|
175
203
|
`delegate_coordinate` is a native **model-only** tool registered with Pi conflict handling, not a pi-subagents alias. It must be enabled if the host uses an explicit tool allowlist. Root has current-registry controls; children get a child-local closure only when their effective profile grants at least one action. Inputs never accept sender/parent/runtime/generation overrides for the actor. Child invocation still counts against that generation's tool/deadline/request limits.
|
|
176
204
|
|
|
177
|
-
Operator `coordination` grants selected `message`, `inbox`, `wait` actions; absent/empty is off. Trusted project and task request arrays only narrow operator grants. Descendants cannot regain any ancestor action. Addressing is restricted to exact-generation parent, direct children and siblings whose parent UUID+generation match. Identities must be explicitly supplied in task/message context: there is no child registry enumeration, transcript read or arbitrary resource lookup. Only direct children may address root by omitting `target`; root cannot message itself. Root-only `status`, `stop`, `park`, `followup`, `revive` are rejected from child closures even if model input names them.
|
|
205
|
+
Operator `coordination` grants selected `message`, `inbox`, `wait` actions; absent/empty is off. Trusted project and task request arrays only narrow operator grants. Descendants cannot regain any ancestor action. Every message, inbox read and wait authorization reloads current initiator/ancestor policy and checks the original admitted action ceilings too. Denial sends no message, consumes no inbox and allocates no waiter. Cancellation, pause, shutdown and stale generations are rechecked after policy-loading awaits before protected effects. Live revocation governs subsequent actions: it does not forcibly cancel already in-flight work, retract accepted messages or roll back completed effects. Addressing is restricted to exact-generation parent, direct children and siblings whose parent UUID+generation match. Identities must be explicitly supplied in task/message context: there is no child registry enumeration, transcript read or arbitrary resource lookup. Only direct children may address root by omitting `target`; root cannot message itself. Root-only `status`, `stop`, `park`, `resume`, `followup`, `revive` are rejected from child closures even if model input names them.
|
|
178
206
|
|
|
179
207
|
| Action | Inputs and behavior |
|
|
180
208
|
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
181
209
|
| `message` | `target?:ChildRef`, nonblank `text` (at most 4096 UTF-8 bytes), `mode?:"inbox" | "steer" | "followUp"` (default inbox). Requires a running recipient. Omitted target means root inbox, never arbitrary supervisor RPC. |
|
|
182
210
|
| `inbox` | Destructively reads only the actor's mailbox. Successful tool output exposes messages to that actor's model. No automatic root inference is triggered. |
|
|
183
|
-
| `wait` | Required `timeoutMs:1..60000`; optional `target` for one
|
|
211
|
+
| `wait` | Required `timeoutMs:1..60000`; optional `target` for one drained window outcome (paused or terminal), or `messageId` for one known receipt, or neither for own inbox. Target and messageId together reject. Receipts are visible only to their originating sender/recipient. |
|
|
184
212
|
| `status` | Root-only pinned `target`; returns `{snapshot,generations,agents}`. Snapshot/history describe assignments; agents describes current presence. Full detached details, bounded text preview. |
|
|
185
213
|
| `stop` | Root-only pinned current `target`; closes idle/parked presence or aborts/drains active subtree. Completed outcomes/files are unchanged. Stopped identities cannot revive. |
|
|
186
214
|
| `park` | Root-only current disposed idle target; records parked presence. Active parking rejects: stop active work explicitly. No live SDK session, shell, permit or writer lease is retained while idle or parked. |
|
|
215
|
+
| `resume` | Root-only drained paused `target` and nonblank `assignment` continuation. Returns the same identity/window snapshot and sends one notification after that resumed window drains. No schema replacement or descendant auto-resume. |
|
|
187
216
|
| `followup` / `revive` | Root-only current target and nonblank `assignment`, optional supported `resultSchema`. Followup requires idle; revive requires parked. Returns the new admitted snapshot, not final success. Use wait/status for completion; no unsolicited duplicate generation-completion message is added. |
|
|
188
217
|
|
|
189
218
|
A message returns `{id,sender,recipient,text,mode,status,error?}`. `accepted` means mailbox admission, `queued` means the public SDK accepted a steer/followUp, `handled` means an SDK input handler consumed it, `delivered` means the exact message entered the recipient transcript or an inbox tool/wait returned it. None means model acknowledgment, understanding or assignment completion. `not-running` means no running recipient (including an undelivered receipt whose recipient ended), and `error` preserves delivery errors. Unknown/foreign/stale identities or receipts reject visibly, not as apparent successful delivery. Inbox messages are not automatically steered; steer/followUp are also retained in the bounded mailbox. Their `delivered` state comes from actual SDK user-message events, not queue acceptance. Message text is literal prefixed data, not tool authority.
|
|
190
219
|
|
|
191
|
-
At most **64 messages per mailbox**, **512 admitted messages per parent runtime**, **64 pending waiters**, and **64 generations per identity**. Consumption frees mailbox slots, not receipt-retention budget; exhaustion rejects without silent eviction. All are parent-lifetime memory only, not a broker or durable service. Wait uses registry/message events and a native timeout/abort listener, never polling. It returns `outcome` (identity/status/limitReason only, no peer result/transcript), `messages`, `message` receipt, `timeout`, `cancelled` or `shutdown`. Cancellation/limits/shutdown close child action admission and wake pending waiters.
|
|
220
|
+
At most **64 messages per mailbox**, **512 admitted messages per parent runtime**, **64 pending waiters**, and **64 generations per identity**. Consumption frees mailbox slots, not receipt-retention budget; exhaustion rejects without silent eviction. All are parent-lifetime memory only, not a broker or durable service. Wait uses registry/message events and a native timeout/abort listener, never polling. It returns `outcome` (identity/status/optional limitReason and pauseReason only, no peer result/transcript), `messages`, `message` receipt, `timeout`, `cancelled` or `shutdown`. Cancellation/limits/shutdown close child action admission and wake pending waiters. Paused/terminal outcomes resolve immediately; SDK sessions waiting in tools retain their actual permits. Self/ancestor/dependency-cycle waits reject, including pending sibling delivery receipts; child waits on queued work reject rather than lend permits. Pending root-receipt waits from children are unsupported (a foreground parent may already be waiting for that child); send without waiting for acknowledgment or wait for an explicit reply in the child's inbox. Root outcome waits may await queued work, bounded by the explicit timeout. Timed-out/cancelled waits do not cancel targets; use stop explicitly.
|
|
192
221
|
|
|
193
222
|
### New assignments, generations and immutable history
|
|
194
223
|
|
|
195
|
-
|
|
224
|
+
Completed-agent renewal is distinct from paused continuation. After real tools/descendants/artifacts drain, resources dispose and a non-cancelled generation has **idle presence**; explicit park changes only presence to **parked**. Active presence covers queued/setup/running/draining; deadline transitions expose pausing/paused presence separately. Cancellation/shutdown/stop produces **stopped presence** without rewriting completed assignment status. Completed generations never gain more transcript entries or overwritten artifacts.
|
|
196
225
|
|
|
197
226
|
Followup/revive re-resolve operator/trusted-project policy and selected root model/auth/resources at invocation. Effective grants are intersected with the previous admitted/request-narrowed grants and, for descendants, each current effective ancestor definition and its admitted authority. Removed grants are not restored on later generations, including builtin tools absent from the original callable parent subset. Later parent activation can benefit a newly admitted identity, not expand old identity/generation ceilings. Missing resources/auth or an empty effective builtin set or incompatible policy fail honestly. A new assignment retains the prior schema unless a new supported schema is supplied; no previous submitted value/attempts/status/result/telemetry can satisfy it. Model/tool/deadline budgets restart per assignment, bounded by current policy and previous execution limits. Direct-child spawn consumption remains cumulative for the identity and each descendant renewal consumes another slot of the live parent's remaining lifetime spawn budget.
|
|
198
227
|
|
|
199
|
-
The UUID stays stable and generation increments atomically **only after** synchronous admission checks. Concurrent renewals race on the original reference; exactly one can replace it. A newly owned generation directory `<id>-g<N>` holds its SDK session, `agent/`, transcript and exclusive artifacts. Generation 1 keeps `<id>/`.
|
|
228
|
+
The UUID stays stable and generation increments atomically **only after** synchronous admission checks. Concurrent renewals race on the original reference; exactly one can replace it. A newly owned generation directory `<id>-g<N>` holds its SDK session, `agent/`, transcript and exclusive artifacts. Generation 1 keeps `<id>/`. For these completed-agent fresh generations, no old files are opened for continuation, consulted, replayed, appended or migrated. The new prompt contains only the new explicit assignment plus current role/resources and identity; provide any relevant prior conclusions explicitly. This deliberately avoids retained-conversation replay/tool execution and does not claim a warm resumed Pi session.
|
|
200
229
|
|
|
201
230
|
Descendant renewal requires its **exact original** `parentId+parentGeneration` still active, non-cancelling and authorized. Current ancestor spawn roles, depth/limits, remaining spawn count, immediate nested capacity and writer ancestry all revalidate before generation mutation/setup. Replaced/disposed/stopped ancestors reject; the caller must delegate a new identity through an authorized live parent, never reparent the old one. Parent completion/stop closes descendant idle/parked presence too. Old callbacks, steering, stops, submissions, message delivery and waits capture the originating entry/reference, never retarget by latest UUID. Wait outcomes remain pinned even if a later generation is admitted. Shutdown/reload closes all presence and waiters, drains actual resources and suppresses old-runtime final messages; no cross-parent/process revival exists.
|
|
202
231
|
|
|
@@ -225,9 +254,9 @@ Hub names are snapshot-derived assignment headlines plus role, not a naming regi
|
|
|
225
254
|
| Steering | Hub `s` or `/subagent-steer`, original UUID+generation, running only. Public SDK queued/handled is not model acknowledgment. |
|
|
226
255
|
| Root inbox message | Hub `m` → existing root `message` authority, bounded native mailbox; receipt shows actual state, never model acknowledgment. Model tool additionally supports steer/followUp modes, inbox consumption and receipt waits. |
|
|
227
256
|
| Outcome wait | Hub `w` → native event wait with explicit 1000ms bound; native tool supports 1–60000ms outcome/inbox/receipt waits with existing deadlock/refusal rules. |
|
|
228
|
-
| Stop subtree | Hub `x` → pinned target → `y` confirmation → existing stop/drain, retaining capacity/writer until disposal. Queued/setup/running UI targets supported; historical/draining/disposed UI targets refuse, including transitions during pinned input. `/subagent-stop` and native root stop retain existing semantics.
|
|
257
|
+
| Stop subtree | Hub `x` → pinned target → `y` confirmation → existing stop/drain, retaining capacity/writer until disposal. Queued/setup/running/paused UI targets supported; historical/pausing/draining/disposed UI targets refuse, including transitions during pinned input. `/subagent-stop` and native root stop retain existing semantics. |
|
|
229
258
|
| Park | Hub `p` or native root tool; only disposed idle current identity with valid original parent. No warm SDK retained. |
|
|
230
|
-
| Followup/revive | **Native model-issued `delegate_coordinate` or compatibility `subagent` resume
|
|
259
|
+
| Followup/revive | **Native model-issued `delegate_coordinate` or compatibility `subagent` resume**. Paused assignments continue the retained conversation; idle/parked assignments use fresh session/generation/current narrowed authority. Both require the exact active original parent for descendants. Hub command context lacks `tools/executeTool`: it cannot safely start a generation; f/r only explain refusal, allocate nothing and dispatch nothing. |
|
|
231
260
|
| Child coordination/spawn/model authority | Coordination/model authority remains operator default-off; spawn defaults to scout/review only. All remain project/request/ancestor subset-only; the UI cannot mint grants or impersonate a child. |
|
|
232
261
|
| Manual !/!!, external editors/terminals, arbitrary extension JS | Uncoordinated; model-tool-only writer boundary, never a shell ban/wrapper/sandbox. |
|
|
233
262
|
| Supplied workspace / production isolation | Explicit path/shell-cwd adaptation through root hooks is supported for existing supplied workspaces. Filesystem sandboxing, managed worktrees, copying/apply and cleanup remain unimplemented; cwd is not a resource grant. |
|