@nklisch/pi-enhanced 0.4.3 → 0.5.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 +17 -0
- package/README.md +1 -1
- package/node_modules/@nklisch/pi-astral-pocket/README.md +127 -93
- package/node_modules/@nklisch/pi-astral-pocket/package.json +2 -2
- package/node_modules/@nklisch/pi-astral-pocket/src/distiller.ts +196 -183
- package/node_modules/@nklisch/pi-astral-pocket/src/guidance.ts +26 -62
- package/node_modules/@nklisch/pi-astral-pocket/src/index.ts +8 -5
- package/node_modules/@nklisch/pi-astral-pocket/src/sessions.ts +10 -3
- package/node_modules/@nklisch/pi-astral-pocket/src/store.ts +439 -321
- package/node_modules/@nklisch/pi-astral-pocket/src/tools.ts +172 -66
- package/node_modules/@nklisch/pi-clearance/docs/CONFIGURATION.md +3 -3
- package/node_modules/@nklisch/pi-clearance/docs/DEVELOPER_GUIDE.md +11 -12
- package/node_modules/@nklisch/pi-clearance/docs/PACK_AUTHORING.md +5 -5
- package/node_modules/@nklisch/pi-clearance/docs/PRINCIPLES.md +1 -2
- package/node_modules/@nklisch/pi-clearance/docs/REFERENCE_PATTERNS.md +13 -19
- package/node_modules/@nklisch/pi-clearance/docs/SPEC.md +4 -4
- package/node_modules/@nklisch/pi-clearance/docs/USER_GUIDE.md +2 -3
- package/node_modules/@nklisch/pi-clearance/docs/VISION.md +1 -1
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.win32-x64-msvc.node +0 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/README.md +2 -2
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/VISION.md +2 -3
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/architecture.md +105 -886
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/decisions/0002-extensions-on-a-minimal-core.md +44 -78
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/decisions/0003-publish-bundled-type-declarations.md +28 -57
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/decisions/0004-reconsider-ui-direction.md +49 -261
- package/node_modules/@nklisch/pi-plugins/package.json +2 -2
- package/package.json +2 -2
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/client-server-opportunities.md +0 -127
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-1-api-boundary.md +0 -8
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-10-structural-decomposition.md +0 -141
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-11-closure-to-class.md +0 -100
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-12-complexity-test-fixtures.md +0 -55
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-13-remaining-smells.md +0 -88
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-14-strip-policy.md +0 -49
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-15-domain-model-evolution.md +0 -73
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-16-invert-dependencies.md +0 -144
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-17-core-consolidation.md +0 -214
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-18-reconsider-ui.md +0 -166
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-19-implement-ui-decisions.md +0 -282
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-2-remove-scheduling.md +0 -9
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-3-remove-rpc-groupjoin.md +0 -11
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-4-implement-service.md +0 -8
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-5-decompose-index.md +0 -42
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-7-encapsulation.md +0 -173
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-8-testability.md +0 -103
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-9-observation-ctx.md +0 -122
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/decisions/0001-deferred-patches.md +0 -80
|
@@ -1,98 +1,64 @@
|
|
|
1
|
-
---
|
|
2
|
-
status: accepted
|
|
3
|
-
date: 2026-05-29
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
# 0002 — Workspaces and permissions are extensions on a minimal core
|
|
7
2
|
|
|
8
|
-
## Status
|
|
9
|
-
|
|
10
|
-
Accepted.
|
|
11
|
-
Supersedes the "agent collaborator architecture" framing of Phase 16 (an abandoned exploration) and the work shipped under it: issue #256 (`WorktreeIsolation` as an `Agent` collaborator) and issue #257 (`ChildSessionFactory` extraction, parked at planning).
|
|
12
|
-
Reclaims Phase 16's original intent — "invert dependencies" — and extends it to evict worktree isolation from the core.
|
|
13
|
-
|
|
14
3
|
## Context
|
|
15
4
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
2. The worktree is *ready* only at dequeue (a concurrency slot is held and `git worktree add` has run).
|
|
21
|
-
"Construct when ready" therefore means constructing the worktree at run-start, not at spawn — which dissolves the lazy `setup()` and makes `cwd` knowable at construction.
|
|
22
|
-
3. The worktree and the child session share one lifespan: both are born at run-start and torn down at completion (the worktree's cleanup saves a branch; the session is disposed).
|
|
23
|
-
Resources with one lifetime are one resource, not sibling collaborators that `Agent` must sequence.
|
|
24
|
-
The `create(cwd?)` parameter only existed because we split one run-scoped resource (the worktree) out and made `Agent` relay its output back in.
|
|
25
|
-
4. Worktrees are not intrinsic to what makes subagents useful.
|
|
26
|
-
The maintainer never uses them (WIP-of-1, trunk-based, CI/CD).
|
|
27
|
-
Git worktree isolation is one *strategy* for answering "where does this child run, and what brackets the run?"
|
|
28
|
-
— a container, a throwaway tmpdir, or a remote sandbox are others.
|
|
29
|
-
The core needs only *a working directory and a disposal hook*; the default (the parent's cwd, no setup/teardown) is always correct.
|
|
30
|
-
5. This mirrors Phase 14, which evicted tool/extension *policy* (`disallowed_tools`, `extensions` filtering) to `@gotgenes/pi-permission-system`.
|
|
31
|
-
Worktrees are *environment* policy; they belong outside the core for the same reason.
|
|
32
|
-
|
|
33
|
-
Permissions and workspaces are orthogonal concerns that must compose as independent extensions on the core, never knowing about each other.
|
|
34
|
-
|
|
35
|
-
## Decision
|
|
36
|
-
|
|
37
|
-
pi-subagents is a minimal orchestrator: it spawns a child session derived from the parent, runs the turn loop, tracks and streams and collects the result, gates concurrency, supports resume, and **publishes its lifecycle**.
|
|
38
|
-
Everything else attaches through exactly two extension surfaces, distinguished by the direction of information flow.
|
|
39
|
-
|
|
40
|
-
### Two extension surfaces
|
|
41
|
-
|
|
42
|
-
1. **Lifecycle events (observational) — unlimited.**
|
|
43
|
-
The core emits awaited, ordered events for the child-execution lifecycle (`spawning`, `session-created` pre-`bindExtensions`, `completed`, `disposed`).
|
|
44
|
-
Any number of extensions subscribe; handlers return nothing.
|
|
45
|
-
Reactive concerns live here: permission detection, telemetry, UI, notifications.
|
|
46
|
-
Adding a reactive concern never modifies the core.
|
|
5
|
+
Worktrees are not intrinsic to what makes subagents useful. Git worktrees are one
|
|
6
|
+
strategy for answering “where does this child run, and what brackets the run?”
|
|
7
|
+
The core needs a working directory and a disposal hook; the default is the
|
|
8
|
+
parent's working directory with no workspace setup or teardown.
|
|
47
9
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
Today there is exactly one: the **workspace provider** (it returns the child's working directory plus bracketed setup/teardown).
|
|
51
|
-
A provider seam is the only place the core is "open," so the list is kept as small as possible.
|
|
10
|
+
Permissions and workspaces are orthogonal concerns. They must compose as
|
|
11
|
+
independent extensions on the core, never knowing about each other.
|
|
52
12
|
|
|
53
|
-
|
|
13
|
+
## Decision
|
|
54
14
|
|
|
55
|
-
|
|
15
|
+
pi-subagents is a minimal orchestrator: it creates child sessions, drives their
|
|
16
|
+
turns, tracks results, gates concurrency, supports resume, and publishes lifecycle
|
|
17
|
+
boundaries. The core does not look up a named permission or workspace consumer.
|
|
56
18
|
|
|
57
|
-
|
|
58
|
-
- It must **return a value the core consumes** → register a provider (generative, rationed).
|
|
19
|
+
### Lifecycle observation
|
|
59
20
|
|
|
60
|
-
|
|
61
|
-
and
|
|
62
|
-
|
|
21
|
+
Child-execution events expose spawning, session creation before extension binding,
|
|
22
|
+
completion, and disposal. A permission extension can identify the child at the
|
|
23
|
+
pre-bind boundary and enforce its own policy inside the child. Observation does
|
|
24
|
+
not replace prompts or results; those decisions use the separate ordered
|
|
25
|
+
interceptor contract in [ADR 0005](0005-ordered-lifecycle-interceptors.md).
|
|
63
26
|
|
|
64
|
-
###
|
|
27
|
+
### Workspace provider
|
|
65
28
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
29
|
+
A registered `WorkspaceProvider` supplies the child's working directory and
|
|
30
|
+
bracketed cleanup. Preparation happens when the run starts, after concurrency
|
|
31
|
+
admission. The session factory receives the resolved working directory, not the
|
|
32
|
+
provider: assembly consumes a value rather than relaying workspace policy.
|
|
69
33
|
|
|
70
|
-
|
|
34
|
+
The provider owns workspace-specific cleanup and result wording. With no provider,
|
|
35
|
+
children run in the parent's working directory. The core does not own Git worktree,
|
|
36
|
+
container, or sandbox implementations.
|
|
71
37
|
|
|
72
|
-
|
|
73
|
-
- **`permission-bridge.ts`** → retired.
|
|
74
|
-
The core stops reaching *out* to `Symbol.for("@gotgenes/pi-permission-system:service")` and instead *emits* lifecycle events the permission system subscribes to.
|
|
75
|
-
- **`isolated` / `extensions: false` / `noSkills`** → removed.
|
|
76
|
-
Deny-at-use (the in-child permission layer blocking disallowed tool calls) covers what `isolated` pretended to do for tools.
|
|
77
|
-
Prevent-load (refusing to bind an extension because of load-time side effects, cost, or true sandboxing) is genuinely generative and cannot be reduced to observation, so it is left as a *latent* (un-built) provider seam, added only if a real consumer needs it.
|
|
38
|
+
### No vacant hooks
|
|
78
39
|
|
|
79
|
-
|
|
40
|
+
The architecture must admit a seam without shipping it until a concrete consumer
|
|
41
|
+
exists. A provider with no consumer is speculative machinery, not useful
|
|
42
|
+
extensibility. The workspace provider and ordered lifecycle interceptors are
|
|
43
|
+
concrete seams; neither justifies a generic hook framework.
|
|
80
44
|
|
|
81
|
-
|
|
82
|
-
It defends the core's own invariant — a subagent must not recursively spawn — keyed off the core's own tool names.
|
|
83
|
-
With `isolated` gone, children always load the parent's resources, so the guard becomes unconditional rather than gated on `cfg.extensions`.
|
|
45
|
+
### Core invariants
|
|
84
46
|
|
|
85
|
-
|
|
47
|
+
The core removes parent-only orchestration tools from children to prevent recursive
|
|
48
|
+
orchestration. `src/tools/parent-tool-registry.ts` owns that set. Children otherwise
|
|
49
|
+
use registration-open extension tooling with the supported denylist; permission
|
|
50
|
+
policy remains a companion concern.
|
|
86
51
|
|
|
87
|
-
|
|
88
|
-
|
|
52
|
+
The composition test is the same with neither companion, only permissions, only
|
|
53
|
+
workspaces, or both: the core does not change, and the companions do not reference
|
|
54
|
+
each other.
|
|
89
55
|
|
|
90
56
|
## Consequences
|
|
91
57
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
58
|
+
Resource ownership stays explicit. The workspace brackets the run, the session
|
|
59
|
+
factory constructs a usable child session, and the subagent owns execution and
|
|
60
|
+
settlement. Contributors can add a workspace strategy or permission consumer
|
|
61
|
+
without introducing an outward dependency from the core.
|
|
62
|
+
|
|
63
|
+
[Architecture](../architecture/architecture.md) owns the overall component and
|
|
64
|
+
lifecycle model.
|
|
@@ -1,71 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
status: accepted
|
|
3
|
-
date: 2026-05-29
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 0003 — Publish a bundled `.d.ts` for the public surface
|
|
7
|
-
|
|
8
|
-
## Status
|
|
9
|
-
|
|
10
|
-
Accepted.
|
|
11
|
-
Introduces the repository's first build step, scoped to type declarations only.
|
|
1
|
+
# 0003 — Publish bundled declarations for the public surface
|
|
12
2
|
|
|
13
3
|
## Context
|
|
14
4
|
|
|
15
|
-
`@
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
1. `package.json` `exports["."]` pointed at `./src/service.ts`, which does not exist — the real module is `./src/service/service.ts`.
|
|
21
|
-
A latent bug, unnoticed because nothing in-repo imported the package by name.
|
|
22
|
-
2. Once corrected, the public entry's internal alias imports cascade.
|
|
23
|
-
`service/service.ts` imports `type LifetimeUsage` and `type WorkspaceProvider` via the `#src/*` alias.
|
|
24
|
-
When a sibling's `tsc` follows the symlink, the consumer's own `paths` (`#src/*` → `./src/*`) intercept first and resolve into the *consumer's* `src/` — a global-`paths` collision, since both packages define `#src/*`.
|
|
25
|
-
The fallback to the publisher's `package.json` `imports` field also fails: `tsc` cannot resolve the extensionless `.ts` target under Node `imports` semantics ("Import specifier '#src/lifecycle/usage' does not exist in package.json scope").
|
|
26
|
-
|
|
27
|
-
The public entry's type closure is deeply entangled: `WorkspaceProvider` (in `lifecycle/workspace.ts`) reaches `AgentStatus` in the 510-line `lifecycle/agent.ts`, plus `SubagentType`/`AgentInvocation` from `types.ts` (which itself re-exports the `Agent` class).
|
|
28
|
-
A shallow alias-free entry is therefore not achievable without a substantial source restructure.
|
|
29
|
-
|
|
30
|
-
This collides with the ship-source model ([ADR-0002]): every package ships raw `.ts` executed directly by Pi, with no build step.
|
|
5
|
+
Consumers must be able to import `@nklisch/pi-subagents` by package name without
|
|
6
|
+
adopting its internal path aliases or source layout. Following the internal type
|
|
7
|
+
graph directly can make a consumer's own `#src/*` aliases intercept imports meant
|
|
8
|
+
for this package. Packaging must not require restructuring the domain model to
|
|
9
|
+
satisfy another project's compiler configuration.
|
|
31
10
|
|
|
32
11
|
## Decision
|
|
33
12
|
|
|
34
|
-
|
|
13
|
+
Publish self-contained declaration bundles for the root service and `./settings`
|
|
14
|
+
exports, while runtime consumers continue to load TypeScript source.
|
|
35
15
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
16
|
+
- `dist/public.d.ts` describes the root service contract.
|
|
17
|
+
- `dist/settings.d.ts` describes the layered-settings helper.
|
|
18
|
+
- The manifest's `types` conditions point to those bundles; runtime export targets
|
|
19
|
+
remain the corresponding source modules.
|
|
20
|
+
- The declaration build inlines internal types while leaving peer-dependency types
|
|
21
|
+
external. It does not generate a second JavaScript runtime.
|
|
22
|
+
- Declarations are generated at pack time, shipped through the package allowlist,
|
|
23
|
+
and not committed. The manifest and declaration build configuration own exact
|
|
24
|
+
paths and build inputs.
|
|
44
25
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- `default` → `./src/service/service.ts` fixes the stale path and serves runtime consumers; its `import type` lines erase, so no runtime `#src/*` resolution is needed.
|
|
49
|
-
- A `pnpm pack` → throwaway-consumer → `tsc` harness proves external consumability with no publish round-trip and no workspace privileges.
|
|
50
|
-
|
|
51
|
-
This is the repository's first build step.
|
|
52
|
-
It is deliberately narrow: it produces type declarations only and changes nothing about how Pi loads the extension from source (`pi.extensions: ["./src/index.ts"]` is untouched).
|
|
26
|
+
Packed public types must compile in an external consumer without workspace path
|
|
27
|
+
privileges. A workspace-linked consumer also needs the generated declarations
|
|
28
|
+
available before typechecking against the package.
|
|
53
29
|
|
|
54
30
|
## Alternatives considered
|
|
55
31
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
- A self-contained entry that re-declares the public types inline, guarded by a conformance test.
|
|
61
|
-
Avoids a build step but duplicates the seam/usage/status type definitions, which drift over time.
|
|
32
|
+
An alias-free public entry could avoid declaration bundling, but would require
|
|
33
|
+
reorganizing the type graph for a packaging concern. Re-declaring public types in
|
|
34
|
+
a separate entry would instead duplicate contract truth and require keeping both
|
|
35
|
+
copies synchronized. A generated declaration bundle avoids both costs.
|
|
62
36
|
|
|
63
37
|
## Consequences
|
|
64
38
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- Sequencing: #270 must be published (its release-please PR merged) before #263 edits `pi-subagents` core, so #263's changes do not batch into the same `pi-subagents` release.
|
|
70
|
-
|
|
71
|
-
[ADR-0002]: ./0002-extensions-on-a-minimal-core.md
|
|
39
|
+
The build is narrow: declarations for the public contract, not a replacement for
|
|
40
|
+
Pi's source-loading model. Consumers use the packaged interface rather than
|
|
41
|
+
reaching into internal source modules. Public type changes carry the same
|
|
42
|
+
compatibility obligations as the runtime service they describe.
|
|
@@ -1,284 +1,72 @@
|
|
|
1
|
-
|
|
2
|
-
status: accepted
|
|
3
|
-
date: 2026-06-18
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 0004 — Reconsider the UI direction from first principles
|
|
7
|
-
|
|
8
|
-
> Historical vocabulary note: this decision predates the joined/detached clean
|
|
9
|
-
> break. In current code, its "background-only widget" means detached-only;
|
|
10
|
-
> joined runs use their inline result surface. Historical delivery labels below
|
|
11
|
-
> are retained as decision history, not as current API contracts.
|
|
12
|
-
|
|
13
|
-
## Status
|
|
14
|
-
|
|
15
|
-
Accepted.
|
|
16
|
-
Completes Phase 18 (reconsider the UI) and gateways Phase 19 (implement the recorded decisions).
|
|
17
|
-
Decision-only: this ADR changes no runtime code.
|
|
18
|
-
The inherited UI stays live until Phase 19 acts on these decisions.
|
|
1
|
+
# 0004 — Keep a narrow, substitutable operator UI
|
|
19
2
|
|
|
20
3
|
## Context
|
|
21
4
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
This final step decides the UI's _direction and distribution_, not whether substitution is possible.
|
|
27
|
-
The goal is **substitutable, not optional**: a human needs some surface, but the specific UI is replaceable — the way Pi ships a default TUI built on the same public API any extension targets.
|
|
28
|
-
The disentangled core stays byte-for-byte identical whether or not a given UI consumer is installed (the composition invariant), so a replacement UI is a downstream concern even though _some_ UI is not.
|
|
29
|
-
|
|
30
|
-
Unlike the worktrees provider seam (generative, rationed — one provider the core consults), the UI is an observational consumer (unlimited, the core never waits on it).
|
|
31
|
-
That asymmetry is why packaging the UI is the secondary question and decoupling it was the real win.
|
|
32
|
-
|
|
33
|
-
Three operator-framed concerns shape the per-component judgment.
|
|
34
|
-
|
|
35
|
-
1. **Foreground progress is already shown by the tool call.**
|
|
36
|
-
In foreground the `subagent` tool's inline `onUpdate` stream renders progress well; the above-editor widget duplicates it.
|
|
37
|
-
2. **Background agents have no tool-call display.**
|
|
38
|
-
When agents run in the background there is no inline stream, so _something_ must indicate their state — and multiple subagents can run in parallel, so that surface must represent N concurrent agents at once.
|
|
39
|
-
3. **Operator visibility into a subagent's session is a distinct, richer need.**
|
|
40
|
-
"Switch into a subagent's session, scroll/read it, switch between subagents, and exit back to root" is a navigation interaction, not a live overlay.
|
|
41
|
-
The core already persists each child as a standalone Pi session JSONL at `Subagent.outputFile`, and `Subagent.messages` exposes the full history — so the data was never the limit; the bespoke, width-capped `ConversationViewer` overlay was.
|
|
5
|
+
Joined runs already have their calling tool's result surface. Detached runs need
|
|
6
|
+
an ambient indication of progress, including several concurrent children. Reading
|
|
7
|
+
a child's conversation is a different task: an operator needs to inspect its
|
|
8
|
+
transcript without replacing the active parent session.
|
|
42
9
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
- `ExtensionActions.switchSession(sessionPath, { withSession })` switches the **active** session to a different session file.
|
|
48
|
-
It is a full active-session takeover: it fires `session_before_switch` / `session_shutdown`, invalidates the current session context (`setBeforeSessionInvalidate` exists for host-owned UI teardown), and returns `{ cancelled }`.
|
|
49
|
-
The switched-to session is fully interactive — `ReplacedSessionContext` exposes `sendUserMessage`.
|
|
50
|
-
- `session-manager` exports `loadEntriesFromFile(filePath)` / `parseSessionEntries(content)`, which read a session file's entries without switching — the read-only alternative to a full takeover.
|
|
10
|
+
The goal is substitutable, not optional. A human needs some surface, but that
|
|
11
|
+
surface should be a consumer of the core's lifecycle and query boundaries rather
|
|
12
|
+
than a responsibility threaded through execution code.
|
|
51
13
|
|
|
52
14
|
## Decision
|
|
53
15
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
### A — Foreground widget: shrink to background agents only
|
|
57
|
-
|
|
58
|
-
The above-editor widget duplicates the foreground tool's inline `onUpdate` stream.
|
|
59
|
-
The widget survives **only** as the background-agent status surface (concern 2): foreground runs suppress it, the inline stream is authoritative there, and the background surface keeps the widget's existing per-agent tree so it represents N parallel agents at once.
|
|
60
|
-
The change is _when_ the widget shows (background-only), not _what_ it shows.
|
|
16
|
+
### Detached status widget
|
|
61
17
|
|
|
62
|
-
|
|
18
|
+
The above-editor widget represents detached agents only. Joined runs use their
|
|
19
|
+
inline tool surface instead of duplicating progress in the widget. The widget
|
|
20
|
+
maintains a bounded reactive view of active detached records and terminal linger
|
|
21
|
+
state; it does not require the core's spawn tools to drive rendering.
|
|
63
22
|
|
|
64
|
-
|
|
65
|
-
|
|
23
|
+
Operator status surfaces show exact effective model, effective thinking level,
|
|
24
|
+
active or final runtime, run id, delivery mode, and terminal reason when present.
|
|
66
25
|
|
|
67
|
-
|
|
26
|
+
### Native, read-only session navigation
|
|
68
27
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (child?.outputFile) {
|
|
73
|
-
await ctx.switchSession(child.outputFile);
|
|
74
|
-
// operator reads/scrolls in Pi's native viewer; a later switch returns to root
|
|
75
|
-
}
|
|
76
|
-
```
|
|
28
|
+
`/subagents:sessions` lets the operator select a child transcript without leaving
|
|
29
|
+
the parent session. It is read-only: steering remains a separate tool behavior,
|
|
30
|
+
not a second editor embedded in the viewer.
|
|
77
31
|
|
|
78
|
-
|
|
32
|
+
Do not use `switchSession` for navigation. Switching replaces the active runtime
|
|
33
|
+
and conflicts with keeping the parent and its detached work running. Read and
|
|
34
|
+
render the transcript through supported Pi APIs instead.
|
|
79
35
|
|
|
80
|
-
|
|
81
|
-
`switchSession` is a full active-session takeover and is interactive, so the operator UX is gated on a Phase 19 spike that chooses between (i) true `switchSession` round-trips and (ii) a read-only transcript built from `loadEntriesFromFile` that renders Pi-standard entries without leaving the root session.
|
|
82
|
-
See "Phase 19 entry criteria."
|
|
36
|
+
The transcript has two sources and one renderer:
|
|
83
37
|
|
|
84
|
-
|
|
38
|
+
- A retained live child supplies messages and update subscriptions.
|
|
39
|
+
- After the live session is released, the retained transcript pointer supplies a
|
|
40
|
+
file snapshot through Pi's session parsing and context-building APIs.
|
|
85
41
|
|
|
86
|
-
|
|
87
|
-
|
|
42
|
+
Both sources render through Pi's own message and tool-execution components, not a
|
|
43
|
+
parallel transcript renderer. Session release updates the source; an unavailable
|
|
44
|
+
snapshot preserves readable content and labels the degraded state. Candidate
|
|
45
|
+
identity and labels come from retained manager records rather than a directory
|
|
46
|
+
scan of unrelated session files. Session selection includes joined and detached
|
|
47
|
+
records with readable transcript sources.
|
|
88
48
|
|
|
89
|
-
|
|
90
|
-
An operator generates a new agent `.md` by asking a Pi agent directly (more capable than a fixed wizard) or by writing the file in an editor.
|
|
91
|
-
- **Agent types (list + config editor)** → **remove.**
|
|
92
|
-
Viewing and editing agent definitions is better served by opening the `.md` files directly in an editor/IDE.
|
|
93
|
-
- **Running agents (visibility)** → **keep the responsibility, re-home it.**
|
|
94
|
-
_Something_ must own running-agent visibility; it moves onto the background widget (Decision A) plus the native session navigation (Decision B), not a bespoke in-menu overlay.
|
|
95
|
-
- **Settings (concurrency / max turns / grace turns)** → **extract to a focused command** (e.g. `/subagents:settings`).
|
|
96
|
-
Some value, but it does not belong bundled with agent management.
|
|
49
|
+
### Focused settings and definition ownership
|
|
97
50
|
|
|
98
|
-
|
|
51
|
+
`/subagents:settings` owns operational settings. Both commands use the
|
|
52
|
+
`subagents:` namespace. Agent-definition creation and editing belong in ordinary
|
|
53
|
+
files and editors, not built-in wizards or a general `/agents` management menu.
|
|
99
54
|
|
|
100
|
-
|
|
101
|
-
The surviving UI — the background widget, a focused settings command, and the session-navigation glue — **stays in-core** as a reactive consumer.
|
|
102
|
-
Extraction to a separate `@gotgenes/pi-subagents-ui` package is **not** chosen now.
|
|
55
|
+
### Distribution
|
|
103
56
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
57
|
+
The detached widget, session navigator, and settings command stay in-core as
|
|
58
|
+
substitutable consumers. The core's execution behavior does not depend on which UI
|
|
59
|
+
consumer renders it. Extracting another UI package is not required by that
|
|
60
|
+
boundary; reconsider distribution only when a materially different consumer or
|
|
61
|
+
actual coupling makes it useful.
|
|
107
62
|
|
|
108
63
|
## Consequences
|
|
109
64
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
-
|
|
114
|
-
|
|
115
|
-
- Phase 19 must preserve the spine's invariants when it acts on these decisions: the runtime holds zero UI state (#422), the widget is a reactive consumer with no inbound calls from core spawn tools (#423), the LLM tool depends only on manager/runtime/settings/registry (#424), and declared event channels equal emitted channels with no vacant hook (#425).
|
|
116
|
-
These are pinned today by the existing observer/widget/event-contract suites, which Phase 19 inherits.
|
|
117
|
-
|
|
118
|
-
## Phase 19 entry criteria
|
|
119
|
-
|
|
120
|
-
The following are open and must be resolved by a Phase 19 spike before committing to a mechanism; they are deliberately not decided here.
|
|
121
|
-
|
|
122
|
-
- **Root-continuity during a session switch.**
|
|
123
|
-
`switchSession` invalidates the current session context — does the root's in-flight turn survive a switch-out-and-return, and what is the correct "return to root" gesture?
|
|
124
|
-
Resolve before committing to true `switchSession` round-trips.
|
|
125
|
-
- **View-only vs interactive.**
|
|
126
|
-
A switched-to child session is interactive (`sendUserMessage`).
|
|
127
|
-
Decide whether steering a child from its own session is desirable, or whether the viewer should be strictly read-only (favoring the `loadEntriesFromFile` transcript path).
|
|
128
|
-
- **Parallel-agent navigation.**
|
|
129
|
-
With N background agents running, decide the operator's gesture to pick which child to view and to cycle between them — driven from the background widget, a dedicated command, or both.
|
|
130
|
-
- **Settings command namespace.**
|
|
131
|
-
Confirm the final command name/namespace for the extracted settings surface (`/subagents:settings` vs another form) against how sibling packages register namespaced commands.
|
|
132
|
-
|
|
133
|
-
The agent create/edit surfaces are **not** open questions: both are removed (Decision C).
|
|
134
|
-
|
|
135
|
-
## Addendum (2026-06-20): Phase 19 entry-criteria answers ([#446])
|
|
136
|
-
|
|
137
|
-
The Phase 19 Step 1 spike ([#446]) resolved all four entry criteria.
|
|
138
|
-
Evidence comes from the bundled `@earendil-works/pi-coding-agent` SDK surface (`packages/pi-subagents/node_modules/@earendil-works/pi-coding-agent/dist`) and a throwaway vitest harness run against a **real child session JSONL** (a 43-entry subagent session: 1 `session` header carrying a `parentSession` backref, 1 `model_change`, 1 `thinking_level_change`, 40 `message` entries).
|
|
139
|
-
The harness was discarded after observation; no production source changed.
|
|
140
|
-
|
|
141
|
-
### Finding 0 — `loadEntriesFromFile` is not part of the package's public surface
|
|
142
|
-
|
|
143
|
-
The original "Relevant Pi SDK surface" section cited `loadEntriesFromFile` as the read-only alternative to a switch.
|
|
144
|
-
The spike found it is **not reachable** from `@earendil-works/pi-coding-agent`, and that this is not a types/runtime mismatch — the type barrel and the runtime barrel agree, both omitting it.
|
|
145
|
-
`loadEntriesFromFile` is defined in the deep module `core/session-manager.ts` (annotated `/** Exported for testing */`), but the public barrel `src/index.ts` (→ `dist/index.d.ts` + `dist/index.js`) re-exports only a curated subset of that module — including `parseSessionEntries` but **not** `loadEntriesFromFile`.
|
|
146
|
-
The `package.json` `exports` map exposes only `"."` → the barrel, so the deep import `@earendil-works/pi-coding-agent/dist/core/session-manager.js` is not a supported entry point either.
|
|
147
|
-
`tsc` correctly rejects `import { loadEntriesFromFile } from "@earendil-works/pi-coding-agent"` with `TS2305: Module … has no exported member 'loadEntriesFromFile'`; the throwaway Vitest harness only reached a runtime `is not a function` because esbuild strips types without type-checking (the package's own `pnpm run check` would have caught it at compile time).
|
|
148
|
-
This is not version-specific: the barrel omits it identically in both the pinned `0.79.1` and the latest `0.79.8`, so an SDK upgrade does not surface it — Step 4 should not chase one.
|
|
149
|
-
The viable read-only path is therefore `parseSessionEntries(readFileSync(outputFile, "utf8"))` — `parseSessionEntries` _is_ public (both types and runtime) — which the harness confirmed returns the full `FileEntry[]` transcript with no session switch and no active-session mutation.
|
|
150
|
-
Step 4 ([#445]) should read the file itself and call `parseSessionEntries`, not `loadEntriesFromFile`.
|
|
151
|
-
|
|
152
|
-
Upstream references:
|
|
153
|
-
|
|
154
|
-
- Barrel that omits it: [`packages/coding-agent/src/index.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/index.ts).
|
|
155
|
-
- Test-annotated definition: [`packages/coding-agent/src/core/session-manager.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/core/session-manager.ts).
|
|
156
|
-
|
|
157
|
-
### Finding 1 — the read-only transcript renders entirely on public Pi APIs (no bespoke renderer)
|
|
158
|
-
|
|
159
|
-
The read-only path returns raw `FileEntry[]`, so _something_ must render them.
|
|
160
|
-
The spike found that every piece needed to turn those entries into a Pi-standard transcript is already re-exported through the public root barrel — so Step 4 wires Pi's own machinery rather than re-implementing the formatting the bespoke `ConversationViewer`/`message-formatters.ts` carried.
|
|
161
|
-
This matters because ADR-0004 Decision B's `switchSession` sketch implied "Pi owns the viewer"; the read-only path keeps that property at the _component_ level (Pi renders each entry) without the active-session takeover.
|
|
162
|
-
|
|
163
|
-
The verified public pipeline (all symbols confirmed present in `dist/index.d.ts`, the root barrel):
|
|
164
|
-
|
|
165
|
-
1. **Load** — `parseSessionEntries(readFileSync(record.outputFile, "utf8")): FileEntry[]` (Finding 0).
|
|
166
|
-
2. **Bridge** — `buildSessionContext(entries, leafId?, byId?): SessionContext` turns the entries into `{ messages: AgentMessage[], thinkingLevel, model }`, handling tree traversal, compaction, and branch summaries along the path.
|
|
167
|
-
Note `buildSessionContext` takes `SessionEntry[]`, so drop the leading `SessionHeader` (`type: "session"`) that `parseSessionEntries` includes.
|
|
168
|
-
3. **Render** — one of:
|
|
169
|
-
- **Text:** `serializeConversation(messages: Message[]): string` (from `core/compaction`) for a plain-text dump.
|
|
170
|
-
- **TUI:** the per-entry components `AssistantMessageComponent`, `UserMessageComponent`, `ToolExecutionComponent`, `BashExecutionComponent`, `CompactionSummaryMessageComponent`, `BranchSummaryMessageComponent`, `CustomMessageComponent`, `SkillInvocationMessageComponent` (from `modes/interactive/components`), plus `renderDiff`, for a scrollable native transcript.
|
|
171
|
-
|
|
172
|
-
Unlike `loadEntriesFromFile`, all of these _are_ public (both types and runtime).
|
|
173
|
-
So the read-only viewer is buildable end-to-end on supported APIs, and ADR-0004 Decision B's "keep the core free of transcript-rendering code" holds — the rendering is Pi's, imported, not hand-rolled.
|
|
174
|
-
|
|
175
|
-
Upstream references:
|
|
176
|
-
|
|
177
|
-
- Component barrel: [`packages/coding-agent/src/modes/interactive/components/index.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/modes/interactive/components/index.ts).
|
|
178
|
-
- `serializeConversation`: [`packages/coding-agent/src/core/compaction/utils.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/core/compaction/utils.ts).
|
|
179
|
-
|
|
180
|
-
### Criterion 1 — Root-continuity during a session switch: avoid the switch
|
|
181
|
-
|
|
182
|
-
`switchSession` is a full active-session takeover: it fires `session_before_switch` (cancellable) and then tears the current runtime down via `session_shutdown` (whose `targetSessionFile` field marks a replacement-driven shutdown).
|
|
183
|
-
The root's in-flight turn does **not** survive the takeover — the runtime that owns that turn is invalidated — and a "return to root" would require a second `switchSession(rootSessionFile)` that re-incurs the teardown on the way back.
|
|
184
|
-
Because background agents run precisely while the operator keeps working at root, a true `switchSession` round-trip is hostile to a root with a turn in flight.
|
|
185
|
-
|
|
186
|
-
**Answer:** do not use `switchSession` for navigation.
|
|
187
|
-
The read-only transcript path (Criterion 2) sidesteps root-continuity entirely — it never touches the active session, so there is no return gesture to get wrong.
|
|
188
|
-
|
|
189
|
-
### Criterion 2 — View-only vs interactive: read-only
|
|
190
|
-
|
|
191
|
-
`ReplacedSessionContext` (handed to a `switchSession` `withSession` callback) extends `ExtensionCommandContext` and exposes `sendUserMessage`/`sendMessage`, so a switched-to child session is interactive.
|
|
192
|
-
But operator visibility (concern 3) is framed as "switch in, scroll/read, switch between, exit back to root" — a navigation interaction, not a live steering overlay — and steering already has a home (`steer_subagent` tool / the widget).
|
|
193
|
-
Adding in-session steering would create a second, redundant steering surface.
|
|
194
|
-
|
|
195
|
-
**Answer:** the viewer is strictly **read-only**, loaded via `parseSessionEntries(readFileSync(record.outputFile))` (Finding 0) and rendered through Pi's public entry components (Finding 1) without leaving the root session.
|
|
196
|
-
This also resolves Criterion 1 by construction.
|
|
197
|
-
|
|
198
|
-
### Criterion 3 — Parallel-agent navigation: command-first
|
|
199
|
-
|
|
200
|
-
With N background agents running, the operator needs a gesture to pick which child to view.
|
|
201
|
-
The background widget (Decision A, [#444]) already represents N parallel agents as a per-agent tree, making it the natural eventual selection surface; a flat command gives a non-widget entry point that lists running background agents and lets the operator pick one keyed on `record.outputFile`.
|
|
202
|
-
|
|
203
|
-
**Answer:** Step 4 ([#445]) ships a **command** as the primary, unit-testable selection surface (list background agents → pick → render that child's transcript read-only), with a widget gesture as an optional later enhancement.
|
|
204
|
-
"Both" remains the eventual target; command-first is the Step 4 starting point because it does not depend on the widget shrink ([#444]) landing first.
|
|
205
|
-
|
|
206
|
-
### Criterion 4 — Settings command name: `/subagents-settings`
|
|
207
|
-
|
|
208
|
-
Sibling packages register flat, hyphenated command names with no `:` namespace: `registerCommand("agents", …)` (this package), `"colgrep-reindex"`, `"permission-system"`.
|
|
209
|
-
A `/subagents:settings` form would be inconsistent with every existing command in the repo, and `/agents-settings` wrongly implies it manages agent definitions (which Decision C removes).
|
|
210
|
-
|
|
211
|
-
**Answer:** confirm **`/subagents-settings`** (flat, hyphenated) for Step 2 ([#447]).
|
|
212
|
-
Reject the tentative `/subagents:settings` and the `/agents-settings` alternative.
|
|
213
|
-
|
|
214
|
-
### Net mechanism for Phase 19
|
|
215
|
-
|
|
216
|
-
- Session navigation (Step 4, [#445]): a read-only transcript, surfaced through a flat command; no `switchSession`, no `loadEntriesFromFile`.
|
|
217
|
-
The pipeline is `parseSessionEntries(readFileSync(record.outputFile, "utf8"))` → drop the `SessionHeader` → `buildSessionContext(...).messages` → render via Pi's public entry components (`AssistantMessageComponent` / `ToolExecutionComponent` / … ) or `serializeConversation` (Findings 0 and 1).
|
|
218
|
-
- Settings command (Step 2, [#447]): `/subagents-settings`.
|
|
219
|
-
|
|
220
|
-
This keeps bespoke transcript rendering out of the package (rendering is Pi's own public components), adds no inbound call from the UI to the core, and preserves the Phase 18 spine invariants (#422–#425).
|
|
221
|
-
|
|
222
|
-
## Addendum 2 (2026-06-20): Step 4 sourcing — live record plus file snapshot ([#445])
|
|
223
|
-
|
|
224
|
-
This addendum revises the first addendum's Criterion 2 answer for Step 4 ([#445]).
|
|
225
|
-
The "strictly read-only, file-only" sourcing and the "not a live overlay" framing are superseded by the dual-source decision below; everything else from the spike stands.
|
|
226
|
-
|
|
227
|
-
**Why revise.**
|
|
228
|
-
The spike's mechanism — `parseSessionEntries(readFileSync(record.outputFile))` read once — serves a _completed_ subagent well but is a frozen snapshot for a _running_ one: it shows only what was flushed to disk at open time and does not stream.
|
|
229
|
-
The clarified desired behavior is to see the **live** activity of any subagent _and_ the activity of a completed subagent.
|
|
230
|
-
The bespoke `ConversationViewer` being removed in Step 5 already delivers both — it subscribes to the live in-memory record (`record.subscribeToUpdates()`) and renders `record.messages` plus a streaming indicator built from `record.activeTools` / `record.responseText`.
|
|
231
|
-
That liveness comes from the in-memory record, not the persisted file, so a file-only viewer drops it.
|
|
232
|
-
|
|
233
|
-
**Decision — dual-source, one renderer.**
|
|
234
|
-
Step 4 sources the transcript by liveness and renders both sources through the same Pi public entry components (Finding 1 holds — no bespoke renderer):
|
|
235
|
-
|
|
236
|
-
- **Tracked agent (still in `manager.listAgents()`)** — render from the live in-memory record: `record.messages` for history, `record.subscribeToUpdates()` to re-render on streaming updates, and `record.activeTools` / `record.responseText` for the running-agent streaming indicator.
|
|
237
|
-
This is live.
|
|
238
|
-
- **Evicted / untracked agent** — render from the file snapshot: `parseSessionEntries(readFileSync(record.outputFile, "utf8"))` → drop the `SessionHeader` → `buildSessionContext(...).messages` (Findings 0 and 1).
|
|
239
|
-
|
|
240
|
-
Both sources yield `AgentMessage[]`, so a single Pi-component renderer serves both.
|
|
241
|
-
|
|
242
|
-
**Type-boundary note.**
|
|
243
|
-
`SubagentSession.messages` is deliberately widened to `readonly unknown[]` at the core boundary (`src/lifecycle/subagent-session.ts`), even though the underlying `_session.messages` is the SDK's `AgentMessage[]`.
|
|
244
|
-
Step 4 should add a typed accessor that returns `AgentMessage[]` (or narrow at the boundary) rather than feeding `unknown[]` into Pi's components.
|
|
245
|
-
This is a read accessor on the existing record — it adds no inbound call from the UI to the core and does not regress the Phase 18 spine invariants.
|
|
246
|
-
|
|
247
|
-
**Still read-only (non-interactive).**
|
|
248
|
-
The viewer remains strictly non-interactive: Criterion 2's anti-redundant-steering rationale stands (steering lives in the `steer_subagent` tool and the widget), and Criterion 1's rejection of `switchSession` is unchanged — "live" here means the in-memory subscription, not an active-session takeover.
|
|
249
|
-
|
|
250
|
-
**Candidate set (revises Criterion 3).**
|
|
251
|
-
The selection command lists **any subagent with a live record or a persisted session file** — foreground agents included, not only running background agents.
|
|
252
|
-
This matches the bespoke viewer's current reach (gated on `record.isSessionReady()`, never background-filtered) and avoids an interim regression.
|
|
253
|
-
A foreground agent is navigable only _after_ it completes — while it runs, the root turn is blocked on it — whereas a background agent is navigable live.
|
|
254
|
-
The background widget ([#444]) remains the optional secondary selection gesture for background agents; the command is the primary, unit-testable surface.
|
|
255
|
-
|
|
256
|
-
**Evicted-agent candidate set ([#463]).**
|
|
257
|
-
Step 4b realizes the file-snapshot branch for fully-evicted agents.
|
|
258
|
-
The candidate set is broadened via **manager-retained descriptors**, not a directory scan of the tasks directory: the persisted child session carries no subagent `type`/`description` (those live only on the in-memory record), so a scan yields degraded labels and parses every file per picker open.
|
|
259
|
-
The cleanup sweep instead stashes a lightweight `EvictedSubagent` descriptor (label fields + `outputFile`, no messages) before disposing a record, preserving rich labels and bounded memory.
|
|
260
|
-
This covers in-session evictions — the sweep's only targets, since a fresh manager per session never reloads prior-process subagents.
|
|
261
|
-
The "render from the file snapshot" mechanism for an evicted agent (above) is unchanged; only the candidate-set _enumeration_ is pinned to descriptors.
|
|
262
|
-
|
|
263
|
-
## Addendum 3 (2026-06-23): adopt the `subagents:` colon namespace, superseding Criterion 4
|
|
264
|
-
|
|
265
|
-
This addendum reverses **Criterion 4**, which confirmed flat, hyphenated command names (`/subagents-settings`) and rejected `/subagents:settings`.
|
|
266
|
-
The two registered commands are renamed: `/subagents-settings` → `/subagents:settings` and `/subagent-sessions` → `/subagents:sessions`.
|
|
267
|
-
This is a breaking change to the command surface.
|
|
268
|
-
|
|
269
|
-
**Why revise.**
|
|
270
|
-
Criterion 4 surveyed only in-repo siblings (`agents`, `colgrep-reindex`, `permission-system`) and concluded a `:` namespace would be inconsistent with every existing command.
|
|
271
|
-
That survey missed the broader Pi ecosystem: `@eko24ive/pi-ask` establishes the colon convention with `answer:again` and `ask:replay`, grouping a package's commands under a shared prefix.
|
|
272
|
-
The colon namespace reads as a deliberate grouping gesture (`subagents:settings`, `subagents:sessions` clearly belong to one package) where the hyphen form blurs into an ordinary command name.
|
|
273
|
-
The newer ecosystem signal outweighs the original in-repo-only consistency argument.
|
|
274
|
-
|
|
275
|
-
**Scope.**
|
|
276
|
-
This change is pi-subagents-only.
|
|
277
|
-
Whether the colon convention becomes repo-wide (renaming `colgrep-reindex`, `permission-system`, etc.) is deferred — not decided here.
|
|
278
|
-
`/agents` is intentionally left flat: it is slated for removal in the Phase 18 / Step 5 menu retirement, so namespacing it would be churn on a command being deleted.
|
|
65
|
+
The core owns run state and behavior; UI owns transient presentation and selection.
|
|
66
|
+
Native Pi components preserve transcript fidelity without maintaining a second
|
|
67
|
+
rendering system. Read-only navigation preserves parent continuity, and keeping
|
|
68
|
+
agent-definition editing outside the package avoids duplicating tools the operator
|
|
69
|
+
already has.
|
|
279
70
|
|
|
280
|
-
[
|
|
281
|
-
|
|
282
|
-
[#446]: https://github.com/gotgenes/pi-packages/issues/446
|
|
283
|
-
[#447]: https://github.com/gotgenes/pi-packages/issues/447
|
|
284
|
-
[#463]: https://github.com/gotgenes/pi-packages/issues/463
|
|
71
|
+
[Architecture](../architecture/architecture.md) owns the detailed lifecycle,
|
|
72
|
+
retention, observation, and transcript-query boundaries.
|