pi-subagents 0.24.0 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.24.1] - 2026-05-10
6
+
7
+ ### Changed
8
+ - Migrated Pi package imports and package metadata to the `@earendil-works/*` scope, switched async TypeScript execution discovery to upstream `jiti`, and hardened forked-session creation to use the public `SessionManager.open()` path.
9
+
5
10
  ## [0.24.0] - 2026-05-03
6
11
 
7
12
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-subagents",
3
- "version": "0.24.0",
3
+ "version": "0.24.1",
4
4
  "description": "Pi extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification",
5
5
  "author": "Nico Bailon",
6
6
  "license": "MIT",
@@ -52,32 +52,33 @@
52
52
  ]
53
53
  },
54
54
  "peerDependencies": {
55
- "@mariozechner/pi-agent-core": "*",
56
- "@mariozechner/pi-ai": "*",
57
- "@mariozechner/pi-coding-agent": "*",
58
- "@mariozechner/pi-tui": "*"
55
+ "@earendil-works/pi-agent-core": "*",
56
+ "@earendil-works/pi-ai": "*",
57
+ "@earendil-works/pi-coding-agent": "*",
58
+ "@earendil-works/pi-tui": "*"
59
59
  },
60
60
  "peerDependenciesMeta": {
61
- "@mariozechner/pi-agent-core": {
61
+ "@earendil-works/pi-agent-core": {
62
62
  "optional": true
63
63
  },
64
- "@mariozechner/pi-ai": {
64
+ "@earendil-works/pi-ai": {
65
65
  "optional": true
66
66
  },
67
- "@mariozechner/pi-coding-agent": {
67
+ "@earendil-works/pi-coding-agent": {
68
68
  "optional": true
69
69
  },
70
- "@mariozechner/pi-tui": {
70
+ "@earendil-works/pi-tui": {
71
71
  "optional": true
72
72
  }
73
73
  },
74
74
  "dependencies": {
75
+ "jiti": "^2.7.0",
75
76
  "typebox": "^1.1.24"
76
77
  },
77
78
  "devDependencies": {
78
- "@mariozechner/pi-agent-core": "^0.65.0",
79
- "@mariozechner/pi-ai": "^0.65.0",
80
- "@mariozechner/pi-coding-agent": "^0.65.0",
81
- "@mariozechner/pi-tui": "^0.65.2"
79
+ "@earendil-works/pi-agent-core": "^0.74.0",
80
+ "@earendil-works/pi-ai": "^0.74.0",
81
+ "@earendil-works/pi-coding-agent": "^0.74.0",
82
+ "@earendil-works/pi-tui": "^0.74.0"
82
83
  }
83
84
  }
@@ -1,7 +1,7 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import type { AgentToolResult } from "@mariozechner/pi-agent-core";
4
- import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
3
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
4
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
5
5
  import {
6
6
  type AgentConfig,
7
7
  type AgentScope,
@@ -1,4 +1,4 @@
1
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import { controlNotificationKey, formatControlNoticeMessage } from "../runs/shared/subagent-control.ts";
3
3
  import type { ControlEvent, SubagentState } from "../shared/types.ts";
4
4
 
@@ -15,9 +15,9 @@
15
15
  import * as fs from "node:fs";
16
16
  import * as os from "node:os";
17
17
  import * as path from "node:path";
18
- import type { AgentToolResult } from "@mariozechner/pi-agent-core";
19
- import { type ExtensionAPI, type ExtensionContext, type ToolDefinition } from "@mariozechner/pi-coding-agent";
20
- import { Box, Container, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component } from "@mariozechner/pi-tui";
18
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
19
+ import { type ExtensionAPI, type ExtensionContext, type ToolDefinition } from "@earendil-works/pi-coding-agent";
20
+ import { Box, Container, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component } from "@earendil-works/pi-tui";
21
21
  import { discoverAgents } from "../agents/agents.ts";
22
22
  import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.ts";
23
23
  import { resolveCurrentSessionId } from "../shared/session-identity.ts";
@@ -242,7 +242,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
242
242
  cleanupAllArtifactDirs(DEFAULT_ARTIFACT_CONFIG.cleanupDays);
243
243
 
244
244
  const state: SubagentState = {
245
- baseCwd: process.cwd(),
245
+ baseCwd: "",
246
246
  currentSessionId: null,
247
247
  asyncJobs: new Map(),
248
248
  foregroundRuns: new Map(),
@@ -190,8 +190,7 @@ function getGlobalNpmRoot(): string | null {
190
190
  }
191
191
 
192
192
  function configuredPiIntercomPackageDir(input: ResolveIntercomBridgeInput, agentDir: string): string | undefined {
193
- const cwd = path.resolve(input.cwd ?? process.cwd());
194
- const projectConfigDir = findNearestProjectConfigDir(cwd);
193
+ const projectConfigDir = input.cwd ? findNearestProjectConfigDir(path.resolve(input.cwd)) : undefined;
195
194
  const settingsFiles = [
196
195
  ...(projectConfigDir ? [{ file: path.join(projectConfigDir, "settings.json"), configDir: projectConfigDir, scope: "project" as const }] : []),
197
196
  { file: path.join(agentDir, "settings.json"), configDir: agentDir, scope: "user" as const },
@@ -8,7 +8,7 @@ import * as os from "node:os";
8
8
  import * as path from "node:path";
9
9
  import { fileURLToPath } from "node:url";
10
10
  import { createRequire } from "node:module";
11
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
11
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
12
12
  import type { AgentConfig } from "../../agents/agents.ts";
13
13
  import { applyThinkingSuffix } from "../shared/pi-args.ts";
14
14
  import { injectSingleOutputInstruction, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
@@ -35,26 +35,52 @@ import {
35
35
 
36
36
  const require = createRequire(import.meta.url);
37
37
  const piPackageRoot = resolvePiPackageRoot();
38
- const jitiCliPath: string | undefined = (() => {
39
- const candidates: Array<() => string> = [
40
- () => path.join(path.dirname(require.resolve("jiti/package.json")), "lib/jiti-cli.mjs"),
41
- () => path.join(path.dirname(require.resolve("@mariozechner/jiti/package.json")), "lib/jiti-cli.mjs"),
38
+
39
+ function resolveJitiCliFromPackageJson(packageJsonPath: string): string | undefined {
40
+ if (!fs.existsSync(packageJsonPath)) return undefined;
41
+ const packageRoot = path.dirname(packageJsonPath);
42
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as {
43
+ bin?: string | Record<string, string>;
44
+ };
45
+ const binField = pkg.bin;
46
+ const binPath = typeof binField === "string"
47
+ ? binField
48
+ : binField?.jiti ?? Object.values(binField ?? {})[0];
49
+ const candidates = [binPath, "lib/jiti-cli.mjs"].filter((candidate): candidate is string => Boolean(candidate));
50
+ for (const candidate of candidates) {
51
+ const cliPath = path.resolve(packageRoot, candidate);
52
+ if (fs.existsSync(cliPath)) return cliPath;
53
+ }
54
+ return undefined;
55
+ }
56
+
57
+ function resolveJitiCliPath(): string | undefined {
58
+ const candidates: Array<() => string | undefined> = [
59
+ () => require.resolve("jiti/package.json"),
60
+ () => piPackageRoot
61
+ ? createRequire(path.join(piPackageRoot, "package.json")).resolve("jiti/package.json")
62
+ : undefined,
42
63
  () => {
64
+ if (!process.argv[1]) return undefined;
43
65
  const piEntry = fs.realpathSync(process.argv[1]);
44
- const piRequire = createRequire(piEntry);
45
- return path.join(path.dirname(piRequire.resolve("@mariozechner/jiti/package.json")), "lib/jiti-cli.mjs");
66
+ return createRequire(piEntry).resolve("jiti/package.json");
46
67
  },
68
+ () => piPackageRoot ? path.join(piPackageRoot, "node_modules", "jiti", "package.json") : undefined,
47
69
  ];
48
70
  for (const candidate of candidates) {
49
71
  try {
50
- const p = candidate();
51
- if (fs.existsSync(p)) return p;
72
+ const packageJsonPath = candidate();
73
+ if (!packageJsonPath) continue;
74
+ const cliPath = resolveJitiCliFromPackageJson(packageJsonPath);
75
+ if (cliPath) return cliPath;
52
76
  } catch {
53
77
  // Candidate not available in this install, continue probing.
54
78
  }
55
79
  }
56
80
  return undefined;
57
- })();
81
+ }
82
+
83
+ const jitiCliPath = resolveJitiCliPath();
58
84
 
59
85
  interface AsyncExecutionContext {
60
86
  pi: ExtensionAPI;
@@ -139,7 +165,7 @@ export function isAsyncAvailable(): boolean {
139
165
  */
140
166
  function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number; error?: string } {
141
167
  if (!jitiCliPath) {
142
- return { error: "jiti for TypeScript execution could not be found" };
168
+ return { error: "upstream jiti for TypeScript execution could not be found; ensure package dependencies are installed" };
143
169
  }
144
170
 
145
171
  try {
@@ -1,4 +1,4 @@
1
- import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
1
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
4
  import { renderWidget } from "../../tui/render.ts";
@@ -2,7 +2,7 @@
2
2
  * Subagent completion notifications.
3
3
  */
4
4
 
5
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
5
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
6
  import { buildCompletionKey, getGlobalSeenMap, markSeenWithTtl } from "./completion-dedupe.ts";
7
7
  import { SUBAGENT_ASYNC_COMPLETE_EVENT } from "../../shared/types.ts";
8
8
 
@@ -87,7 +87,7 @@ export function createResultWatcher(
87
87
  intercomTarget?: string;
88
88
  };
89
89
  if (data.sessionId && data.sessionId !== state.currentSessionId) return;
90
- if (!data.sessionId && data.cwd && data.cwd !== state.baseCwd) return;
90
+ if (!data.sessionId && data.cwd && (!state.baseCwd || data.cwd !== state.baseCwd)) return;
91
91
 
92
92
  const now = Date.now();
93
93
  const completionKey = buildCompletionKey(data, `result:${file}`);
@@ -1,6 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import type { AgentToolResult } from "@mariozechner/pi-agent-core";
3
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
4
4
  import { formatAsyncRunList, formatAsyncRunOutputPath, formatAsyncRunProgressLabel, listAsyncRuns } from "./async-status.ts";
5
5
  import { formatActivityLabel } from "../../shared/status-format.ts";
6
6
  import { ASYNC_DIR, RESULTS_DIR, type AsyncStatus, type Details } from "../../shared/types.ts";
@@ -1,12 +1,11 @@
1
1
  import { spawn, spawnSync } from "node:child_process";
2
2
  import * as fs from "node:fs";
3
- import { createRequire } from "node:module";
4
3
  import * as path from "node:path";
5
4
  import { pathToFileURL } from "node:url";
6
- import type { Message } from "@mariozechner/pi-ai";
5
+ import type { Message } from "@earendil-works/pi-ai";
7
6
  import { writeAtomicJson } from "../../shared/atomic-json.ts";
8
7
  import { appendJsonl, getArtifactPaths } from "../../shared/artifacts.ts";
9
- import { getPiSpawnCommand } from "../shared/pi-spawn.ts";
8
+ import { PI_CODING_AGENT_PACKAGE, getPiSpawnCommand, resolveInstalledPiPackageRoot } from "../shared/pi-spawn.ts";
10
9
  import { captureSingleOutputSnapshot, finalizeSingleOutput, formatSavedOutputReference, resolveSingleOutput, type SingleOutputSnapshot } from "../shared/single-output.ts";
11
10
  import {
12
11
  type ActivityState,
@@ -109,7 +108,6 @@ interface StepResult {
109
108
  truncated?: boolean;
110
109
  }
111
110
 
112
- const require = createRequire(import.meta.url);
113
111
  const ASYNC_INTERRUPT_SIGNAL: NodeJS.Signals = process.platform === "win32" ? "SIGBREAK" : "SIGUSR2";
114
112
 
115
113
  function findLatestSessionFile(sessionDir: string): string | null {
@@ -424,21 +422,9 @@ function runPiStreaming(
424
422
  }
425
423
 
426
424
  function resolvePiPackageRootFallback(): string {
427
- // Try to resolve the main entry point and walk up to find the package root
428
- const entryPoint = require.resolve("@mariozechner/pi-coding-agent");
429
- // Entry point is typically /path/to/dist/index.js, so go up to find package root
430
- let dir = path.dirname(entryPoint);
431
- while (dir !== path.dirname(dir)) {
432
- const pkgJsonPath = path.join(dir, "package.json");
433
- try {
434
- const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
435
- if (pkg.name === "@mariozechner/pi-coding-agent") return dir;
436
- } catch {
437
- // Keep walking up until a readable package.json is found.
438
- }
439
- dir = path.dirname(dir);
440
- }
441
- throw new Error("Could not resolve @mariozechner/pi-coding-agent package root");
425
+ const root = resolveInstalledPiPackageRoot();
426
+ if (root) return root;
427
+ throw new Error(`Could not resolve ${PI_CODING_AGENT_PACKAGE} package root`);
442
428
  }
443
429
 
444
430
  async function exportSessionHtml(sessionFile: string, outputDir: string, piPackageRoot?: string): Promise<string> {
@@ -5,9 +5,9 @@
5
5
  * Supports runtime editing of templates, output paths, reads lists, and progress toggle.
6
6
  */
7
7
 
8
- import type { Theme } from "@mariozechner/pi-coding-agent";
9
- import type { Component, TUI } from "@mariozechner/pi-tui";
10
- import { matchesKey, visibleWidth, truncateToWidth } from "@mariozechner/pi-tui";
8
+ import type { Theme } from "@earendil-works/pi-coding-agent";
9
+ import type { Component, TUI } from "@earendil-works/pi-tui";
10
+ import { matchesKey, visibleWidth, truncateToWidth } from "@earendil-works/pi-tui";
11
11
  import type { AgentConfig } from "../../agents/agents.ts";
12
12
  import type { ResolvedStepBehavior } from "../../shared/settings.ts";
13
13
  import { resolveModelCandidate, splitThinkingSuffix } from "../shared/model-fallback.ts";
@@ -4,8 +4,8 @@
4
4
 
5
5
  import * as fs from "node:fs";
6
6
  import * as path from "node:path";
7
- import type { AgentToolResult } from "@mariozechner/pi-agent-core";
8
- import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
7
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
8
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
9
9
  import type { AgentConfig } from "../../agents/agents.ts";
10
10
  import { ChainClarifyComponent, type ChainClarifyResult, type BehaviorOverride } from "./chain-clarify.ts";
11
11
  import { toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { spawn } from "node:child_process";
6
6
  import { existsSync } from "node:fs";
7
- import type { Message } from "@mariozechner/pi-ai";
7
+ import type { Message } from "@earendil-works/pi-ai";
8
8
  import type { AgentConfig } from "../../agents/agents.ts";
9
9
  import {
10
10
  ensureArtifactsDir,
@@ -1,8 +1,8 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import type { AgentToolResult } from "@mariozechner/pi-agent-core";
5
- import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
4
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
5
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
6
6
  import { type AgentConfig, type AgentScope } from "../../agents/agents.ts";
7
7
  import { getArtifactsDir } from "../../shared/artifacts.ts";
8
8
  import { ChainClarifyComponent, type ChainClarifyResult } from "./chain-clarify.ts";
@@ -880,7 +880,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
880
880
 
881
881
  if (!isAsyncAvailable()) {
882
882
  return {
883
- content: [{ type: "text", text: "Async mode requires jiti for TypeScript execution but it could not be found. Install globally: npm install -g jiti" }],
883
+ content: [{ type: "text", text: "Async mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the pi-subagents package dependencies are installed." }],
884
884
  isError: true,
885
885
  details: { mode: "single" as const, results: [] },
886
886
  };
@@ -1070,7 +1070,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
1070
1070
  if (chainResult.requestedAsync) {
1071
1071
  if (!isAsyncAvailable()) {
1072
1072
  return {
1073
- content: [{ type: "text", text: "Background mode requires jiti for TypeScript execution but it could not be found." }],
1073
+ content: [{ type: "text", text: "Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the pi-subagents package dependencies are installed." }],
1074
1074
  isError: true,
1075
1075
  details: { mode: "chain" as const, results: [] },
1076
1076
  };
@@ -1479,7 +1479,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
1479
1479
  if (result.runInBackground) {
1480
1480
  if (!isAsyncAvailable()) {
1481
1481
  return {
1482
- content: [{ type: "text", text: "Background mode requires jiti for TypeScript execution but it could not be found." }],
1482
+ content: [{ type: "text", text: "Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the pi-subagents package dependencies are installed." }],
1483
1483
  isError: true,
1484
1484
  details: { mode: "parallel" as const, results: [] },
1485
1485
  };
@@ -1756,7 +1756,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
1756
1756
  if (result.runInBackground) {
1757
1757
  if (!isAsyncAvailable()) {
1758
1758
  return {
1759
- content: [{ type: "text", text: "Background mode requires jiti for TypeScript execution but it could not be found." }],
1759
+ content: [{ type: "text", text: "Background mode requires upstream jiti for TypeScript execution but it could not be found. Ensure the pi-subagents package dependencies are installed." }],
1760
1760
  isError: true,
1761
1761
  details: { mode: "single" as const, results: [] },
1762
1762
  };
@@ -1,4 +1,4 @@
1
- import type { Message } from "@mariozechner/pi-ai";
1
+ import type { Message } from "@earendil-works/pi-ai";
2
2
  import { isMutatingBashCommand } from "./long-running-guard.ts";
3
3
 
4
4
  const REVIEW_ONLY_PATTERNS = [
@@ -1,23 +1,34 @@
1
1
  import * as fs from "node:fs";
2
- import { createRequire } from "node:module";
3
2
  import * as path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
4
 
5
- const require = createRequire(import.meta.url);
5
+ export const PI_CODING_AGENT_PACKAGE = "@earendil-works/pi-coding-agent";
6
+
7
+ export function findPiPackageRootFromEntry(entryPoint: string): string | undefined {
8
+ let dir = path.dirname(entryPoint);
9
+ while (dir !== path.dirname(dir)) {
10
+ const packageJsonPath = path.join(dir, "package.json");
11
+ if (fs.existsSync(packageJsonPath)) {
12
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as { name?: unknown };
13
+ if (pkg.name === PI_CODING_AGENT_PACKAGE) return dir;
14
+ }
15
+ dir = path.dirname(dir);
16
+ }
17
+ return undefined;
18
+ }
19
+
20
+ export function resolveInstalledPiPackageRoot(): string | undefined {
21
+ return findPiPackageRootFromEntry(fileURLToPath(import.meta.resolve(PI_CODING_AGENT_PACKAGE)));
22
+ }
6
23
 
7
24
  export function resolvePiPackageRoot(): string | undefined {
8
25
  try {
9
26
  const entry = process.argv[1];
10
- if (!entry) return undefined;
11
- let dir = path.dirname(fs.realpathSync(entry));
12
- while (dir !== path.dirname(dir)) {
13
- try {
14
- const pkg = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf-8"));
15
- if (pkg.name === "@mariozechner/pi-coding-agent") return dir;
16
- } catch {}
17
- dir = path.dirname(dir);
18
- }
19
- } catch {}
20
- return undefined;
27
+ return entry ? findPiPackageRootFromEntry(fs.realpathSync(entry)) : undefined;
28
+ } catch {
29
+ // process.argv[1] probing is best-effort; callers can fall back to PATH/package resolution.
30
+ return undefined;
31
+ }
21
32
  }
22
33
 
23
34
  export interface PiSpawnDeps {
@@ -27,6 +38,7 @@ export interface PiSpawnDeps {
27
38
  existsSync?: (filePath: string) => boolean;
28
39
  readFileSync?: (filePath: string, encoding: "utf-8") => string;
29
40
  resolvePackageJson?: () => string;
41
+ resolvePackageEntry?: () => string;
30
42
  piPackageRoot?: string;
31
43
  }
32
44
 
@@ -60,7 +72,11 @@ export function resolveWindowsPiCliScript(deps: PiSpawnDeps = {}): string | unde
60
72
  const resolvePackageJson = deps.resolvePackageJson ?? (() => {
61
73
  const root = deps.piPackageRoot ?? resolvePiPackageRoot();
62
74
  if (root) return path.join(root, "package.json");
63
- return require.resolve("@mariozechner/pi-coding-agent/package.json");
75
+ const packageRoot = deps.resolvePackageEntry
76
+ ? findPiPackageRootFromEntry(deps.resolvePackageEntry())
77
+ : resolveInstalledPiPackageRoot();
78
+ if (!packageRoot) throw new Error(`Could not resolve ${PI_CODING_AGENT_PACKAGE} package root`);
79
+ return path.join(packageRoot, "package.json");
64
80
  });
65
81
  const packageJsonPath = resolvePackageJson();
66
82
  const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as {
@@ -71,11 +87,12 @@ export function resolveWindowsPiCliScript(deps: PiSpawnDeps = {}): string | unde
71
87
  ? binField
72
88
  : binField?.pi ?? Object.values(binField ?? {})[0];
73
89
  if (!binPath) return undefined;
74
- const candidate = normalizePath(path.resolve(path.dirname(packageJsonPath), binPath));
90
+ const candidate = path.resolve(path.dirname(packageJsonPath), binPath);
75
91
  if (isRunnableNodeScript(candidate, existsSync)) {
76
92
  return candidate;
77
93
  }
78
94
  } catch {
95
+ // Windows CLI resolution is optional; falling back to `pi` lets PATH handle execution.
79
96
  return undefined;
80
97
  }
81
98
 
@@ -1,4 +1,4 @@
1
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
 
3
3
  const SUBAGENT_INHERIT_PROJECT_CONTEXT_ENV = "PI_SUBAGENT_INHERIT_PROJECT_CONTEXT";
4
4
  const SUBAGENT_INHERIT_SKILLS_ENV = "PI_SUBAGENT_INHERIT_SKILLS";
@@ -1,13 +1,17 @@
1
- type SubagentExecutionContext = "fresh" | "fork";
1
+ import * as fs from "node:fs";
2
+ import { SessionManager } from "@earendil-works/pi-coding-agent";
2
3
 
3
- interface ForkableSessionManagerStatic {
4
- open(path: string): { createBranchedSession(leafId: string): string | undefined };
5
- }
4
+ type SubagentExecutionContext = "fresh" | "fork";
6
5
 
7
6
  interface ForkableSessionManager {
8
7
  getSessionFile(): string | undefined;
9
8
  getLeafId(): string | null;
10
- constructor: ForkableSessionManagerStatic;
9
+ getSessionDir?(): string;
10
+ openSession?: (path: string, sessionDir?: string) => { createBranchedSession(leafId: string): string | undefined };
11
+ }
12
+
13
+ interface ForkContextResolverOptions {
14
+ openSession?: (path: string, sessionDir?: string) => { createBranchedSession(leafId: string): string | undefined };
11
15
  }
12
16
 
13
17
  interface ForkContextResolver {
@@ -21,6 +25,7 @@ export function resolveSubagentContext(value: unknown): SubagentExecutionContext
21
25
  export function createForkContextResolver(
22
26
  sessionManager: ForkableSessionManager,
23
27
  requestedContext: unknown,
28
+ options: ForkContextResolverOptions = {},
24
29
  ): ForkContextResolver {
25
30
  if (resolveSubagentContext(requestedContext) !== "fork") {
26
31
  return {
@@ -38,6 +43,10 @@ export function createForkContextResolver(
38
43
  throw new Error("Forked subagent context requires a current leaf to fork from.");
39
44
  }
40
45
 
46
+ const openSession = options.openSession
47
+ ?? sessionManager.openSession
48
+ ?? ((file: string, dir?: string) => SessionManager.open(file, dir));
49
+ const sessionDir = sessionManager.getSessionDir?.();
41
50
  const cachedSessionFiles = new Map<number, string>();
42
51
 
43
52
  return {
@@ -45,10 +54,16 @@ export function createForkContextResolver(
45
54
  const cached = cachedSessionFiles.get(index);
46
55
  if (cached) return cached;
47
56
  try {
48
- const sourceManager = sessionManager.constructor.open(parentSessionFile);
57
+ if (!fs.existsSync(parentSessionFile)) {
58
+ throw new Error(`Parent session file does not exist: ${parentSessionFile}. Pi has not persisted enough history to fork yet.`);
59
+ }
60
+ const sourceManager = openSession(parentSessionFile, sessionDir);
49
61
  const sessionFile = sourceManager.createBranchedSession(leafId);
50
62
  if (!sessionFile) {
51
- throw new Error("Session manager did not return a session file.");
63
+ throw new Error("Session manager did not return a forked session file.");
64
+ }
65
+ if (!fs.existsSync(sessionFile)) {
66
+ throw new Error(`Session manager returned a forked session file that does not exist: ${sessionFile}`);
52
67
  }
53
68
  cachedSessionFiles.set(index, sessionFile);
54
69
  return sessionFile;
@@ -4,9 +4,9 @@
4
4
 
5
5
  import * as os from "node:os";
6
6
  import * as path from "node:path";
7
- import type { Message } from "@mariozechner/pi-ai";
7
+ import type { Message } from "@earendil-works/pi-ai";
8
8
  import type { FSWatcher } from "node:fs";
9
- import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
9
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
10
10
 
11
11
  // ============================================================================
12
12
  // Basic Types
@@ -456,7 +456,7 @@ export interface RunSyncOptions {
456
456
  interruptSignal?: AbortSignal;
457
457
  allowIntercomDetach?: boolean;
458
458
  intercomEvents?: IntercomEventBus;
459
- onUpdate?: (r: import("@mariozechner/pi-agent-core").AgentToolResult<Details>) => void;
459
+ onUpdate?: (r: import("@earendil-works/pi-agent-core").AgentToolResult<Details>) => void;
460
460
  onControlEvent?: (event: ControlEvent) => void;
461
461
  controlConfig?: ResolvedControlConfig;
462
462
  intercomSessionName?: string;
@@ -5,7 +5,7 @@
5
5
  import * as fs from "node:fs";
6
6
  import * as os from "node:os";
7
7
  import * as path from "node:path";
8
- import type { Message } from "@mariozechner/pi-ai";
8
+ import type { Message } from "@earendil-works/pi-ai";
9
9
  import { formatToolCall } from "./formatters.ts";
10
10
  import type { AgentProgress, AsyncStatus, Details, DisplayItem, ErrorInfo, SingleResult, ToolCallSummary } from "./types.ts";
11
11
 
@@ -1,5 +1,5 @@
1
- import type { AgentToolResult } from "@mariozechner/pi-agent-core";
2
- import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
1
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
2
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
3
3
  import type { SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
4
4
  import {
5
5
  SLASH_SUBAGENT_CANCEL_EVENT,
@@ -1,8 +1,8 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
5
- import { Key, matchesKey } from "@mariozechner/pi-tui";
4
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
5
+ import { Key, matchesKey } from "@earendil-works/pi-tui";
6
6
  import { discoverAgents, discoverAgentsAll, type ChainConfig } from "../agents/agents.ts";
7
7
  import type { SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
8
8
  import { isParallelStep, type ChainStep } from "../shared/settings.ts";
@@ -87,6 +87,7 @@ const extractExecutionFlags = (rawArgs: string): { args: string; bg: boolean; fo
87
87
  };
88
88
 
89
89
  const makeAgentCompletions = (state: SubagentState, multiAgent: boolean) => (prefix: string) => {
90
+ if (!state.baseCwd) return null;
90
91
  const agents = discoverAgents(state.baseCwd, "both").agents;
91
92
  if (!multiAgent) {
92
93
  if (prefix.includes(" ")) return null;
@@ -116,7 +117,7 @@ const discoverSavedChains = (cwd: string): ChainConfig[] => {
116
117
  };
117
118
 
118
119
  const makeChainCompletions = (state: SubagentState) => (prefix: string) => {
119
- if (prefix.includes(" ")) return null;
120
+ if (prefix.includes(" ") || !state.baseCwd) return null;
120
121
  return discoverSavedChains(state.baseCwd)
121
122
  .filter((chain) => chain.name.startsWith(prefix))
122
123
  .map((chain) => ({ value: chain.name, label: chain.name }));
@@ -382,6 +383,10 @@ const parseAgentArgs = (
382
383
  ctx.ui.notify(usage, "error");
383
384
  return null;
384
385
  }
386
+ if (!state.baseCwd) {
387
+ ctx.ui.notify("Subagent session cwd is not initialized yet", "error");
388
+ return null;
389
+ }
385
390
  const agents = discoverAgents(state.baseCwd, "both").agents;
386
391
  for (const step of steps) {
387
392
  if (!agents.find((a) => a.name === step.name)) {
@@ -415,6 +420,7 @@ export function registerSlashCommands(
415
420
  const { name: agentName, config: inline } = parseAgentToken(firstSpace === -1 ? input : input.slice(0, firstSpace));
416
421
  const task = firstSpace === -1 ? "" : input.slice(firstSpace + 1).trim();
417
422
 
423
+ if (!state.baseCwd) { ctx.ui.notify("Subagent session cwd is not initialized yet", "error"); return; }
418
424
  const agents = discoverAgents(state.baseCwd, "both").agents;
419
425
  if (!agents.find((a) => a.name === agentName)) { ctx.ui.notify(`Unknown agent: ${agentName}`, "error"); return; }
420
426
 
@@ -474,6 +480,7 @@ export function registerSlashCommands(
474
480
  ctx.ui.notify(usage, "error");
475
481
  return;
476
482
  }
483
+ if (!state.baseCwd) { ctx.ui.notify("Subagent session cwd is not initialized yet", "error"); return; }
477
484
  const chain = discoverSavedChains(state.baseCwd).find((candidate) => candidate.name === chainName);
478
485
  if (!chain) {
479
486
  ctx.ui.notify(`Unknown chain: ${chainName}`, "error");
@@ -1,5 +1,5 @@
1
- import type { AgentToolResult } from "@mariozechner/pi-agent-core";
2
- import type { Message } from "@mariozechner/pi-ai";
1
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
2
+ import type { Message } from "@earendil-works/pi-ai";
3
3
  import type { SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
4
4
  import type { SlashSubagentResponse, SlashSubagentUpdate } from "./slash-bridge.ts";
5
5
  import { type Details, type SingleResult, type Usage, SLASH_RESULT_TYPE } from "../shared/types.ts";
@@ -1,5 +1,5 @@
1
- import type { Theme } from "@mariozechner/pi-coding-agent";
2
- import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
1
+ import type { Theme } from "@earendil-works/pi-coding-agent";
2
+ import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
3
3
 
4
4
  function fuzzyScore(query: string, text: string): number {
5
5
  const lq = query.toLowerCase();
package/src/tui/render.ts CHANGED
@@ -3,9 +3,9 @@
3
3
  */
4
4
 
5
5
  import * as path from "node:path";
6
- import type { AgentToolResult } from "@mariozechner/pi-agent-core";
7
- import { getMarkdownTheme, type ExtensionContext } from "@mariozechner/pi-coding-agent";
8
- import { Container, Markdown, Spacer, Text, visibleWidth, type Component } from "@mariozechner/pi-tui";
6
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
7
+ import { getMarkdownTheme, type ExtensionContext } from "@earendil-works/pi-coding-agent";
8
+ import { Container, Markdown, Spacer, Text, visibleWidth, type Component } from "@earendil-works/pi-tui";
9
9
  import {
10
10
  type AgentProgress,
11
11
  type AsyncJobState,