@gotgenes/pi-subagents 7.8.1 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/README.md +11 -15
- package/docs/architecture/architecture.md +179 -133
- package/docs/architecture/history/phase-13-remaining-smells.md +88 -0
- package/docs/plans/0237-remove-disallowed-tools.md +180 -0
- package/docs/plans/0238-remove-extensions-filtering.md +191 -0
- package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +28 -0
- package/docs/retro/0237-remove-disallowed-tools.md +78 -0
- package/docs/retro/0238-remove-extensions-filtering.md +39 -0
- package/package.json +1 -1
- package/src/config/custom-agents.ts +13 -6
- package/src/lifecycle/agent-runner.ts +11 -19
- package/src/session/session-config.ts +5 -12
- package/src/types.ts +2 -4
- package/src/ui/agent-config-editor.ts +1 -5
- package/src/ui/agent-creation-wizard.ts +1 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,52 @@ 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
|
+
## [9.0.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v8.0.0...pi-subagents-v9.0.0) (2026-05-27)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### ⚠ BREAKING CHANGES
|
|
12
|
+
|
|
13
|
+
* extensions field in agent frontmatter no longer supports a comma-separated allowlist. Values like `extensions: pi-github-tools, colgrep` are coerced to `true` (inherit all). Users who relied on per-extension filtering should migrate to `permission:` frontmatter in pi-permission-system.
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* narrow extensions type from union to boolean ([90350ab](https://github.com/gotgenes/pi-packages/commit/90350abef259f5494990c5796a005f2edba2163d))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Documentation
|
|
21
|
+
|
|
22
|
+
* mark Phase 14 Step 2 complete in architecture ([fc8bc57](https://github.com/gotgenes/pi-packages/commit/fc8bc57788a3ade9ec2f74200973e5a811c48bb3))
|
|
23
|
+
* plan remove extensions filtering ([#238](https://github.com/gotgenes/pi-packages/issues/238)) ([1f4046a](https://github.com/gotgenes/pi-packages/commit/1f4046a84a308e80472425b5ca9e83263ecc310a))
|
|
24
|
+
* **retro:** add planning stage notes for issue [#238](https://github.com/gotgenes/pi-packages/issues/238) ([b2ce842](https://github.com/gotgenes/pi-packages/commit/b2ce842dcc999fd48f6aeb67c6d464750d6d87aa))
|
|
25
|
+
* **retro:** add retro notes for issue [#237](https://github.com/gotgenes/pi-packages/issues/237) ([6be215e](https://github.com/gotgenes/pi-packages/commit/6be215e6f4d9307f94b5bb61c024292a8eb77469))
|
|
26
|
+
* **retro:** add TDD stage notes for issue [#238](https://github.com/gotgenes/pi-packages/issues/238) ([87256db](https://github.com/gotgenes/pi-packages/commit/87256dbd97ba4a69267f2091896d959bef9ff9df))
|
|
27
|
+
* simplify extensions field to boolean in README ([b4028d7](https://github.com/gotgenes/pi-packages/commit/b4028d7c203fce792238acb147709ad3c3d4d28e))
|
|
28
|
+
|
|
29
|
+
## [8.0.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v7.8.1...pi-subagents-v8.0.0) (2026-05-27)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### ⚠ BREAKING CHANGES
|
|
33
|
+
|
|
34
|
+
* The `disallowed_tools` frontmatter field is no longer supported. Users should migrate to pi-permission-system's `permission:`
|
|
35
|
+
|
|
36
|
+
### Features
|
|
37
|
+
|
|
38
|
+
* remove disallowedTools from AgentConfig ([6044552](https://github.com/gotgenes/pi-packages/commit/6044552db68180bf8a151c0f64c13f264f3dacb9))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Documentation
|
|
42
|
+
|
|
43
|
+
* add Phase 14 issue links, anemic-model and lifecycle-object smells to discovery skill ([9831176](https://github.com/gotgenes/pi-packages/commit/983117652937dce08e61a6cf7624e35ccf8a0dcb))
|
|
44
|
+
* mark Phase 14 Step 1 complete in architecture ([d24e3dd](https://github.com/gotgenes/pi-packages/commit/d24e3dd5c664e63a372f399f9e325f1bdf877022))
|
|
45
|
+
* **pi-subagents:** add Phase 14 issue links ([#237](https://github.com/gotgenes/pi-packages/issues/237), [#238](https://github.com/gotgenes/pi-packages/issues/238), [#239](https://github.com/gotgenes/pi-packages/issues/239)) ([b152a75](https://github.com/gotgenes/pi-packages/commit/b152a75dcd6596468c5c0aeca1b36ac33216dfa8))
|
|
46
|
+
* **pi-subagents:** add target architecture vision and renumber phases 14-17 ([f7e5315](https://github.com/gotgenes/pi-packages/commit/f7e5315bbfb088d7a40637deead1811691dec1bd))
|
|
47
|
+
* **pi-subagents:** archive Phase 13, update metrics for Phase 14 ([9d473db](https://github.com/gotgenes/pi-packages/commit/9d473db1b0e420fa52ce9d09bea7afff75331e58))
|
|
48
|
+
* plan removal of disallowed_tools ([#237](https://github.com/gotgenes/pi-packages/issues/237)) ([9cb600c](https://github.com/gotgenes/pi-packages/commit/9cb600cad9c8cc7b1081b021f039b0f36b9f9a44))
|
|
49
|
+
* remove disallowed_tools from README and add migration note ([9b3905f](https://github.com/gotgenes/pi-packages/commit/9b3905fbaa81db47633c0c8edf21ed3b766d5507))
|
|
50
|
+
* **retro:** add planning stage notes for issue [#237](https://github.com/gotgenes/pi-packages/issues/237) ([85460dd](https://github.com/gotgenes/pi-packages/commit/85460dd72949d7da83eaf9ed2196ff5c41d92f7f))
|
|
51
|
+
* **retro:** add retro notes for issue [#219](https://github.com/gotgenes/pi-packages/issues/219) ([2d92af5](https://github.com/gotgenes/pi-packages/commit/2d92af577c0cfb71c575a5c334df2347840b4a8d))
|
|
52
|
+
* **retro:** add TDD stage notes for issue [#237](https://github.com/gotgenes/pi-packages/issues/237) ([f334538](https://github.com/gotgenes/pi-packages/commit/f3345383a76975f09c0ee689edcb5df286b76fc7))
|
|
53
|
+
|
|
8
54
|
## [7.8.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v7.8.0...pi-subagents-v7.8.1) (2026-05-26)
|
|
9
55
|
|
|
10
56
|
|
package/README.md
CHANGED
|
@@ -31,7 +31,6 @@ Run them in foreground or background, steer them mid-run, resume completed sessi
|
|
|
31
31
|
- **Persistent agent memory** — three scopes (project, local, user) with automatic read-only fallback for agents without write tools
|
|
32
32
|
- **Git worktree isolation** — run agents in isolated repo copies; changes auto-committed to branches on completion
|
|
33
33
|
- **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)
|
|
34
|
-
- **Tool denylist** — block specific tools via `disallowed_tools` frontmatter
|
|
35
34
|
- **Styled completion notifications** — background agent results render as themed, compact notification boxes (icon, stats, result preview) instead of raw XML.
|
|
36
35
|
Expandable to show full output
|
|
37
36
|
- **Event bus** — lifecycle events (`subagents:created`, `started`, `completed`, `failed`, `steered`, `compacted`) emitted via `pi.events`, enabling other extensions to react to sub-agent activity
|
|
@@ -176,10 +175,9 @@ All fields are optional — sensible defaults for everything.
|
|
|
176
175
|
| `description` | filename | Agent description shown in tool listings |
|
|
177
176
|
| `display_name` | — | Display name for UI (e.g. widget, agent list) |
|
|
178
177
|
| `tools` | all 7 | Comma-separated built-in tools: read, bash, edit, write, grep, find, ls. `none` for no tools |
|
|
179
|
-
| `extensions` | `true` |
|
|
178
|
+
| `extensions` | `true` | `true` to inherit all MCP/extension tools, `false` to disable |
|
|
180
179
|
| `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) |
|
|
181
180
|
| `memory` | — | Persistent agent memory scope: `project`, `local`, or `user`. Auto-detects read-only agents |
|
|
182
|
-
| `disallowed_tools` | — | Comma-separated tools to deny even if extensions provide them |
|
|
183
181
|
| `isolation` | — | Set to `worktree` to run in an isolated git worktree |
|
|
184
182
|
| `model` | inherit parent | Model — `provider/modelId` or fuzzy name (`"haiku"`, `"sonnet"`) |
|
|
185
183
|
| `thinking` | inherit | off, minimal, low, medium, high, xhigh |
|
|
@@ -356,8 +354,6 @@ Agents with write tools get full read-write access.
|
|
|
356
354
|
**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
355
|
This prevents unintended tool escalation.
|
|
358
356
|
|
|
359
|
-
The `disallowed_tools` field is respected when determining write capability — an agent with `tools: write` + `disallowed_tools: write` correctly gets read-only memory.
|
|
360
|
-
|
|
361
357
|
## Worktree Isolation
|
|
362
358
|
|
|
363
359
|
Set `isolation: worktree` to run an agent in a temporary git worktree:
|
|
@@ -408,18 +404,19 @@ Traversal is byte-order sorted for deterministic resolution across filesystems.
|
|
|
408
404
|
**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.
|
|
409
405
|
Skill names with path-traversal characters (`..`, `/`, `\`, spaces, leading dot, >128 chars) are rejected.
|
|
410
406
|
|
|
411
|
-
##
|
|
407
|
+
## Migrating from `disallowed_tools`
|
|
412
408
|
|
|
413
|
-
|
|
409
|
+
The `disallowed_tools` frontmatter field has been removed.
|
|
410
|
+
Use [`@gotgenes/pi-permission-system`](https://github.com/gotgenes/pi-permission-system)'s `permission:` frontmatter instead — it provides richer semantics (allow/ask/deny vs. binary hide):
|
|
414
411
|
|
|
415
412
|
```yaml
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
disallowed_tools: write, edit
|
|
419
|
-
---
|
|
420
|
-
```
|
|
413
|
+
# Before (no longer supported)
|
|
414
|
+
disallowed_tools: bash
|
|
421
415
|
|
|
422
|
-
|
|
416
|
+
# After
|
|
417
|
+
permission:
|
|
418
|
+
bash: deny
|
|
419
|
+
```
|
|
423
420
|
|
|
424
421
|
## Permission System Integration
|
|
425
422
|
|
|
@@ -482,8 +479,7 @@ Each has a corresponding upstream PR:
|
|
|
482
479
|
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.
|
|
483
480
|
Also fixes a latent bug where `ThinkingLevel` was imported from `pi-agent-core` (an undeclared transitive dep that breaks under pnpm).
|
|
484
481
|
Upstream PR: [tintinweb/pi-subagents#71](https://github.com/tintinweb/pi-subagents/pull/71).
|
|
485
|
-
2. **Post-`bindExtensions` active-tool re-filter** (`src/agent-runner.ts`) — `runAgent` re-runs its active-tool filter after `session.bindExtensions(...)` so extension-registered tools join the
|
|
486
|
-
Without this, the `extensions: string[]` allowlist branch was functionally dead for extension tools, and `extensions: true` with a `disallowedTools` denylist let denylisted extension tools slip through.
|
|
482
|
+
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`).
|
|
487
483
|
Upstream PR: [tintinweb/pi-subagents#72](https://github.com/tintinweb/pi-subagents/pull/72).
|
|
488
484
|
3. **`<active_agent>` system-prompt tag** (`src/prompts.ts`) — `buildAgentPrompt` prepends `<active_agent name="${config.name}"/>` to every assembled child system prompt (both `replace` and `append` modes).
|
|
489
485
|
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.
|
|
@@ -22,6 +22,9 @@ This document describes the architecture of the pi-subagents fork: a focused, co
|
|
|
22
22
|
No post-construction field writes from external code — if an object can't be instantiated ready-to-go, the prep work hasn't been done and the right dependencies haven't been identified.
|
|
23
23
|
9. **State owns its mutations** — mutable state lives in a class whose methods enforce valid transitions and invariants.
|
|
24
24
|
Free functions that mutate module-scoped variables, closure-captured bags-of-functions, and external writes to shared interfaces are replaced by classes that encapsulate the state they manage.
|
|
25
|
+
10. **Open for extension, closed for modification** — pi-subagents is a minimal core that publishes events and a service API.
|
|
26
|
+
Other packages (pi-permission-system, a future UI extension, hypothetical OTel integration) hook into these events to add permissions, rendering, or telemetry.
|
|
27
|
+
Pi-subagents has zero knowledge of its consumers — dependency arrows point inward, never outward.
|
|
25
28
|
|
|
26
29
|
## Domain model
|
|
27
30
|
|
|
@@ -222,7 +225,7 @@ sequenceDiagram
|
|
|
222
225
|
|
|
223
226
|
## Module organization
|
|
224
227
|
|
|
225
|
-
The extension has
|
|
228
|
+
The extension has 56 source files organized into six domains plus entry-point wiring.
|
|
226
229
|
All eight domains have directories: `config/`, `session/`, `lifecycle/`, `observation/`, `service/`, `tools/`, `ui/`, and `handlers/`.
|
|
227
230
|
Issue #164 moved the 26 previously flat root-level files into five new domain directories, reducing the root to 5 files + 8 directories.
|
|
228
231
|
|
|
@@ -260,6 +263,7 @@ src/
|
|
|
260
263
|
│ ├── parent-snapshot.ts immutable spawn-time parent state
|
|
261
264
|
│ ├── execution-state.ts session/output phase state
|
|
262
265
|
│ ├── permission-bridge.ts optional bridge to pi-permission-system registry
|
|
266
|
+
│ ├── run-handle.ts per-run cleanup lifecycle
|
|
263
267
|
│ ├── worktree.ts git worktree isolation
|
|
264
268
|
│ ├── worktree-state.ts worktree phase state
|
|
265
269
|
│ └── usage.ts token usage tracking
|
|
@@ -272,7 +276,7 @@ src/
|
|
|
272
276
|
│
|
|
273
277
|
├── service/ cross-extension API boundary
|
|
274
278
|
│ ├── service.ts SubagentsService interface + Symbol.for() accessors
|
|
275
|
-
│ └── service-adapter.ts
|
|
279
|
+
│ └── service-adapter.ts SubagentsServiceAdapter class wrapping AgentManager
|
|
276
280
|
│
|
|
277
281
|
├── tools/ LLM-facing tool implementations
|
|
278
282
|
│ ├── agent-tool.ts Agent tool definition, validation, dispatch
|
|
@@ -288,8 +292,8 @@ src/
|
|
|
288
292
|
│ ├── agent-widget.ts above-editor live status widget
|
|
289
293
|
│ ├── widget-renderer.ts pure rendering for widget
|
|
290
294
|
│ ├── agent-menu.ts /agents slash command menu
|
|
291
|
-
│ ├── agent-config-editor.ts agent detail/edit view
|
|
292
|
-
│ ├── agent-creation-wizard.ts agent creation (
|
|
295
|
+
│ ├── agent-config-editor.ts agent detail/edit view (AgentConfigEditor class)
|
|
296
|
+
│ ├── agent-creation-wizard.ts agent creation (AgentCreationWizard class)
|
|
293
297
|
│ ├── conversation-viewer.ts scrollable session overlay
|
|
294
298
|
│ ├── message-formatters.ts pure per-message-type formatters (extracted from conversation-viewer)
|
|
295
299
|
│ ├── agent-activity-tracker.ts live activity state tracker
|
|
@@ -335,8 +339,9 @@ They declare this package as an optional peer dependency and use dynamic import
|
|
|
335
339
|
|
|
336
340
|
- The three tools: `Agent`, `get_subagent_result`, `steer_subagent`.
|
|
337
341
|
- `AgentManager` — spawn, queue, abort, resume, concurrency control.
|
|
338
|
-
- `agent-runner` — session creation, turn loop,
|
|
342
|
+
- `agent-runner` — session creation, turn loop, extension binding.
|
|
339
343
|
- `permission-bridge` — optional cross-extension bridge to `@gotgenes/pi-permission-system`; registers each child session with `SubagentSessionRegistry` before `bindExtensions()` so the permission system detects in-process children deterministically.
|
|
344
|
+
Scheduled for removal in Phase 16 — replaced by lifecycle events that consumers listen for.
|
|
340
345
|
- `session-config` — pure configuration assembler (extracted from `agent-runner`).
|
|
341
346
|
- `SubagentRuntime` — session-scoped state bag with methods.
|
|
342
347
|
- `ParentSnapshot` — immutable snapshot of parent session state, captured once at spawn time.
|
|
@@ -430,22 +435,56 @@ The core emits events on `pi.events` that any extension can observe:
|
|
|
430
435
|
|
|
431
436
|
These are fire-and-forget broadcast events — no request IDs, no reply channels.
|
|
432
437
|
|
|
438
|
+
## Target architecture
|
|
439
|
+
|
|
440
|
+
The long-term architectural direction is to make pi-subagents a **minimal core** with inverted dependencies.
|
|
441
|
+
Today, pi-subagents reaches outward to pi-permission-system via a bridge module and owns tool/extension filtering logic that duplicates permission-system responsibilities.
|
|
442
|
+
The target state eliminates this overlap and flips the dependency direction.
|
|
443
|
+
|
|
444
|
+
### Core responsibilities (keep)
|
|
445
|
+
|
|
446
|
+
- **Agent definitions** — name, model, thinking, system prompt, tools list.
|
|
447
|
+
- **Prompt composition** — system prompt assembly, skill preloading into prompt.
|
|
448
|
+
- **Session lifecycle** — create child sessions, bind extensions, run conversation loop, track results.
|
|
449
|
+
- **Concurrency management** — queue, abort, resume, max concurrency.
|
|
450
|
+
- **Recursion guard** — remove pi-subagents' own three tools from child sessions (prevent infinite nesting).
|
|
451
|
+
- **Lifecycle events** — emit events on `pi.events` when child sessions are created, completed, etc.
|
|
452
|
+
- **Service API** — publish `SubagentsService` via `Symbol.for()` for cross-extension access.
|
|
453
|
+
|
|
454
|
+
### Responsibilities to remove
|
|
455
|
+
|
|
456
|
+
- **Tool policy** (`disallowed_tools`, `ToolFilterConfig.disallowedSet`) — access control belongs in pi-permission-system's `permission:` frontmatter.
|
|
457
|
+
- **Extension filtering** (`extensions: string[]` allowlist) — tool visibility is pi-permission-system's job.
|
|
458
|
+
- **Permission bridge** (`permission-bridge.ts`) — outbound coupling to pi-permission-system.
|
|
459
|
+
Replaced by lifecycle events that pi-permission-system listens for.
|
|
460
|
+
- **Extension lifecycle control** (`extensions: false`, `isolated`) — extensions provide behavioral layers (permissions, formatting, context management) that benefit all agents.
|
|
461
|
+
Blanket-disabling them is a blunt instrument with no clear use case; tool restrictions belong in the permission system.
|
|
462
|
+
|
|
463
|
+
### Composition model
|
|
464
|
+
|
|
465
|
+
In the target state, pi-subagents publishes events and other packages hook in:
|
|
466
|
+
|
|
467
|
+
- **pi-permission-system** listens for child session lifecycle events, applies per-agent policy (allow/ask/deny), gates tool calls at runtime.
|
|
468
|
+
- **pi-subagents-ui** (future) subscribes to the service API, renders the widget, conversation viewer, and `/agents` menu.
|
|
469
|
+
- **Any future extension** (OTel, auditing, cost tracking) hooks into the same events without pi-subagents knowing.
|
|
470
|
+
|
|
471
|
+
This is achieved across three phases: Phase 14 (strip policy), Phase 16 (invert dependencies), and Phase 17 (extract UI).
|
|
472
|
+
|
|
433
473
|
## Current structural analysis
|
|
434
474
|
|
|
435
475
|
### Health metrics
|
|
436
476
|
|
|
437
|
-
| Metric | Value
|
|
438
|
-
| -------------------------- |
|
|
439
|
-
| Health score | 78/100 (B)
|
|
440
|
-
| Total LOC | 8,
|
|
441
|
-
|
|
|
442
|
-
|
|
|
443
|
-
|
|
|
444
|
-
|
|
|
445
|
-
|
|
|
446
|
-
|
|
|
447
|
-
|
|
|
448
|
-
| Fallow refactoring targets | 1 (buildParentContext, cognitive 30) |
|
|
477
|
+
| Metric | Value |
|
|
478
|
+
| -------------------------- | --------------------------------- |
|
|
479
|
+
| Health score | 78/100 (B) |
|
|
480
|
+
| Total LOC | 8,382 (56 files) |
|
|
481
|
+
| Dead code | 0 files, 0 exports |
|
|
482
|
+
| Maintainability index | 90.8 (good) |
|
|
483
|
+
| Avg cyclomatic complexity | 1.4 |
|
|
484
|
+
| P90 cyclomatic complexity | 2 |
|
|
485
|
+
| Production duplication | 11 lines (1 internal clone group) |
|
|
486
|
+
| Test duplication | 38 clone groups, 634 lines |
|
|
487
|
+
| Fallow refactoring targets | 0 |
|
|
449
488
|
|
|
450
489
|
### Dependency bag inventory
|
|
451
490
|
|
|
@@ -471,28 +510,29 @@ Bags with 10+ fields are the highest priority for decomposition.
|
|
|
471
510
|
|
|
472
511
|
Functions with cyclomatic complexity ≥ 21 (critical threshold):
|
|
473
512
|
|
|
474
|
-
No functions remain above the critical threshold — all hotspots resolved in Phase 12.
|
|
513
|
+
No functions remain above the critical threshold — all hotspots resolved in Phase 12. 6 functions remain at HIGH severity (CRAP ≥ 65); 13 at moderate.
|
|
475
514
|
|
|
476
515
|
### Churn hotspots
|
|
477
516
|
|
|
478
|
-
Files with highest commit frequency × complexity
|
|
517
|
+
Files with highest commit frequency × complexity:
|
|
479
518
|
|
|
480
519
|
| Score | File | Commits | Trend |
|
|
481
520
|
| ----- | --------------------------- | ------- | -------------- |
|
|
482
|
-
|
|
|
483
|
-
|
|
|
484
|
-
|
|
|
485
|
-
|
|
|
486
|
-
|
|
|
487
|
-
|
|
|
521
|
+
| 65.0 | `index.ts` | 128 | ▲ accelerating |
|
|
522
|
+
| 9.1 | `ui/agent-widget.ts` | 13 | ▼ cooling |
|
|
523
|
+
| 8.4 | `ui/conversation-viewer.ts` | 11 | ─ stable |
|
|
524
|
+
| 6.4 | `runtime.ts` | 12 | ─ stable |
|
|
525
|
+
| 3.3 | `settings.ts` | 4 | ─ stable |
|
|
526
|
+
| 2.9 | `handlers/lifecycle.ts` | 11 | ─ stable |
|
|
488
527
|
|
|
489
|
-
|
|
490
|
-
|
|
528
|
+
Most files have cooled to stable after 13 phases of structural work.
|
|
529
|
+
`index.ts` remains the sole accelerating hotspot — expected as the wiring entry point for each refactoring phase.
|
|
491
530
|
|
|
492
531
|
### Production duplication
|
|
493
532
|
|
|
494
533
|
The prior clone group between `agent-runner.ts` and `message-formatters.ts` was resolved in #172.
|
|
495
|
-
The 20-line clone group between `agent-config-editor.ts` and `agent-creation-wizard.ts` was resolved in #217 — extracted into `ui/agent-file-writer.ts` (`writeAgentFile`).
|
|
534
|
+
The 20-line clone group between `agent-config-editor.ts` and `agent-creation-wizard.ts` was resolved in #217 — extracted into `ui/agent-file-writer.ts` (`writeAgentFile`).
|
|
535
|
+
One 11-line internal clone group remains within `agent-config-editor.ts` (lines 135–145 / 173–183).
|
|
496
536
|
|
|
497
537
|
### Proposed bag decompositions
|
|
498
538
|
|
|
@@ -619,123 +659,97 @@ See [phase-11-closure-to-class.md](history/phase-11-closure-to-class.md) for det
|
|
|
619
659
|
Phase 12 decomposed the three remaining high-complexity UI functions and extracted shared test fixtures.
|
|
620
660
|
All four steps are closed: [#205], [#206], [#207], [#208].
|
|
621
661
|
|
|
622
|
-
##
|
|
623
|
-
|
|
624
|
-
Phase 13 addresses the remaining fallow refactoring target, `agent-manager.ts` oversized method, production duplication, SDK boundary coupling, and the heaviest test clone families.
|
|
625
|
-
Health score target: 80+ (A).
|
|
626
|
-
|
|
627
|
-
### Findings summary
|
|
628
|
-
|
|
629
|
-
| Finding | Category | Impact | Risk | Priority |
|
|
630
|
-
| ---------------------------------------------------------------- | -------------- | ------ | ---- | -------- |
|
|
631
|
-
| 3 remaining closure factories (Phase 11 survivors) | C: Coupling | 4 | 2 | 16 |
|
|
632
|
-
| `buildParentContext` cognitive 30 (only fallow target) | B: Oversized | 3 | 1 | 15 |
|
|
633
|
-
| `startAgent` in agent-manager.ts (~130 LOC method) | B: Oversized | 4 | 3 | 12 |
|
|
634
|
-
| Test duplication: 59 clone groups, 1,046 lines | D: Testability | 3 | 2 | 12 |
|
|
635
|
-
| Overwrite guard duplicated across UI modules (20 lines) | A: Redundant | 2 | 1 | 10 |
|
|
636
|
-
| `settings.ts` calls SDK function `getAgentDir()` at module level | C: Coupling | 2 | 1 | 10 |
|
|
637
|
-
|
|
638
|
-
### Step 1: Convert remaining closure factories to classes — [#214] ✓
|
|
662
|
+
## Phase 13 (complete)
|
|
639
663
|
|
|
640
|
-
|
|
664
|
+
Phase 13 addressed remaining closure factories, the last fallow refactoring target, oversized methods, production duplication, SDK boundary coupling, and test clone families.
|
|
665
|
+
All six steps are closed: [#214], [#215], [#216], [#217], [#218], [#219].
|
|
666
|
+
See [phase-13-remaining-smells.md](history/phase-13-remaining-smells.md) for details.
|
|
641
667
|
|
|
642
|
-
|
|
643
|
-
| ------------------------------------------------------ | ----------------------------- | ---------------------------------------- |
|
|
644
|
-
| `createAgentConfigEditor()` → `AgentConfigEditor` | `ui/agent-config-editor.ts` | `fileOps`, `registry`, 2 dirs |
|
|
645
|
-
| `createAgentCreationWizard()` → `AgentCreationWizard` | `ui/agent-creation-wizard.ts` | `fileOps`, `manager`, `registry`, 2 dirs |
|
|
646
|
-
| `createSubagentsService()` → `SubagentsServiceAdapter` | `service/service-adapter.ts` | `manager`, `resolveModel`, `runtime` |
|
|
668
|
+
## Improvement roadmap (Phase 14 — strip policy from core)
|
|
647
669
|
|
|
648
|
-
|
|
649
|
-
|
|
670
|
+
Phase 14 removes tool and extension policy enforcement from pi-subagents.
|
|
671
|
+
This code duplicates what pi-permission-system already provides with richer semantics (allow/ask/deny vs. binary hide).
|
|
672
|
+
Removing it simplifies `runAgent`, shrinks `AgentConfig` and `SessionConfig`, and makes the Phase 15 domain-model work operate on a cleaner codebase.
|
|
650
673
|
|
|
651
|
-
###
|
|
652
|
-
|
|
653
|
-
`buildParentContext` in `session/context.ts` is the only remaining fallow refactoring target.
|
|
654
|
-
The function loops over branch entries with 3 type-check branches, each with sub-branches for role or summary.
|
|
655
|
-
Extract per-entry-type formatters: `formatMessageEntry(entry)` and `formatCompactionEntry(entry)`.
|
|
656
|
-
|
|
657
|
-
- Target: `src/session/context.ts`
|
|
658
|
-
- Smell: B (oversized function)
|
|
659
|
-
- Outcome: cognitive complexity < 10, function < 15 LOC
|
|
660
|
-
|
|
661
|
-
### Step 3: Decompose `startAgent` in `agent-manager.ts` — [#216] ✓
|
|
662
|
-
|
|
663
|
-
`startAgent` had two mutable closure variables (`unsubRecordObserver`, `detachParentSignal`) shared across three callbacks with duplicated finalization logic in `.then()`/`.catch()`.
|
|
664
|
-
The fix introduced a `RunHandle` lifecycle object (private to `agent-manager.ts`) that owns the per-run cleanup state and exposes `complete()`/`fail()` as Tell-Don't-Ask methods.
|
|
665
|
-
`WorktreeState` gained `performCleanup(worktrees, description)` to eliminate the ask-tell dance at cleanup sites.
|
|
674
|
+
### Findings summary
|
|
666
675
|
|
|
667
|
-
|
|
676
|
+
| Finding | Category | Impact | Risk | Priority |
|
|
677
|
+
| ----------------------------------------------------------------------------------------- | ------------- | ------ | ---- | -------- |
|
|
678
|
+
| `disallowed_tools` duplicates pi-permission-system's `permission:` frontmatter | A: Overlap | 4 | 2 | 12 |
|
|
679
|
+
| `extensions: string[]` allowlist is tool filtering disguised as lifecycle control | A: Overlap | 3 | 2 | 9 |
|
|
680
|
+
| `filterActiveTools` runs twice (pre-bind + post-bind) to catch extension-registered tools | B: Complexity | 3 | 1 | 6 |
|
|
681
|
+
| `ToolFilterConfig` exists solely to carry filtering state through the runner | C: Accidental | 2 | 1 | 4 |
|
|
668
682
|
|
|
669
|
-
|
|
670
|
-
2. `finalizeBackgroundRun(record)` — shared `runningBackground--`, crash-safe observer notification, `drainQueue()`.
|
|
671
|
-
3. `setupWorktree(id, record, isolation)` — worktree creation with strict failure.
|
|
672
|
-
4. `flushPendingSteers(id, session)` — drain buffered steers on session creation.
|
|
673
|
-
5. `WorktreeState.performCleanup(worktrees, description)` — self-cleanup eliminating ask-tell.
|
|
683
|
+
### Step 1: Remove `disallowed_tools` — [#237] ✅ Complete
|
|
674
684
|
|
|
675
|
-
|
|
676
|
-
- Smell: B (oversized method) + A (duplicated finalization logic in then/catch)
|
|
677
|
-
- Outcome: `startAgent` reduced to ~40 LOC coordinator with zero mutable `let` bindings; `.then()`/`.catch()` are one-liners
|
|
685
|
+
Remove the `disallowedTools` field from `AgentConfig` and all code that processes it.
|
|
678
686
|
|
|
679
|
-
|
|
687
|
+
1. Remove `disallowedTools` from the `AgentConfig` interface in `types.ts`.
|
|
688
|
+
2. Remove `disallowed_tools` parsing from custom agent frontmatter in `custom-agents.ts`.
|
|
689
|
+
3. Remove `disallowedSet` from `ToolFilterConfig` in `session-config.ts`.
|
|
690
|
+
4. Remove the `disallowedSet` construction in `assembleSessionConfig`.
|
|
691
|
+
5. Remove the `disallowedSet` branch from `filterActiveTools` in `agent-runner.ts`.
|
|
692
|
+
6. Remove `disallowed_tools` from the agent config editor UI.
|
|
693
|
+
7. Remove `disallowed_tools` from the agent creation wizard.
|
|
694
|
+
8. Update tests.
|
|
680
695
|
|
|
681
|
-
|
|
682
|
-
|
|
696
|
+
- Target: `types.ts`, `custom-agents.ts`, `session-config.ts`, `agent-runner.ts`, `ui/agent-config-editor.ts`, `ui/agent-creation-wizard.ts`
|
|
697
|
+
- Smell: A (responsibility overlap with pi-permission-system)
|
|
698
|
+
- Outcome: users migrate to `permission:` frontmatter for tool restrictions; single source of truth for access control
|
|
683
699
|
|
|
684
|
-
|
|
685
|
-
- Smell: A (production duplication)
|
|
686
|
-
- Outcome: 0 production clone groups
|
|
700
|
+
### Step 2: Remove `extensions` filtering — [#238] ✅ Complete
|
|
687
701
|
|
|
688
|
-
|
|
702
|
+
Remove the `extensions: string[]` allowlist and simplify the field to a boolean.
|
|
703
|
+
The `extensions: false` case (used by `isolated`) is retained in this step and removed in Phase 16.
|
|
689
704
|
|
|
690
|
-
`
|
|
691
|
-
|
|
692
|
-
|
|
705
|
+
1. Change `extensions` type from `true | string[] | false` to `boolean` in `AgentConfig`.
|
|
706
|
+
2. Remove the `extensions` array branch from `filterActiveTools` in `agent-runner.ts`.
|
|
707
|
+
3. Remove `extensions` from the agent config editor and creation wizard.
|
|
708
|
+
4. Update custom agent frontmatter parsing to treat array values as `true` (with a warning).
|
|
709
|
+
5. Update tests.
|
|
693
710
|
|
|
694
|
-
- Target: `
|
|
695
|
-
- Smell:
|
|
696
|
-
- Outcome: `
|
|
711
|
+
- Target: `types.ts`, `agent-runner.ts`, `session-config.ts`, `ui/agent-config-editor.ts`, `ui/agent-creation-wizard.ts`
|
|
712
|
+
- Smell: A (tool filtering disguised as extension lifecycle control)
|
|
713
|
+
- Outcome: `filterActiveTools` reduces to two concerns: recursion guard and `extensions: false` passthrough
|
|
697
714
|
|
|
698
|
-
### Step
|
|
715
|
+
### Step 3: Collapse `filterActiveTools` to recursion guard — [#239]
|
|
699
716
|
|
|
700
|
-
|
|
717
|
+
With Steps 1–2 complete, `filterActiveTools` has only two remaining branches: the `EXCLUDED_TOOL_NAMES` recursion guard and the `extensions === false` passthrough.
|
|
718
|
+
Inline the `extensions === false` passthrough into the callsite and reduce the function to its essential purpose.
|
|
701
719
|
|
|
702
|
-
1. `
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
720
|
+
1. Simplify `filterActiveTools` to filter only `EXCLUDED_TOOL_NAMES`.
|
|
721
|
+
2. Remove `ToolFilterConfig` — the function no longer needs a config bag.
|
|
722
|
+
3. Remove the pre-bind filter call — extension tools aren't in the active set yet, so filtering built-in tools pre-bind is only needed for denylist/allowlist logic that no longer exists.
|
|
723
|
+
4. Keep a single post-bind filter call for the recursion guard.
|
|
724
|
+
5. Simplify `SessionConfig` — remove the `toolFilter` field, keep `toolNames` as a flat field.
|
|
725
|
+
6. Update tests.
|
|
708
726
|
|
|
709
|
-
- Target: `
|
|
710
|
-
- Smell:
|
|
711
|
-
- Outcome:
|
|
727
|
+
- Target: `agent-runner.ts`, `session-config.ts`
|
|
728
|
+
- Smell: B (accidental complexity), C (two-pass filter dance)
|
|
729
|
+
- Outcome: `filterActiveTools` is a one-liner; `SessionConfig` loses one nested type; the pre-bind/post-bind dance is gone
|
|
712
730
|
|
|
713
731
|
### Step dependency diagram
|
|
714
732
|
|
|
715
733
|
```mermaid
|
|
716
734
|
flowchart LR
|
|
717
|
-
S1["Step 1\
|
|
718
|
-
S2["Step 2\
|
|
719
|
-
S3["Step 3\
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
S1 --> S4
|
|
725
|
-
S1 --> S6
|
|
726
|
-
S3 --> S6
|
|
727
|
-
S2 ~~~ S5
|
|
735
|
+
S1["Step 1\nRemove disallowed_tools"]
|
|
736
|
+
S2["Step 2\nRemove extensions filtering"]
|
|
737
|
+
S3["Step 3\nCollapse filterActiveTools"]
|
|
738
|
+
|
|
739
|
+
S1 --> S3
|
|
740
|
+
S2 --> S3
|
|
728
741
|
```
|
|
729
742
|
|
|
730
|
-
|
|
743
|
+
Steps 1 and 2 are independent and can proceed in parallel.
|
|
744
|
+
Step 3 depends on both.
|
|
731
745
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
746
|
+
[#237]: https://github.com/gotgenes/pi-packages/issues/237
|
|
747
|
+
[#238]: https://github.com/gotgenes/pi-packages/issues/238
|
|
748
|
+
[#239]: https://github.com/gotgenes/pi-packages/issues/239
|
|
735
749
|
|
|
736
|
-
## Improvement roadmap (Phase
|
|
750
|
+
## Improvement roadmap (Phase 15 — domain model evolution)
|
|
737
751
|
|
|
738
|
-
Phase
|
|
752
|
+
Phase 15 addresses the anemic domain model in the lifecycle layer.
|
|
739
753
|
`AgentRecord` is a data bag — identity, status transitions, and stats — but no behavior.
|
|
740
754
|
`AgentManager` reaches into records 37 times, doing work that belongs on the agent.
|
|
741
755
|
Per-agent state (pending steers, abort logic, run lifecycle) is scattered across the manager, `RunHandle`, and a manager-level Map.
|
|
@@ -839,26 +853,51 @@ flowchart LR
|
|
|
839
853
|
Sequential — each depends on the previous.
|
|
840
854
|
2. **Track B — Decoupling** (Steps 3, 4, 5): independent, can proceed in parallel with Track A.
|
|
841
855
|
|
|
856
|
+
## Improvement roadmap (Phase 16 — invert dependencies)
|
|
857
|
+
|
|
858
|
+
Phase 16 completes the architectural inversion by removing the outbound permission bridge and the `extensions: false` / `isolated` concepts.
|
|
859
|
+
It depends on Phase 15's observer pattern (#229) as the replacement mechanism.
|
|
860
|
+
|
|
861
|
+
Phase 16 is scoped but not yet broken into steps.
|
|
862
|
+
Key changes:
|
|
863
|
+
|
|
864
|
+
1. Remove `permission-bridge.ts` — the outbound coupling to pi-permission-system.
|
|
865
|
+
2. Emit child session lifecycle events via the observer — pi-permission-system and other consumers listen for these events instead of being called.
|
|
866
|
+
3. Remove `extensions: false` — all child sessions load all extensions.
|
|
867
|
+
4. Dissolve or redefine `isolated` — without extension control and tool filtering, the concept either disappears or becomes purely about prompt composition (no skill preloading, no parent context inheritance).
|
|
868
|
+
5. Update pi-permission-system to listen for child session events instead of being registered by the bridge.
|
|
869
|
+
|
|
870
|
+
## Improvement roadmap (Phase 17 — extract UI)
|
|
871
|
+
|
|
872
|
+
Phase 17 is the long-deferred UI extraction (originally Phase 6).
|
|
873
|
+
The widget, conversation viewer, and `/agents` command menu move to a separate `pi-subagents-ui` extension that consumes the `SubagentsService` API.
|
|
874
|
+
By this point the core is minimal and stable — the API boundary has been proven across Phases 14–16.
|
|
875
|
+
|
|
842
876
|
## Refactoring history
|
|
843
877
|
|
|
844
878
|
Phases 1–5 and 7–12 are complete.
|
|
845
879
|
Phase 6 (UI extraction to a separate package) is deferred.
|
|
846
880
|
Detailed records are preserved in per-phase history files:
|
|
847
881
|
|
|
848
|
-
| Phase
|
|
849
|
-
|
|
|
850
|
-
| 1
|
|
851
|
-
| 2
|
|
852
|
-
| 3
|
|
853
|
-
| 4
|
|
854
|
-
| 5
|
|
855
|
-
| 6
|
|
856
|
-
| 7
|
|
857
|
-
| 8
|
|
858
|
-
| 9
|
|
859
|
-
| 10
|
|
860
|
-
| 11
|
|
861
|
-
| 12
|
|
882
|
+
| Phase | Title | Status | History |
|
|
883
|
+
| -------- | --------------------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
884
|
+
| 1 | Export SubagentsService API boundary | Complete | [phase-1-api-boundary.md](history/phase-1-api-boundary.md) |
|
|
885
|
+
| 2 | Remove scheduling subsystem | Complete | [phase-2-remove-scheduling.md](history/phase-2-remove-scheduling.md) |
|
|
886
|
+
| 3 | Remove group-join, RPC; replace output-file | Complete | [phase-3-remove-rpc-groupjoin.md](history/phase-3-remove-rpc-groupjoin.md) |
|
|
887
|
+
| 4 | Implement and publish SubagentsService | Complete | [phase-4-implement-service.md](history/phase-4-implement-service.md) |
|
|
888
|
+
| 5 | Decompose index.ts | Complete | [phase-5-decompose-index.md](history/phase-5-decompose-index.md) |
|
|
889
|
+
| 6 | Extract UI to separate package | Deferred → Phase 17 | — |
|
|
890
|
+
| 7 | Encapsulation and dependency narrowing | Complete | [phase-7-encapsulation.md](history/phase-7-encapsulation.md) |
|
|
891
|
+
| 8 | Testability, display extraction, menu decomposition | Complete | [phase-8-testability.md](history/phase-8-testability.md) |
|
|
892
|
+
| 9 | Observation consolidation, ctx elimination | Complete | [phase-9-observation-ctx.md](history/phase-9-observation-ctx.md) |
|
|
893
|
+
| 10 | Domain organization, bag decomposition, complexity | Complete | [phase-10-structural-decomposition.md](history/phase-10-structural-decomposition.md) |
|
|
894
|
+
| 11 | Closure factories to classes | Complete | [phase-11-closure-to-class.md](history/phase-11-closure-to-class.md) |
|
|
895
|
+
| 12 | Complexity reduction and test fixture extraction | Complete | [phase-12-complexity-test-fixtures.md](history/phase-12-complexity-test-fixtures.md) |
|
|
896
|
+
| 13 | Remaining structural smells | Complete | [phase-13-remaining-smells.md](history/phase-13-remaining-smells.md) |
|
|
897
|
+
| 14 | Strip policy from core | Planned | — |
|
|
898
|
+
| 15 | Domain model evolution | Planned | — |
|
|
899
|
+
| 16 | Invert dependencies | Planned | — |
|
|
900
|
+
| 17 | Extract UI to separate package | Planned | — |
|
|
862
901
|
|
|
863
902
|
### Structural refactoring issues
|
|
864
903
|
|
|
@@ -876,7 +915,8 @@ Detailed records are preserved in per-phase history files:
|
|
|
876
915
|
| Phase 11 | #192, #193, #194, #195, #196 | SessionContext, runtime queries, interface alignment, tool classes, runner/menu classes, index.ts simplification |
|
|
877
916
|
| Phase 12 | #205, #206, #207, #208 | renderWidgetLines, showAgentDetail, widget update, shared test fixtures |
|
|
878
917
|
| Phase 13 | #214, #215, #216, #217, #218, #219 | Closure-to-class, buildParentContext, startAgent decomp, overwrite guard, settings SDK, test duplication |
|
|
879
|
-
| Phase 14 | #
|
|
918
|
+
| Phase 14 | #237, #238, #239 | Remove disallowed_tools, remove extensions filtering, collapse filterActiveTools |
|
|
919
|
+
| Phase 15 | #227, #228, #229, #230, #231, #232 | Agent domain model, async startAgent, onSessionCreated observer, ConcurrencyQueue, relay deps, resume unification |
|
|
880
920
|
|
|
881
921
|
The remaining open issue is #22 (parent-session resolution), a cross-extension track that does not gate the structural work.
|
|
882
922
|
|
|
@@ -909,3 +949,9 @@ The upstream test suite is run periodically as a regression canary for the agent
|
|
|
909
949
|
[#217]: https://github.com/gotgenes/pi-packages/issues/217
|
|
910
950
|
[#218]: https://github.com/gotgenes/pi-packages/issues/218
|
|
911
951
|
[#219]: https://github.com/gotgenes/pi-packages/issues/219
|
|
952
|
+
[#227]: https://github.com/gotgenes/pi-packages/issues/227
|
|
953
|
+
[#228]: https://github.com/gotgenes/pi-packages/issues/228
|
|
954
|
+
[#229]: https://github.com/gotgenes/pi-packages/issues/229
|
|
955
|
+
[#230]: https://github.com/gotgenes/pi-packages/issues/230
|
|
956
|
+
[#231]: https://github.com/gotgenes/pi-packages/issues/231
|
|
957
|
+
[#232]: https://github.com/gotgenes/pi-packages/issues/232
|