@gotgenes/pi-subagents 16.2.1 → 16.3.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/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [16.3.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v16.2.2...pi-subagents-v16.3.0) (2026-06-16)
9
+
10
+
11
+ ### Features
12
+
13
+ * self-seed finished agents in AgentWidget.update ([#377](https://github.com/gotgenes/pi-packages/issues/377)) ([fd99a29](https://github.com/gotgenes/pi-packages/commit/fd99a297f70ce4c1c078fd486be042f691fe5a79))
14
+
15
+
16
+ ### Documentation
17
+
18
+ * **pi-subagents:** drop removed memory, skills, isolation surfaces ([7cf20ee](https://github.com/gotgenes/pi-packages/commit/7cf20eebf109c979a864000cd8b9d84a13d0df90))
19
+
20
+ ## [16.2.2](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v16.2.1...pi-subagents-v16.2.2) (2026-06-15)
21
+
22
+
23
+ ### Documentation
24
+
25
+ * **pi-subagents:** replace fork notice with upstream comparison ([513df4d](https://github.com/gotgenes/pi-packages/commit/513df4d6149178c5c8074cf07d8ad248c50d4c47))
26
+
8
27
  ## [16.2.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v16.2.0...pi-subagents-v16.2.1) (2026-06-15)
9
28
 
10
29
 
package/README.md CHANGED
@@ -6,11 +6,8 @@ A [pi](https://pi.dev) extension that brings **Claude Code-style autonomous sub-
6
6
  Spawn specialized agents that run in isolated sessions — each with its own tools, system prompt, model, and thinking level.
7
7
  Run them in foreground or background, steer them mid-run, resume completed sessions, and define your own custom agent types.
8
8
 
9
- > **Fork notice:** This package is a friendly fork of [`tintinweb/pi-subagents`](https://github.com/tintinweb/pi-subagents), published to npm as `@gotgenes/pi-subagents`.
10
- > It carries a small number of patches on top of upstream — peer-dep migration to `@earendil-works/pi-*`, a post-`bindExtensions` active-tool re-filter, and an `<active_agent>` system-prompt tag for permission resolution.
11
- > See [Deviations from upstream](#deviations-from-upstream) at the bottom of this README for details.
12
- >
13
- > **Status:** Early release.
9
+ > Originally forked from [`tintinweb/pi-subagents`](https://github.com/tintinweb/pi-subagents) by [@tintinweb](https://github.com/tintinweb), now an independently maintained hard fork.
10
+ > See [Comparison with upstream](./docs/comparison-with-upstream.md) for a feature-by-feature comparison and guidance on which to choose.
14
11
 
15
12
  <img width="600" alt="pi-subagents screenshot" src="https://github.com/gotgenes/pi-subagents/raw/main/media/screenshot.png" />
16
13
 
@@ -30,9 +27,6 @@ Run them in foreground or background, steer them mid-run, resume completed sessi
30
27
  Unknown types fall back to general-purpose with a note
31
28
  - **Fuzzy model selection** — specify models by name (`"haiku"`, `"sonnet"`) instead of full IDs, with automatic filtering to only available/configured models
32
29
  - **Context inheritance** — optionally fork the parent conversation into a sub-agent so it knows what's been discussed
33
- - **Persistent agent memory** — three scopes (project, local, user) with automatic read-only fallback for agents without write tools
34
- - **Git worktree isolation** — run agents in isolated repo copies; changes auto-committed to branches on completion
35
- - **Skill preloading** — inject named skills into agent system prompts, discovered from `.pi/skills/`, `.agents/skills/`, and global locations (Pi-standard `<name>/SKILL.md` directory layout supported)
36
30
  - **Styled completion notifications** — background agent results render as themed, compact notification boxes (icon, stats, result preview) instead of raw XML.
37
31
  Expandable to show full output
38
32
  - **Event bus** — lifecycle events (`subagents:created`, `started`, `completed`, `failed`, `steered`, `compacted`) emitted via `pi.events`, enabling other extensions to react to sub-agent activity
@@ -177,21 +171,16 @@ All fields are optional — sensible defaults for everything.
177
171
  | `description` | filename | Agent description shown in tool listings |
178
172
  | `display_name` | — | Display name for UI (e.g. widget, agent list) |
179
173
  | `tools` | all 7 | Comma-separated built-in tools: read, bash, edit, write, grep, find, ls. `none` for no tools |
180
- | `extensions` | `true` | `true` to inherit all MCP/extension tools, `false` to disable |
181
- | `skills` | `true` | Inherit skills from parent. Can be a comma-separated list of skill names to preload (see [Skill Preloading](#skill-preloading) for discovery locations) |
182
- | `memory` | — | Persistent agent memory scope: `project`, `local`, or `user`. Auto-detects read-only agents |
183
- | `isolation` | — | Set to `worktree` to run in an isolated git worktree |
184
174
  | `model` | inherit parent | Model — `provider/modelId` or fuzzy name (`"haiku"`, `"sonnet"`) |
185
175
  | `thinking` | inherit | off, minimal, low, medium, high, xhigh |
186
176
  | `max_turns` | unlimited | Max agentic turns before graceful shutdown. `0` or omit for unlimited |
187
177
  | `prompt_mode` | `append` | `replace`: parent prompt is the cacheable base; body is appended last with full control (no `<sub_agent_context>` bridge, no `<agent_instructions>` wrapper). `append`: parent prompt is the base; body is wrapped in `<agent_instructions>` and a sub-agent context bridge is injected (agent acts as a "parent twin") |
188
178
  | `inherit_context` | `false` | Fork parent conversation into agent |
189
179
  | `run_in_background` | `false` | Run in background by default |
190
- | `isolated` | `false` | No extension/MCP tools, only built-in |
191
180
  | `enabled` | `true` | Set to `false` to disable an agent (useful for hiding a default agent per-project) |
192
181
 
193
182
  Frontmatter is authoritative.
194
- If an agent file sets `model`, `thinking`, `max_turns`, `inherit_context`, `run_in_background`, `isolated`, or `isolation`, those values are locked for that agent.
183
+ If an agent file sets `model`, `thinking`, `max_turns`, `inherit_context`, or `run_in_background`, those values are locked for that agent.
195
184
  `subagent` tool parameters only fill fields the agent config leaves unspecified.
196
185
 
197
186
  ## Tools
@@ -210,8 +199,6 @@ Launch a sub-agent.
210
199
  | `max_turns` | number | no | Max agentic turns. Omit for unlimited (default) |
211
200
  | `run_in_background` | boolean | no | Run without blocking |
212
201
  | `resume` | string | no | Agent ID to resume a previous session |
213
- | `isolated` | boolean | no | No extension/MCP tools |
214
- | `isolation` | `"worktree"` | no | Run in an isolated git worktree |
215
202
  | `inherit_context` | boolean | no | Fork parent conversation into agent |
216
203
 
217
204
  ### `get_subagent_result`
@@ -334,77 +321,16 @@ Agent lifecycle events are emitted via `pi.events.emit()` so other extensions ca
334
321
  `cacheRead` is excluded — each turn's `cacheRead` is the cumulative cached prefix re-read on that one API call, so summing per-message would over-count it.
335
322
  Use `contextUsage.percent` (surfaced as `(NN%)` in the widget) for current context size.
336
323
 
337
- ## Persistent Agent Memory
338
-
339
- Agents can have persistent memory across sessions.
340
- Set `memory` in frontmatter to enable:
341
-
342
- ```yaml
343
- ---
344
- memory: project # project | local | user
345
- ---
346
- ```
347
-
348
- | Scope | Location | Use case |
349
- | --------- | -------------------------------- | ---------------------------------- |
350
- | `project` | `.pi/agent-memory/<name>/` | Shared across the team (committed) |
351
- | `local` | `.pi/agent-memory-local/<name>/` | Machine-specific (gitignored) |
352
- | `user` | `~/.pi/agent-memory/<name>/` | Global personal memory |
353
-
354
- Memory uses a `MEMORY.md` index file and individual memory files with frontmatter.
355
- Agents with write tools get full read-write access.
356
- **Read-only agents** (no `write`/`edit` tools) automatically get read-only memory — they can consume memories written by other agents but cannot modify them.
357
- This prevents unintended tool escalation.
358
-
359
324
  ## Worktree Isolation
360
325
 
361
- Set `isolation: worktree` to run an agent in a temporary git worktree:
362
-
363
- ```text
364
- subagent({ subagent_type: "refactor", prompt: "...", isolation: "worktree" })
365
- ```
366
-
367
- The agent gets a full, isolated copy of the repository.
368
- On completion:
369
-
370
- - **No changes:** worktree is cleaned up automatically
371
- - **Changes made:** changes are committed to a new branch (`pi-agent-<id>`) and returned in the result
372
-
373
- If the worktree cannot be created (not a git repo, no commits, or `git worktree add` fails), the `subagent` tool returns a clear error instead of running unisolated — `isolation: "worktree"` is a strict guarantee, not a hint.
374
- Initialize git and commit at least once, or omit `isolation`.
326
+ Worktree isolation lives in a companion package, not this core.
327
+ Install [`@gotgenes/pi-subagents-worktrees`](https://github.com/gotgenes/pi-packages/tree/main/packages/pi-subagents-worktrees) and list the agent types you want isolated in its `worktreeAgents` config — opted-in agents run in a temporary git worktree, and their changes are saved to a branch on completion.
328
+ The earlier `isolation: "worktree"` spawn flag and `isolation:` frontmatter key were removed from the core.
375
329
 
376
- ## Skill Preloading
330
+ ## Removed: agent memory and skill preloading
377
331
 
378
- Skills can be preloaded by name and injected into the agent's system prompt:
379
-
380
- ```yaml
381
- ---
382
- skills: api-conventions, error-handling
383
- ---
384
- ```
385
-
386
- **Discovery roots** (checked in this order, first match wins):
387
-
388
- | Scope | Path | Source |
389
- | ------- | -------------------------------------------------------------- | ------------------------------------------------------------ |
390
- | Project | `<cwd>/.pi/skills/` | Pi-standard |
391
- | Project | `<cwd>/.agents/skills/` | [Agent Skills spec](https://agentskills.io/integrate-skills) |
392
- | User | `$PI_CODING_AGENT_DIR/skills/` (default `~/.pi/agent/skills/`) | Pi-standard |
393
- | User | `~/.agents/skills/` | [Agent Skills spec](https://agentskills.io/integrate-skills) |
394
- | User | `~/.pi/skills/` | Legacy (pre-Pi) |
395
-
396
- **Per root, a skill named `foo` resolves to the first of:**
397
-
398
- - `<root>/foo.md` — flat file at the top level
399
- - `<root>/foo/SKILL.md` — directory skill (top-level)
400
- - `<root>/*/.../foo/SKILL.md` — directory skill, found by recursive descent
401
-
402
- Recursion skips dotfile directories and `node_modules`.
403
- A directory that itself contains a `SKILL.md` is treated as a single skill — we don't descend into it.
404
- Traversal is byte-order sorted for deterministic resolution across filesystems.
405
-
406
- **Security:** symlinks are rejected at every layer (root, flat file, skill directory, `SKILL.md` inside a skill directory) — intentional deviation from Pi, which follows symlinks.
407
- Skill names with path-traversal characters (`..`, `/`, `\`, spaces, leading dot, >128 chars) are rejected.
332
+ Persistent agent memory (the `memory:` frontmatter key) and skill preloading (the `skills:` frontmatter key) were removed when the core was slimmed down.
333
+ Children now always inherit the parent's skills and extensions, so the `isolated`, `extensions`, and `skills` frontmatter keys no longer exist.
408
334
 
409
335
  ## Migrating from `disallowed_tools`
410
336
 
@@ -436,73 +362,18 @@ When `@gotgenes/pi-permission-system` is not installed, the lifecycle events hav
436
362
 
437
363
  ## Architecture
438
364
 
439
- See `docs/architecture/architecture.md` for the full architecture document with domain decomposition, Mermaid diagrams, and improvement roadmap.
365
+ This extension is a minimal, composable core: it owns agent spawning, execution, and result retrieval, and exposes a typed `SubagentsService` plus lifecycle events that other extensions build on.
440
366
 
441
- ```text
442
- src/
443
- index.ts # Extension entry: tool/command registration, rendering
444
- runtime.ts # Session-scoped state bag with methods
445
- types.ts # Shared type definitions
446
- settings.ts # Persistent settings (concurrency, turn limits)
447
- config/ # Agent type registry and configuration
448
- agent-types.ts # Unified agent registry (defaults + custom)
449
- default-agents.ts # Embedded default agent configs
450
- custom-agents.ts # Load user-defined agents from .pi/agents/*.md
451
- invocation-config.ts # Per-call merge of tool params + agent config
452
- session/ # Pure session assembly
453
- session-config.ts # Session configuration assembler
454
- prompts.ts # Config-driven system prompt builder
455
- context.ts # Parent conversation context for inherit_context
456
- conversation.ts # Render a session's messages as formatted text
457
- content-items.ts # Shared message content parsing
458
- env.ts # Environment detection (git, platform)
459
- model-resolver.ts # Fuzzy model matching
460
- session-dir.ts # Session directory derivation
461
- lifecycle/ # Agent execution and state tracking
462
- agent-manager.ts # Collection manager + observer wiring
463
- agent.ts # Full execution lifecycle (run, abort, steer, workspace)
464
- create-subagent-session.ts # Assembly factory: session creation, binding, tool filtering
465
- subagent-session.ts # Born-complete child session: turn loop, steer, dispose
466
- child-lifecycle.ts # Child-execution lifecycle event publisher
467
- concurrency-queue.ts # Background agent scheduling
468
- parent-snapshot.ts # Immutable spawn-time parent state
469
- turn-limits.ts # Turn-count policy (normalizeMaxTurns)
470
- workspace.ts # Workspace provider seam
471
- usage.ts # Token usage tracking
472
- observation/ # Progress tracking and notification
473
- record-observer.ts # Session-event stats observer
474
- notification.ts # Completion nudges
475
- notification-state.ts # Notification state tracking
476
- renderer.ts # Notification rendering
477
- service/ # Cross-extension API boundary
478
- service.ts # SubagentsService interface + Symbol.for() accessors
479
- service-adapter.ts # SubagentsService wrapper around AgentManager
480
- tools/ # LLM-facing tools
481
- ui/ # Widget, conversation viewer, /agents menu
482
- ```
483
-
484
- ## Deviations from upstream
367
+ See [`docs/architecture/architecture.md`](./docs/architecture/architecture.md) for the full architecture document — design principles, domain decomposition, module dependency flow, Mermaid diagrams, and the improvement roadmap.
485
368
 
486
- This fork carries three divergences from [`tintinweb/pi-subagents`](https://github.com/tintinweb/pi-subagents).
487
- Each has a corresponding upstream PR:
369
+ ## Relationship to upstream
488
370
 
489
- 1. **Peer-dep migration to `@earendil-works/pi-*`** `peerDependencies` and all imports point at `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, and `@earendil-works/pi-tui` (the active scope on npm) instead of the deprecated `@mariozechner/pi-*` scope.
490
- Also fixes a latent bug where `ThinkingLevel` was imported from `pi-agent-core` (an undeclared transitive dep that breaks under pnpm).
491
- Upstream PR: [tintinweb/pi-subagents#71](https://github.com/tintinweb/pi-subagents/pull/71).
492
- 2. **Post-`bindExtensions` active-tool re-filter** (`src/agent-runner.ts`) — `runAgent` re-runs its active-tool filter after `session.bindExtensions(...)` so the `EXCLUDED_TOOL_NAMES` recursion guard applies to extension-registered tools (which join the active set during `bindExtensions`).
493
- Upstream PR: [tintinweb/pi-subagents#72](https://github.com/tintinweb/pi-subagents/pull/72).
494
- 3. **`<active_agent>` system-prompt tag** (`src/prompts.ts`) — `buildAgentPrompt` includes `<active_agent name="${config.name}"/>` in every assembled child system prompt (both `replace` and `append` modes); the tag follows the cacheable parent-prompt prefix in both modes.
495
- Downstream extensions like [`@gotgenes/pi-permission-system`](https://github.com/gotgenes/pi-permission-system) parse this tag to resolve per-agent `permission:` frontmatter inside the child session.
496
- Upstream PR: [tintinweb/pi-subagents#73](https://github.com/tintinweb/pi-subagents/pull/73).
497
- 4. **Child-execution lifecycle events** (`src/lifecycle/child-lifecycle.ts`) — the child-session execution lifecycle is published as ordered events on `pi.events` (`subagents:child:spawning`, `session-created`, `completed`, `disposed`).
498
- `session-created` fires synchronously before `bindExtensions()` so consumers (e.g. `@gotgenes/pi-permission-system`) can register the child session before binding proceeds.
499
- This inverts the former outbound `permission-bridge` pattern ([ADR-0002] / [#261]) — the core publishes, consumers subscribe.
500
- No upstream equivalent — this feature is specific to the `@gotgenes` fork.
371
+ This package is an independently maintained hard fork of [`tintinweb/pi-subagents`](https://github.com/tintinweb/pi-subagents) by [@tintinweb](https://github.com/tintinweb).
372
+ It has diverged substantially in scope and architecture: a minimal core with a typed service API and lifecycle events, with tool-restriction policy and worktree isolation delegated to companion packages.
373
+ Upstream remains the batteries-included option, keeping scheduling, cross-extension RPC, model-scope enforcement, and a built-in tool denylist in a single package.
501
374
 
502
- The upstream `vitest` suite plus tests added for each patch all pass on every commit.
375
+ See [Comparison with upstream](./docs/comparison-with-upstream.md) for a full feature-by-feature comparison against the current upstream release and guidance on which to choose.
503
376
 
504
377
  ## License
505
378
 
506
379
  MIT — [tintinweb](https://github.com/tintinweb) (upstream) and [Chris Lasher](https://github.com/gotgenes) (fork)
507
-
508
- [ADR-0002]: docs/decisions/0002-extensions-on-a-minimal-core.md
@@ -646,7 +646,7 @@ That method — testability friction as a boundary probe, with its limits — is
646
646
  | Metric | Value |
647
647
  | -------------------------- | --------------------------------------- |
648
648
  | Health score | 78/100 (B) |
649
- | Total LOC | 8,356 (60 files, as of Phase 17 Step 4) |
649
+ | Total LOC | 8,356 (61 files, as of Phase 17 Step 5) |
650
650
  | Dead code | 0 files, 0 exports |
651
651
  | Maintainability index | 90.8 (good) |
652
652
  | Avg cyclomatic complexity | 1.4 |
@@ -893,7 +893,7 @@ Updated health metrics (fallow, package-wide including tests):
893
893
  | Metric | Phase 16 baseline | Current |
894
894
  | -------------------------- | ------------------------------ | --------------------------------------------- |
895
895
  | Health score | 78/100 (B) | 78/100 (B) |
896
- | Source LOC | 7,778 (57 files) | 8,356 (60 files, landed Phase 17 Step 4) |
896
+ | Source LOC | 7,778 (57 files) | 8,356 (61 files, landed Phase 17 Step 5) |
897
897
  | Dead code | 0 files, 0 exports | 0 files, 0 exports |
898
898
  | Maintainability index | 90.8 (good) | 90.8 (good) |
899
899
  | Avg / P90 cyclomatic | 1.4 / 2 | 1.4 / 2 |
@@ -970,7 +970,7 @@ Priority = Impact × (6 − Risk).
970
970
  Fixed by inverting control: `scheduleVia` captures the limiter promise eagerly inside the agent (no external `.promise =` write), restoring the invariant.
971
971
  Lesson: a step's acceptance criteria must include the cross-step invariants it could regress, not only its own grep-verifiable outcome.
972
972
 
973
- #### Step 4 — Extract run-listener and workspace-bracket collaborators from Subagent ([#375])
973
+ #### Step 4 — Extract run-listener and workspace-bracket collaborators from Subagent ([#375]) ✅ Complete
974
974
 
975
975
  - Targets: `src/lifecycle/subagent.ts` (455 LOC after Step 2 extracted SubagentState — still the largest source file).
976
976
  - Smell: Category B (oversized class; per-run listener fields declared mid-class) and Category C (state owns its mutations: workspace dispose logic appears in `run()`'s catch, `completeRun`, and `failRun`).
@@ -981,19 +981,26 @@ Priority = Impact × (6 − Risk).
981
981
  `subagent.ts`: 488 → 448 LOC.
982
982
  Test count: 982 → 994 (+12: 7 RunListeners + 13 WorkspaceBracket − 8 redundant Subagent listener tests).
983
983
 
984
- #### Step 5 — Extract the manager observer from index.ts into a class ([#376])
984
+ #### Step 5 — Extract the manager observer from index.ts into a class ([#376]) ✅ Complete
985
985
 
986
986
  - Targets: `src/index.ts` (inline `SubagentManagerObserver` literal, ~70 lines), new module under `src/observation/`.
987
987
  - Smell: Category B/E — `index.ts` is the dominant churn hotspot (31.3, 91 commits); the literal mixes event emission, record persistence (`appendEntry`), and notification dispatch; principle 9 (state and behavior belong in classes, not closure-captured literals).
988
988
  - Change: extract a class (e.g. `SubagentEventsObserver`) constructed with narrow deps (`emit`, `appendEntry`, the `NotificationSystem`).
989
989
  - Outcome: `index.ts` < 170 lines; the observer's three concerns unit-tested directly without booting the extension.
990
+ - Landed: `src/observation/subagent-events-observer.ts` (new, 97 LOC); `index.ts` 226 → 177 lines; 60 → 61 source files; 994 → 1009 tests (+15 covering all four observer methods).
990
991
 
991
- #### Step 6 — Split widget delegation out of SubagentRuntime ([#377])
992
+ #### Step 6 — Split widget delegation out of SubagentRuntime ([#377]) ✅ Complete
992
993
 
993
- - Targets: `src/runtime.ts`, `src/tools/agent-tool.ts` (`AgentToolRuntime`), `src/tools/foreground-runner.ts`, `src/tools/background-spawner.ts`, `src/observation/notification.ts` (`NotificationManager` constructor), `src/index.ts`.
994
+ - Targets: `src/runtime.ts`, `src/tools/agent-tool.ts` (`AgentToolRuntime`), `src/handlers/tool-start.ts` (`ToolStartRuntime`), `src/observation/notification.ts` (`NotificationManager` constructor), `src/ui/agent-widget.ts`, `src/index.ts`.
994
995
  - Smell: Category C — relay-only dependency (five delegation methods that only forward to `widget`) and a post-construction `runtime.widget =` write violating principle 8.
995
- - Change: pass the existing `WidgetLike` handle directly to the consumers that need it (tool deps, `NotificationManager`) and construct the widget before them; remove the `widget` field and the five relay methods from `SubagentRuntime`.
996
- - Outcome: `SubagentRuntime` has zero widget knowledge; no post-construction field writes in `index.ts`; tool fixtures stub a 5-method `WidgetLike` instead of widget methods on the runtime mock.
996
+ - Constraint discovered in planning: "pass the handle directly to the consumers" is infeasible for `NotificationManager`, which is a _transitive dependency_ of the widget (`NotificationManager widget manager observer NotificationManager`).
997
+ The `runtime.widget` lazy field exists to break exactly this cycle; removing it forces the one late seam to move, and the operator's principles (no setters, instantiate ready-to-work) rule out relocating it to a setter or forward-ref `let`.
998
+ - Change (tidy-first): first dissolve the cycle by giving `AgentWidget.update()` self-seeding of `finishedTurnAge` for finished agents it observes via `listAgents()`, then drop the `markFinished`/`updateWidget` callbacks from `NotificationManager` (it keeps `agentActivity.delete` + nudge scheduling).
999
+ With the cycle gone, the widget is constructed as a `const` after the manager and injected directly into `AgentTool` and `ToolStartHandler`; the `widget` field, the five relay methods, and the post-construction write delete cleanly, and `AgentToolRuntime` narrows to its context-query slice.
1000
+ - Outcome: `SubagentRuntime` has zero widget knowledge; no post-construction field writes in `index.ts`; the construction graph is a cycle-free DAG (`notifications → observer → manager → widget → {AgentTool, ToolStartHandler}`).
1001
+ - Landed: `AgentWidget.seedFinishedAgents` owns completion detection; `NotificationManager` has no widget dependency (2-arg constructor); `AgentToolWidget` (in `agent-tool.ts`) and `ToolStartWidget` (in `tool-start.ts`) are narrow per-consumer interfaces the real widget satisfies structurally; tool fixtures stub a `widget` separate from the narrowed `runtime` mock.
1002
+ Behavior-preserving: the widget timer runs through every background completion, so self-seeding lands ≤80ms later within the same turn (linger is turn-based).
1003
+ Test count: 1009 → 1005 (+3 widget self-seed tests, −7 removed relay/field tests).
997
1004
 
998
1005
  #### Step 7 — Consolidate lifecycle test fixtures ([#378])
999
1006
 
@@ -0,0 +1,77 @@
1
+ # Comparison with upstream
2
+
3
+ `@gotgenes/pi-subagents` began as a fork of [`tintinweb/pi-subagents`](https://github.com/tintinweb/pi-subagents) by [@tintinweb](https://github.com/tintinweb).
4
+ The original design — Claude Code-style subagent dispatch, the live widget, the conversation viewer, custom agent types — is the foundation everything here builds on.
5
+
6
+ It has since become an independently maintained hard fork.
7
+ It follows its own architecture, does not track upstream as a merge target, and cherry-picks upstream fixes only when they fit its scope.
8
+ This document compares the fork against the current upstream release so you can choose between them.
9
+
10
+ Versions compared: `@gotgenes/pi-subagents` 16.2.1 and `@tintinweb/pi-subagents` 0.10.3 (current at the time of writing).
11
+
12
+ ## At a glance
13
+
14
+ | Aspect | @gotgenes/pi-subagents | @tintinweb/pi-subagents |
15
+ | --------------- | ------------------------------- | --------------------------------------- |
16
+ | Philosophy | Minimal, composable core | Batteries-included, all-in-one |
17
+ | Pi peer scope | `@earendil-works/pi-*` (>=0.75) | `@earendil-works/pi-*` (>=0.74) |
18
+ | Spawn tool name | `subagent` | `Agent` |
19
+ | Runtime deps | `@sinclair/typebox` | `@sinclair/typebox`, `croner`, `nanoid` |
20
+ | License | MIT | MIT |
21
+
22
+ Both ship TypeScript source directly (Pi runs `./src/index.ts`) and target the same `@earendil-works/pi-*` Pi.
23
+ The peer-dep migration that prompted the original fork has since landed upstream, so the Pi scope is no longer a differentiator.
24
+
25
+ ## Common ground
26
+
27
+ Both extensions provide the same core experience:
28
+
29
+ - Claude Code-style foreground/background subagents with a live above-editor widget and a conversation viewer.
30
+ - Custom agent types defined in `.pi/agents/<name>.md` with YAML frontmatter (system prompt, model, thinking, tools).
31
+ - Fuzzy model selection, context inheritance, mid-run steering, session resume, and graceful turn limits.
32
+ - A `pi.events` lifecycle bus (`subagents:created`, `started`, `completed`, `failed`, `steered`, `compacted`).
33
+
34
+ ## What upstream has that this fork does not
35
+
36
+ Upstream is the batteries-included option.
37
+ It keeps several subsystems built in that this fork deliberately removed or delegated:
38
+
39
+ | Capability | @tintinweb/pi-subagents | @gotgenes/pi-subagents |
40
+ | ----------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
41
+ | Tool restrictions | `disallowed_tools` frontmatter (denylist) | Delegated — `permission:` via [`@gotgenes/pi-permission-system`](https://github.com/gotgenes/pi-packages/tree/main/packages/pi-permission-system) |
42
+ | Worktree isolation | Built-in | Delegated — [`@gotgenes/pi-subagents-worktrees`](https://github.com/gotgenes/pi-packages/tree/main/packages/pi-subagents-worktrees) |
43
+ | Persistent agent memory | `memory:` frontmatter (project / local / user) | Removed |
44
+ | Skill preloading | `skills:` frontmatter (preload named skills) | Removed — children always inherit the parent's skills |
45
+ | Scheduling | Cron / interval / one-shot subagents (`schedule`) | Removed |
46
+ | Cross-extension control | `subagents:rpc:*` event RPC | Replaced by a typed service (below) |
47
+ | Model-scope enforcement | `enabledModels` allowlist validation | Not included |
48
+ | Notifications | Smart group-join consolidation | Individual per-agent notifications |
49
+
50
+ ## What this fork adds
51
+
52
+ This fork is a minimal core that other extensions build on, plus a small companion ecosystem:
53
+
54
+ - **Typed service API** — `SubagentsService` exposed via `Symbol.for()` accessors, so another extension can spawn and manage subagents without importing this package or relying on ad-hoc event RPC.
55
+ - **Child-session lifecycle events** — `subagents:child:spawning` / `session-created` / `completed` / `disposed`, with `session-created` firing synchronously before `bindExtensions()` so consumers can register the child session deterministically.
56
+ - **`<active_agent>` system-prompt tag** — lets [`@gotgenes/pi-permission-system`](https://github.com/gotgenes/pi-packages/tree/main/packages/pi-permission-system) resolve per-agent `permission:` frontmatter (allow / ask / deny — richer than a binary denylist) inside the child session.
57
+ - **Companion packages** — permission policy and worktree isolation live in dedicated packages rather than the core.
58
+ - **Re-architected codebase** — decomposed into seven domains behind a typed public API boundary, backed by ~994 tests.
59
+
60
+ ## Which should I use?
61
+
62
+ **Use `@tintinweb/pi-subagents`** if you want a single, batteries-included extension with nothing else to install: built-in tool denylist, scheduled / cron subagents, cross-extension RPC, and model-scope enforcement in one package.
63
+ It is the canonical upstream and the original.
64
+
65
+ **Use `@gotgenes/pi-subagents`** if you want a minimal, composable core: richer allow / ask / deny permissions and worktree isolation through companion packages, a typed service plus lifecycle events to build your own extensions on, and an actively refactored codebase — and you do not need built-in scheduling, RPC, or model-scope enforcement.
66
+
67
+ The spawn tool is named `subagent` here versus `Agent` upstream, so prompts and docs that hard-code the tool name are not drop-in portable between the two.
68
+
69
+ ## Patches contributed upstream
70
+
71
+ Three of the fork's early changes were opened as PRs against upstream and remain a record of the shared lineage:
72
+
73
+ 1. Peer-dep migration to `@earendil-works/pi-*` — [tintinweb/pi-subagents#71](https://github.com/tintinweb/pi-subagents/pull/71) (upstream has since migrated).
74
+ 2. Post-`bindExtensions` active-tool re-filter — [tintinweb/pi-subagents#72](https://github.com/tintinweb/pi-subagents/pull/72).
75
+ 3. `<active_agent>` system-prompt tag — [tintinweb/pi-subagents#73](https://github.com/tintinweb/pi-subagents/pull/73).
76
+
77
+ The fork has since diverged well beyond these.