@gotgenes/pi-subagents 11.5.0 → 12.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.
@@ -0,0 +1,106 @@
1
+ ---
2
+ issue: 270
3
+ issue_title: "Make @gotgenes/pi-subagents type-consumable by sibling workspace packages"
4
+ ---
5
+
6
+ # Retro: #270 — Make @gotgenes/pi-subagents type-consumable by sibling workspace packages
7
+
8
+ ## Stage: Planning (2026-05-29T00:00:00Z)
9
+
10
+ ### Session summary
11
+
12
+ Diagnosed the consumability failure empirically with `tsc --traceResolution` and planned a `.d.ts`-emit fix.
13
+ The plan adds a `rollup-plugin-dts` build that bundles `src/service/service.ts` into a self-contained `dist/public.d.ts`, wires conditional `exports` (`types` → the bundle, `default` → the real source), generates the artifact at `prepack` time, ships it via a `files` allowlist, and proves external consumability with a `pnpm pack` → throwaway-consumer → `tsc` harness.
14
+
15
+ ### Observations
16
+
17
+ - Root cause is two compounding failures: the stale `exports["."]` path (`./src/service.ts` does not exist) and, once fixed, an unresolvable `#src/*` cascade.
18
+ The trace showed the consumer's own `paths` (`#src/*` → `./src/*`) intercept first (both packages define `#src/*`), and the publisher's `imports`-field fallback cannot resolve the extensionless `.ts` target.
19
+ - The public type closure is entangled: `WorkspaceProvider` → `AgentStatus` (in the 510-LOC `agent.ts`) → `types.ts` (which re-exports the `Agent` class).
20
+ This made the alias-free-entry alternative (Option 2) a substantial source restructure, so it was rejected.
21
+ - Decisions taken via `ask_user`:
22
+ 1. Approach — emit a bundled `.d.ts` (the repo's first build step), over alias-free restructure or type re-declaration.
23
+ 2. Bundler — `rollup-plugin-dts` (purpose-built for flattening declarations; no JS bundle, which suits ship-source), over `tsdown`/`api-extractor`.
24
+ 3. Artifact — not committed; generated at `prepack` and shipped in the tarball, consumed via the package interface.
25
+ 4. Scope — tight: packaging + a `pnpm pack`-based verification harness in #270; defer the `pi-subagents-worktrees` registry-consumption flip (drop `workspace:*`, `link-workspace-packages: false`, wire the real import) to #263.
26
+ - Scope was deliberately narrowed after a chicken-and-egg surfaced: the registry version carrying the fix does not exist until #270 publishes, so the meaningful consumer flip belongs to #263.
27
+ - Sequencing constraint for #263 (captured in the plan): publish #270 first — merge its release-please PR so `pi-subagents` publishes — *before* resuming #263, otherwise #263's `pi-subagents` core edits batch into the same release.
28
+ The current `#263` scaffold commits on `main` touch only the unregistered `pi-subagents-worktrees` component, so they do not batch into `pi-subagents` and #270 ships cleanly.
29
+ - Primary feasibility risk flagged: whether `rollup-plugin-dts` resolves `#src/*` while rolling up the type graph.
30
+ Build Step 1 is the explicit checkpoint (emit + assert the output is alias-free and exports the expected symbols).
31
+ - `dist/` is gitignored and already excluded by eslint/biome; the new wrinkle is that a `files` allowlist is required so the gitignored `dist/public.d.ts` is included in the npm tarball — validate `pnpm pack --dry-run` parity so no currently-shipped file is dropped.
32
+
33
+ ## Stage: Implementation — Build (2026-05-29T00:00:00Z)
34
+
35
+ ### Session summary
36
+
37
+ Executed all four build-order steps: added `rollup` + `rollup-plugin-dts` and a `build:types` script that bundles `src/service/service.ts` into a self-contained `dist/public.d.ts`; wired conditional `exports` (`types` + `default`, fixing the stale path) with a `prepack` hook and a `files` allowlist; added a `pnpm pack` → throwaway-consumer → `tsc` verification harness (`scripts/verify-public-types.sh`) plus a CI step; and recorded ADR 0003.
38
+ A fifth commit documented the new build step in the `package-pi-subagents` skill (reviewer WARN).
39
+ Root `pnpm run check`, root `pnpm run lint`, and `verify:public-types` all pass.
40
+
41
+ ### Observations
42
+
43
+ - The primary feasibility risk (`rollup-plugin-dts` resolving `#src/*`) resolved cleanly out of the box: driving it with the package `tsconfig` (which carries the `#src/*` paths) produced a 178-line `dist/public.d.ts` with zero `#src/` residue and only `ThinkingLevel` kept external from `@earendil-works/pi-ai`.
44
+ No alias/path resolver plugin was needed.
45
+ - Harness deviation (fixed in the same step): `pnpm add` in the isolated (`--ignore-workspace`) throwaway consumer exited non-zero with `ERR_PNPM_IGNORED_BUILDS` because it does not inherit the workspace `allowBuilds` approvals (`@google/genai`, `protobufjs`).
46
+ Fixed by adding `--ignore-scripts` — a type-check needs no dependency build scripts.
47
+ Worth remembering for any future packaged-consumer harness.
48
+ - A subtle gotcha while debugging: `pnpm ... | tail; echo $?` reports `tail`'s exit, not pnpm's, which masked the real failure.
49
+ Use `set -o pipefail` or check the command directly.
50
+ - `files` allowlist parity was validated with a before/after `pnpm pack --dry-run` diff: nothing dropped, only `dist/public.d.ts` added.
51
+ The allowlist reproduces the current contents (`src`, `docs`, `vitest.config.ts`, `AGENTS.md`, `CHANGELOG.md`, `.prettierignore`) plus `dist`.
52
+ Did not take the opportunity to slim the tarball (docs/test-config still ship) — that would be a separate deliberate change.
53
+ - Runtime `default` → `./src/service/service.ts` is safe because that module's only internal imports are `import type`, which erase; no runtime `#src/*` resolution occurs.
54
+ - No `src/`/`test/` `.ts` files were touched, so the vitest suite and `tsc` were unaffected (confirmed via root check).
55
+ - Pre-completion reviewer: WARN — no findings attributable to this session.
56
+ Reviewer warnings: (1) the `package-pi-subagents` skill lacked a build-step note — addressed in commit `2ff5a375`; (2) `pnpm fallow dead-code` exits non-zero on a pre-existing finding in `packages/pi-subagents-worktrees/package.json` from the #263 scaffold (commit `9a7dcfc5`), out of scope for #270 and left for #263.
57
+
58
+ ## Stage: Final Retrospective (2026-05-29T21:00:00Z)
59
+
60
+ ### Session summary
61
+
62
+ Shipped #270 end-to-end across planning, build, and ship stages: diagnosed the cross-package type-resolution failure empirically, built a `rollup-plugin-dts` declaration bundle plus a pack-based verification harness, and published `@gotgenes/pi-subagents@11.6.0` (tag `pi-subagents-v11.6.0`).
63
+ Two CI failures during the ship stage — a pre-existing `pnpm fallow dead-code` gate and lockfile drift — required two extra fix commits before CI went green.
64
+
65
+ ### Observations
66
+
67
+ #### What went well
68
+
69
+ - Empirical-first diagnosis: `tsc --traceResolution` in planning pinned the exact two-part failure (consumer `paths` collision + the publisher's `imports`-field extensionless-`.ts` miss) and directly justified the chosen `.d.ts`-emit approach over the alias-free restructure.
70
+ - The flagged primary risk evaporated: `rollup-plugin-dts` resolved `#src/*` out of the box via the package `tsconfig` paths, producing a clean 178-line `dist/public.d.ts` with no resolver plugin.
71
+ - Novel, reusable pattern: `scripts/verify-public-types.sh` proves a ship-source package is externally type-consumable via `pnpm pack` → throwaway-consumer → `tsc`, with no publish round-trip.
72
+ Worth promoting if other packages grow public surfaces.
73
+ - Disciplined `ask_user` use on the genuinely ambiguous decisions (approach, bundler, artifact handling, scope), with strong user steering — the `tsup`-is-unmaintained redirect to `rollup-plugin-dts`, the "no workspace trickery / use released versions" directive, and the #263 chicken-and-egg catch that correctly narrowed scope.
74
+
75
+ #### What caused friction (agent side)
76
+
77
+ - `missing-context` — Pushed to `main` with a pre-existing `pnpm fallow dead-code` failure (unused `@earendil-works/pi-coding-agent` devDependency in `packages/pi-subagents-worktrees/package.json`, from the #263 scaffold).
78
+ The pre-completion reviewer reported it as `FAIL` but labelled it out-of-scope, and I accepted that framing and pushed.
79
+ The CI `Fallow dead-code gate` runs `if: github.ref == 'refs/heads/main'` — a hard gate that fires on every `main` push regardless of who introduced the failure — so CI failed (run `26659647270`).
80
+ Impact: 2 fix commits (`7e7afadd`, `10e74f2f`) and 2 extra CI cycles (~10 min).
81
+ The ship pre-push step runs only `pnpm run lint`, never `pnpm fallow dead-code`.
82
+ - `missing-context` — Removed the devDependency and committed/pushed `package.json` (`7e7afadd`) without the updated `pnpm-lock.yaml`.
83
+ CI's `pnpm install --frozen-lockfile` failed with `ERR_PNPM_OUTDATED_LOCKFILE` (run `26659851716`).
84
+ Impact: 1 extra commit (`10e74f2f`) and 1 extra CI cycle.
85
+ Self-identified from the CI log.
86
+ - `rabbit-hole` (minor) — While debugging the harness's `ERR_PNPM_IGNORED_BUILDS`, the `pnpm ... | tail; echo $?` idiom reported `tail`'s exit code, masking pnpm's real failure; took ~4 tool calls before tracing with `bash -x`.
87
+ Impact: added friction, no rework.
88
+
89
+ #### What caused friction (user side)
90
+
91
+ - The "pi-subagents-* extensions should use the released, npm-installed version, no workspace trickery" directive arrived mid-planning, after initial exploration.
92
+ Surfacing the consumption-model constraint at kickoff would have framed the scope question earlier.
93
+ Opportunity, not criticism — the same exchange produced the high-value chicken-and-egg catch (the registry version with the fix cannot exist until #270 publishes) that correctly deferred the worktrees flip to #263.
94
+ - A brief "there is no ADR 0003" → "My mistake" exchange; no rework.
95
+
96
+ ### Diagnostic details
97
+
98
+ - Model-performance correlation — the lone subagent dispatch (`pre-completion-reviewer`) ran on `anthropic/claude-sonnet-4-6`, appropriate for judgment-heavy review.
99
+ The dead-code-gate framing miss was a protocol-scope issue (pre-existing vs blocking), not a model-capability mismatch.
100
+ - Escalation-delay — no error sequence exceeded 5 consecutive tool calls; the harness `ERR_PNPM_IGNORED_BUILDS` resolved in ~4.
101
+ - Feedback-loop gap — build-stage verification ran incrementally after each step (good); the gap was at ship: the pre-push check omits the `main`-only gates (`pnpm fallow dead-code`) and lockfile validation that CI enforces, so a locally-clean `pnpm run lint` still failed CI twice.
102
+
103
+ ### Changes made
104
+
105
+ 1. `.pi/prompts/ship-issue.md` — renamed Step 2 to "Pre-push checks" and added `pnpm fallow dead-code` alongside `pnpm run lint`, with a one-line note that the gate is `main`-only and blocks pushes regardless of who introduced the failure.
106
+ 2. `AGENTS.md` (§ Code Style pnpm rules) — added a rule to run `pnpm install` and commit the updated `pnpm-lock.yaml` in the same commit when a `package.json` dependency changes, since CI installs with `--frozen-lockfile`.
package/package.json CHANGED
@@ -1,10 +1,22 @@
1
1
  {
2
2
  "name": "@gotgenes/pi-subagents",
3
- "version": "11.5.0",
3
+ "version": "12.0.0",
4
4
  "type": "module",
5
5
  "exports": {
6
- ".": "./src/service.ts"
6
+ ".": {
7
+ "types": "./dist/public.d.ts",
8
+ "default": "./src/service/service.ts"
9
+ }
7
10
  },
11
+ "files": [
12
+ "src",
13
+ "dist",
14
+ "docs",
15
+ "vitest.config.ts",
16
+ "AGENTS.md",
17
+ "CHANGELOG.md",
18
+ ".prettierignore"
19
+ ],
8
20
  "imports": {
9
21
  "#src/*": "./src/*",
10
22
  "#test/*": "./test/*"
@@ -51,6 +63,8 @@
51
63
  "@earendil-works/pi-coding-agent": "0.75.4",
52
64
  "@earendil-works/pi-tui": "0.75.4",
53
65
  "@types/node": "^22.15.3",
66
+ "rollup": "^4.60.4",
67
+ "rollup-plugin-dts": "^6.4.1",
54
68
  "rumdl": "^0.1.93",
55
69
  "typescript": "^6.0.3",
56
70
  "vitest": "^4.1.5"
@@ -64,6 +78,8 @@
64
78
  },
65
79
  "scripts": {
66
80
  "check": "tsc --noEmit",
81
+ "build:types": "rollup -c rollup.dts.config.mjs",
82
+ "verify:public-types": "bash scripts/verify-public-types.sh",
67
83
  "test": "vitest run",
68
84
  "test:watch": "vitest",
69
85
  "lint:md": "rumdl check *.md docs/**/*.md",
@@ -68,7 +68,6 @@ function loadFromDir(dir: string, agents: Map<string, AgentConfig>, source: "pro
68
68
  inheritContext: fm.inherit_context != null ? fm.inherit_context === true : undefined,
69
69
  runInBackground: fm.run_in_background != null ? fm.run_in_background === true : undefined,
70
70
  isolated: fm.isolated != null ? fm.isolated === true : undefined,
71
- isolation: fm.isolation === "worktree" ? "worktree" : undefined,
72
71
  enabled: fm.enabled !== false, // default true; explicitly false disables
73
72
  source,
74
73
  });
@@ -1,4 +1,4 @@
1
- import type { AgentConfig, IsolationMode, ThinkingLevel } from "#src/types";
1
+ import type { AgentConfig, ThinkingLevel } from "#src/types";
2
2
 
3
3
  interface AgentInvocationParams {
4
4
  model?: string;
@@ -7,7 +7,6 @@ interface AgentInvocationParams {
7
7
  run_in_background?: boolean;
8
8
  inherit_context?: boolean;
9
9
  isolated?: boolean;
10
- isolation?: IsolationMode;
11
10
  }
12
11
 
13
12
  export function resolveAgentInvocationConfig(
@@ -21,7 +20,6 @@ export function resolveAgentInvocationConfig(
21
20
  inheritContext: boolean;
22
21
  runInBackground: boolean;
23
22
  isolated: boolean;
24
- isolation?: IsolationMode;
25
23
  } {
26
24
  return {
27
25
  modelInput: agentConfig?.model ?? params.model,
@@ -31,6 +29,5 @@ export function resolveAgentInvocationConfig(
31
29
  inheritContext: agentConfig?.inheritContext ?? params.inherit_context ?? false,
32
30
  runInBackground: agentConfig?.runInBackground ?? params.run_in_background ?? false,
33
31
  isolated: agentConfig?.isolated ?? params.isolated ?? false,
34
- isolation: agentConfig?.isolation ?? params.isolation,
35
32
  };
36
33
  }
package/src/index.ts CHANGED
@@ -28,7 +28,6 @@ import { ConcreteAgentRunner, type RunnerDeps } from "#src/lifecycle/agent-runne
28
28
  import { createChildLifecyclePublisher } from "#src/lifecycle/child-lifecycle";
29
29
  import { ConcurrencyQueue } from "#src/lifecycle/concurrency-queue";
30
30
  import { buildParentSnapshot } from "#src/lifecycle/parent-snapshot";
31
- import { GitWorktreeManager } from "#src/lifecycle/worktree";
32
31
  import { buildEventData, type NotificationDetails, NotificationManager } from "#src/observation/notification";
33
32
  import { createNotificationRenderer } from "#src/observation/renderer";
34
33
  import { createSubagentRuntime } from "#src/runtime";
@@ -166,7 +165,6 @@ export default function (pi: ExtensionAPI) {
166
165
 
167
166
  const manager = new AgentManager({
168
167
  runner: new ConcreteAgentRunner(runnerDeps),
169
- worktrees: new GitWorktreeManager(process.cwd()),
170
168
  baseCwd: process.cwd(),
171
169
  observer,
172
170
  queue,
@@ -14,11 +14,9 @@ import type { AgentRunner } from "#src/lifecycle/agent-runner";
14
14
  import type { ConcurrencyQueue } from "#src/lifecycle/concurrency-queue";
15
15
  import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
16
16
  import type { WorkspaceProvider } from "#src/lifecycle/workspace";
17
- import type { WorktreeManager } from "#src/lifecycle/worktree";
18
- import { WorktreeIsolation } from "#src/lifecycle/worktree-isolation";
19
17
 
20
18
  import type { RunConfig } from "#src/runtime";
21
- import type { AgentInvocation, CompactionInfo, IsolationMode, ParentSessionInfo, SubagentType, ThinkingLevel } from "#src/types";
19
+ import type { AgentInvocation, CompactionInfo, ParentSessionInfo, SubagentType, ThinkingLevel } from "#src/types";
22
20
 
23
21
  /** Observer interface for agent lifecycle notifications. */
24
22
  export interface AgentManagerObserver {
@@ -31,7 +29,6 @@ export interface AgentManagerObserver {
31
29
 
32
30
  export interface AgentManagerOptions {
33
31
  runner: AgentRunner;
34
- worktrees: WorktreeManager;
35
32
  /** Concurrency queue — owns scheduling, limit checks, and drain logic. */
36
33
  queue: ConcurrencyQueue;
37
34
  /** Base working directory handed to a workspace provider (the parent cwd). */
@@ -54,8 +51,6 @@ export interface AgentSpawnConfig {
54
51
  * callers (e.g. cross-extension RPC) that must not be deferred by the queue.
55
52
  */
56
53
  bypassQueue?: boolean;
57
- /** Isolation mode - "worktree" creates a temp git worktree for the agent. */
58
- isolation?: IsolationMode;
59
54
  /** Resolved invocation snapshot captured for UI display. */
60
55
  invocation?: AgentInvocation;
61
56
  /** Parent abort signal - when aborted, the subagent is also stopped. */
@@ -71,7 +66,6 @@ export class AgentManager {
71
66
  private cleanupInterval: ReturnType<typeof setInterval>;
72
67
  private readonly observer?: AgentManagerObserver;
73
68
  private readonly runner: AgentRunner;
74
- private readonly worktrees: WorktreeManager;
75
69
  private readonly queue: ConcurrencyQueue;
76
70
  private readonly baseCwd: string;
77
71
  private getRunConfig?: () => RunConfig;
@@ -84,7 +78,6 @@ export class AgentManager {
84
78
 
85
79
  constructor(options: AgentManagerOptions) {
86
80
  this.runner = options.runner;
87
- this.worktrees = options.worktrees;
88
81
  this.queue = options.queue;
89
82
  this.baseCwd = options.baseCwd;
90
83
  this.observer = options.observer;
@@ -162,10 +155,6 @@ export class AgentManager {
162
155
  signal: options.signal,
163
156
  // Shared deps
164
157
  runner: this.runner,
165
- worktree:
166
- options.isolation === "worktree"
167
- ? new WorktreeIsolation(this.worktrees, id)
168
- : undefined,
169
158
  observer: this.buildObserver(options),
170
159
  getRunConfig: this.getRunConfig,
171
160
  baseCwd: this.baseCwd,
@@ -322,7 +311,5 @@ export class AgentManager {
322
311
  record.session?.dispose();
323
312
  }
324
313
  this.agents.clear();
325
- // Prune any orphaned git worktrees (crash recovery)
326
- try { this.worktrees.prune(); } catch (err) { debugLog("pruneWorktrees on dispose", err); }
327
314
  }
328
315
  }
@@ -8,11 +8,11 @@
8
8
  * Stats (toolUses, lifetimeUsage, compactionCount) are owned by the class and
9
9
  * accumulated via mutation methods (incrementToolUses, addUsage, incrementCompactions).
10
10
  *
11
- * Behavior (abort, steer buffering, worktree setup) lives on the agent
12
- * rather than on AgentManager — each agent manages its own lifecycle concerns.
11
+ * Behavior (abort, steer buffering) lives on the agent rather than on
12
+ * AgentManager — each agent manages its own lifecycle concerns.
13
13
  *
14
- * Worktree isolation is delegated to an optional WorktreeIsolation collaborator
15
- * (set at construction when isolation is requested); its presence IS the mode.
14
+ * The child's working directory is supplied by a registered WorkspaceProvider
15
+ * (the workspace seam); with no provider the child runs in the parent cwd.
16
16
  *
17
17
  * Phase-specific collaborators (execution, notification) are attached
18
18
  * after construction as lifecycle information becomes available.
@@ -27,7 +27,6 @@ import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
27
27
  import type { LifetimeUsage } from "#src/lifecycle/usage";
28
28
  import { addUsage } from "#src/lifecycle/usage";
29
29
  import type { Workspace, WorkspaceProvider } from "#src/lifecycle/workspace";
30
- import type { WorktreeIsolation } from "#src/lifecycle/worktree-isolation";
31
30
  import { NotificationState } from "#src/observation/notification-state";
32
31
  import { subscribeAgentObserver } from "#src/observation/record-observer";
33
32
  import type { RunConfig } from "#src/runtime";
@@ -70,7 +69,6 @@ export interface AgentInit {
70
69
 
71
70
  // Shared deps (required for run(), optional for tests)
72
71
  runner?: AgentRunner;
73
- worktree?: WorktreeIsolation;
74
72
  observer?: AgentLifecycleObserver;
75
73
  getRunConfig?: () => RunConfig;
76
74
  /** Resolves the registered workspace provider (if any) at run-start. */
@@ -130,8 +128,6 @@ export class Agent {
130
128
 
131
129
  // Shared deps — optional (required for run())
132
130
  private readonly _runner?: AgentRunner;
133
- /** Worktree isolation collaborator — present only when isolation: "worktree". */
134
- readonly worktree?: WorktreeIsolation;
135
131
  readonly observer?: AgentLifecycleObserver;
136
132
  private readonly _getRunConfig?: () => RunConfig;
137
133
  private readonly _getWorkspaceProvider?: () => WorkspaceProvider | undefined;
@@ -192,7 +188,6 @@ export class Agent {
192
188
 
193
189
  // Shared deps
194
190
  this._runner = init.runner;
195
- this.worktree = init.worktree;
196
191
  this.observer = init.observer;
197
192
  this._getRunConfig = init.getRunConfig;
198
193
  this._getWorkspaceProvider = init.getWorkspaceProvider;
@@ -215,8 +210,8 @@ export class Agent {
215
210
  }
216
211
 
217
212
  /**
218
- * Execute the full agent lifecycle: worktree setup, runner invocation,
219
- * session-creation handling, observer wiring, worktree cleanup, and
213
+ * Execute the full agent lifecycle: workspace preparation, runner invocation,
214
+ * session-creation handling, observer wiring, workspace disposal, and
220
215
  * status transitions.
221
216
  *
222
217
  * Requires runner and snapshot to be set at construction.
@@ -236,8 +231,8 @@ export class Agent {
236
231
 
237
232
  let cwd: string | undefined;
238
233
  try {
239
- // Provider-first: a registered workspace provider supplies the cwd and
240
- // owns teardown; otherwise fall back to the legacy worktree collaborator.
234
+ // A registered workspace provider supplies the child's cwd and owns its
235
+ // teardown; with no provider the child runs in the parent cwd.
241
236
  const provider = this._getWorkspaceProvider?.();
242
237
  if (provider) {
243
238
  this._workspace = await provider.prepare({
@@ -247,9 +242,6 @@ export class Agent {
247
242
  invocation: this.invocation,
248
243
  });
249
244
  cwd = this._workspace?.cwd;
250
- } else {
251
- this.worktree?.setup();
252
- cwd = this.worktree?.path;
253
245
  }
254
246
  } catch (err) {
255
247
  this.markError(err);
@@ -463,7 +455,7 @@ export class Agent {
463
455
  this._detachFn = undefined;
464
456
  }
465
457
 
466
- /** Complete a run: release listeners, worktree cleanup, status transition, execution update, notify observer. */
458
+ /** Complete a run: release listeners, dispose the workspace, status transition, execution update, notify observer. */
467
459
  completeRun(result: RunResult): void {
468
460
  this.releaseListeners();
469
461
 
@@ -476,11 +468,6 @@ export class Agent {
476
468
  : "completed";
477
469
  const disposeResult = this._workspace.dispose({ status: finalStatus, description: this.description });
478
470
  if (disposeResult?.resultAddendum) finalResult += disposeResult.resultAddendum;
479
- } else {
480
- const wtResult = this.worktree?.cleanup(this.description);
481
- if (wtResult?.hasChanges && wtResult.branch) {
482
- finalResult += `\n\n---\nChanges saved to branch \`${wtResult.branch}\`. Merge with: \`git merge ${wtResult.branch}\``;
483
- }
484
471
  }
485
472
 
486
473
  if (result.aborted) this.markAborted(finalResult);
@@ -495,14 +482,13 @@ export class Agent {
495
482
  this.observer?.onRunFinished?.(this);
496
483
  }
497
484
 
498
- /** Fail a run: mark error, release listeners, best-effort worktree cleanup, notify observer. */
485
+ /** Fail a run: mark error, release listeners, best-effort workspace dispose, notify observer. */
499
486
  failRun(err: unknown): void {
500
487
  this.markError(err);
501
488
  this.releaseListeners();
502
489
 
503
490
  try {
504
491
  if (this._workspace) this._workspace.dispose({ status: "error", description: this.description });
505
- else this.worktree?.cleanup(this.description);
506
492
  } catch (cleanupErr) { debugLog("workspace dispose on agent error", cleanupErr); }
507
493
 
508
494
  this.observer?.onRunFinished?.(this);
@@ -69,7 +69,6 @@ export class SubagentsServiceAdapter implements SubagentsService {
69
69
  isolated: options?.isolated,
70
70
  inheritContext: options?.inheritContext,
71
71
  bypassQueue: options?.bypassQueue,
72
- isolation: options?.isolation,
73
72
  isBackground,
74
73
  });
75
74
  }
@@ -134,8 +133,6 @@ export function toSubagentRecord(record: Agent): SubagentRecord {
134
133
  if (record.result !== undefined) out.result = record.result;
135
134
  if (record.error !== undefined) out.error = record.error;
136
135
  if (record.completedAt !== undefined) out.completedAt = record.completedAt;
137
- const worktreeResult = record.worktree?.cleanupResult;
138
- if (worktreeResult !== undefined) out.worktreeResult = worktreeResult;
139
136
 
140
137
  return out;
141
138
  }
@@ -14,8 +14,9 @@ import type { WorkspaceProvider } from "#src/lifecycle/workspace";
14
14
 
15
15
  // Generative extension seam (ADR 0002, Phase 16 Step 2). Only the provider
16
16
  // entry-point type is re-exported here; a consumer assigning to
17
- // `WorkspaceProvider` gets `Workspace` and the context types via inference.
18
- // The worktrees package (#263) adds named re-exports when it imports them.
17
+ // `WorkspaceProvider` recovers `Workspace` and the context types via inference
18
+ // (e.g. `Parameters<WorkspaceProvider["prepare"]>[0]`). Named re-exports of
19
+ // those collaborator types are tracked in #272.
19
20
  export type { LifetimeUsage, WorkspaceProvider };
20
21
 
21
22
  export type SubagentStatus =
@@ -40,7 +41,6 @@ export interface SubagentRecord {
40
41
  completedAt?: number;
41
42
  lifetimeUsage: LifetimeUsage;
42
43
  compactionCount: number;
43
- worktreeResult?: { hasChanges: boolean; branch?: string };
44
44
  }
45
45
 
46
46
  /** Options for spawning an agent via the service. */
@@ -53,7 +53,6 @@ export interface SpawnOptions {
53
53
  inheritContext?: boolean;
54
54
  foreground?: boolean;
55
55
  bypassQueue?: boolean;
56
- isolation?: "worktree";
57
56
  }
58
57
 
59
58
  /** The public service contract for cross-extension subagent access. */
@@ -179,7 +179,7 @@ Guidelines:
179
179
  - Use model to specify a different model (as "provider/modelId", or fuzzy e.g. "haiku", "sonnet").
180
180
  - Use thinking to control extended thinking level.
181
181
  - Use inherit_context if the agent needs the parent conversation history.
182
- - Use isolation: "worktree" to run the agent in an isolated git worktree (safe parallel file modifications).`,
182
+ `,
183
183
  parameters: Type.Object({
184
184
  prompt: Type.String({
185
185
  description: "The task for the agent to perform.",
@@ -231,12 +231,6 @@ Guidelines:
231
231
  "If true, fork parent conversation into the agent. Default: false (fresh context).",
232
232
  }),
233
233
  ),
234
- isolation: Type.Optional(
235
- Type.Literal("worktree", {
236
- description:
237
- 'Set to "worktree" to run the agent in a temporary git worktree (isolated copy of the repo). Changes are saved to a branch on completion.',
238
- }),
239
- ),
240
234
  }),
241
235
 
242
236
  // ---- Custom rendering: Claude Code style ----
@@ -52,7 +52,6 @@ export function spawnBackground(
52
52
  inheritContext: execution.inheritContext,
53
53
  thinkingLevel: execution.thinking,
54
54
  isBackground: true,
55
- isolation: execution.isolation,
56
55
  invocation: execution.agentInvocation,
57
56
  observer: {
58
57
  onSessionCreated: (_agent, session) => {
@@ -105,7 +105,6 @@ export async function runForeground(
105
105
  isolated: execution.isolated,
106
106
  inheritContext: execution.inheritContext,
107
107
  thinkingLevel: execution.thinking,
108
- isolation: execution.isolation,
109
108
  invocation: execution.agentInvocation,
110
109
  signal,
111
110
  parentSession: params.parentSession,
@@ -12,7 +12,7 @@ import type { AgentTypeRegistry } from "#src/config/agent-types";
12
12
  import { resolveAgentInvocationConfig } from "#src/config/invocation-config";
13
13
  import { normalizeMaxTurns } from "#src/lifecycle/agent-runner";
14
14
  import { resolveInvocationModel } from "#src/session/model-resolver";
15
- import type { AgentInvocation, IsolationMode, SubagentType, ThinkingLevel } from "#src/types";
15
+ import type { AgentInvocation, SubagentType, ThinkingLevel } from "#src/types";
16
16
  import {
17
17
  type AgentDetails,
18
18
  buildInvocationTags,
@@ -44,7 +44,6 @@ export interface SpawnExecution {
44
44
  inheritContext: boolean;
45
45
  runInBackground: boolean;
46
46
  isolated: boolean;
47
- isolation: IsolationMode | undefined;
48
47
  agentInvocation: AgentInvocation;
49
48
  }
50
49
 
@@ -104,7 +103,6 @@ export function resolveSpawnConfig(
104
103
  const inheritContext = resolvedConfig.inheritContext;
105
104
  const runInBackground = resolvedConfig.runInBackground;
106
105
  const isolated = resolvedConfig.isolated;
107
- const isolation = resolvedConfig.isolation;
108
106
 
109
107
  // Compute display model name (only shown when different from parent)
110
108
  const parentModelId = modelInfo.parentModel?.id;
@@ -125,7 +123,6 @@ export function resolveSpawnConfig(
125
123
  isolated,
126
124
  inheritContext,
127
125
  runInBackground,
128
- isolation,
129
126
  };
130
127
 
131
128
  const modeLabel = getPromptModeLabel(subagentType, registry);
@@ -151,7 +148,6 @@ export function resolveSpawnConfig(
151
148
  inheritContext,
152
149
  runInBackground,
153
150
  isolated,
154
- isolation,
155
151
  agentInvocation,
156
152
  },
157
153
  presentation: { modelName, agentTags, detailBase },
package/src/types.ts CHANGED
@@ -21,9 +21,6 @@ export interface SubscribableSession {
21
21
  /** Agent type: any string name (built-in defaults or user-defined). */
22
22
  export type SubagentType = string;
23
23
 
24
- /** Isolation mode for agent execution. */
25
- export type IsolationMode = "worktree";
26
-
27
24
  /** UI display and agent listing — name, display name, description, prompt mode. */
28
25
  export interface AgentIdentity {
29
26
  name: string;
@@ -55,8 +52,6 @@ export interface AgentConfig extends AgentIdentity, AgentPromptConfig {
55
52
  runInBackground?: boolean;
56
53
  /** Default for spawn: no extension tools. undefined = caller decides. */
57
54
  isolated?: boolean;
58
- /** Isolation mode — "worktree" runs the agent in a temporary git worktree */
59
- isolation?: IsolationMode;
60
55
  /** true = this is an embedded default agent (informational) */
61
56
  isDefault?: boolean;
62
57
  /** false = agent is hidden from the registry */
@@ -73,7 +68,6 @@ export interface AgentInvocation {
73
68
  isolated?: boolean;
74
69
  inheritContext?: boolean;
75
70
  runInBackground?: boolean;
76
- isolation?: IsolationMode;
77
71
  }
78
72
 
79
73
  /**
@@ -54,7 +54,6 @@ export function buildEjectContent(cfg: AgentConfig): string {
54
54
  if (cfg.inheritContext) fmFields.push("inherit_context: true");
55
55
  if (cfg.runInBackground) fmFields.push("run_in_background: true");
56
56
  if (cfg.isolated) fmFields.push("isolated: true");
57
- if (cfg.isolation) fmFields.push(`isolation: ${cfg.isolation}`);
58
57
  return `---\n${fmFields.join("\n")}\n---\n\n${cfg.systemPrompt}\n`;
59
58
  }
60
59
 
@@ -109,7 +109,6 @@ skills: <true (inherit all), false (none), or comma-separated skill names to pre
109
109
  inherit_context: <true to fork parent conversation into agent so it sees chat history. Default: false>
110
110
  run_in_background: <true to run in background by default. Default: false>
111
111
  isolated: <true for no extension/MCP tools, only built-in tools. Default: false>
112
- isolation: <"worktree" to run in isolated git worktree. Omit for normal>
113
112
  ---
114
113
 
115
114
  <system prompt body — instructions for the agent>
package/src/ui/display.ts CHANGED
@@ -136,7 +136,6 @@ export function buildInvocationTags(
136
136
  if (!invocation) return { tags };
137
137
  if (invocation.thinking) tags.push(`thinking: ${invocation.thinking}`);
138
138
  if (invocation.isolated) tags.push("isolated");
139
- if (invocation.isolation === "worktree") tags.push("worktree");
140
139
  if (invocation.inheritContext) tags.push("inherit context");
141
140
  if (invocation.runInBackground) tags.push("background");
142
141
  if (invocation.maxTurns != null) tags.push(`max turns: ${invocation.maxTurns}`);
@@ -1,59 +0,0 @@
1
- /**
2
- * worktree-isolation.ts — WorktreeIsolation: collaborator that owns the
3
- * git-worktree lifecycle for an isolated agent.
4
- *
5
- * Constructed by AgentManager only when isolation === "worktree", bound to a
6
- * WorktreeManager and the agent id. Agent tells it `setup()` and
7
- * `cleanup(description)` instead of managing worktree internals itself.
8
- *
9
- * The presence/absence of this collaborator IS the isolation mode: Agent calls
10
- * `this.worktree?.setup()` rather than checking an isolation string.
11
- */
12
-
13
- import type { WorktreeCleanupResult, WorktreeInfo, WorktreeManager } from "#src/lifecycle/worktree";
14
-
15
- export class WorktreeIsolation {
16
- private _info?: WorktreeInfo;
17
- private _cleanupResult?: WorktreeCleanupResult;
18
-
19
- constructor(
20
- private readonly worktrees: WorktreeManager,
21
- private readonly agentId: string,
22
- ) {}
23
-
24
- /** Absolute worktree path — undefined before setup(). */
25
- get path(): string | undefined {
26
- return this._info?.path;
27
- }
28
-
29
- /** Cleanup outcome — undefined until cleanup() runs. */
30
- get cleanupResult(): WorktreeCleanupResult | undefined {
31
- return this._cleanupResult;
32
- }
33
-
34
- /**
35
- * Create the git worktree and store its info.
36
- * Throws on failure (strict isolation — no silent fallback).
37
- */
38
- setup(): void {
39
- const wt = this.worktrees.create(this.agentId);
40
- if (!wt) {
41
- throw new Error(
42
- 'Cannot run with isolation: "worktree" — not a git repo, no commits yet, or `git worktree add` failed. ' +
43
- "Initialize git and commit at least once, or omit `isolation`.",
44
- );
45
- }
46
- this._info = wt;
47
- }
48
-
49
- /**
50
- * Perform worktree cleanup and record the result.
51
- * No-op returning { hasChanges: false } if setup never ran.
52
- */
53
- cleanup(description: string): WorktreeCleanupResult {
54
- if (!this._info) return { hasChanges: false };
55
- const result = this.worktrees.cleanup(this._info, description);
56
- this._cleanupResult = result;
57
- return result;
58
- }
59
- }