@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 +19 -0
- package/README.md +16 -145
- package/docs/architecture/architecture.md +15 -8
- package/docs/comparison-with-upstream.md +77 -0
- package/docs/plans/0376-extract-manager-observer.md +232 -0
- package/docs/plans/0377-split-widget-delegation-from-runtime.md +254 -0
- package/docs/retro/0375-extract-run-listener-workspace-bracket.md +53 -0
- package/docs/retro/0376-extract-manager-observer.md +81 -0
- package/docs/retro/0377-split-widget-delegation-from-runtime.md +49 -0
- package/package.json +1 -1
- package/src/handlers/tool-start.ts +5 -5
- package/src/index.ts +16 -65
- package/src/observation/notification.ts +0 -6
- package/src/observation/subagent-events-observer.ts +97 -0
- package/src/runtime.ts +0 -45
- package/src/tools/agent-tool.ts +15 -6
- package/src/ui/agent-widget.ts +16 -2
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
|
-
>
|
|
10
|
-
>
|
|
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`,
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
-
##
|
|
330
|
+
## Removed: agent memory and skill preloading
|
|
377
331
|
|
|
378
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
487
|
-
Each has a corresponding upstream PR:
|
|
369
|
+
## Relationship to upstream
|
|
488
370
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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/
|
|
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
|
-
-
|
|
996
|
-
|
|
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.
|