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 +5 -0
- package/package.json +14 -13
- package/src/agents/agent-management.ts +2 -2
- package/src/extension/control-notices.ts +1 -1
- package/src/extension/index.ts +4 -4
- package/src/intercom/intercom-bridge.ts +1 -2
- package/src/runs/background/async-execution.ts +37 -11
- package/src/runs/background/async-job-tracker.ts +1 -1
- package/src/runs/background/notify.ts +1 -1
- package/src/runs/background/result-watcher.ts +1 -1
- package/src/runs/background/run-status.ts +1 -1
- package/src/runs/background/subagent-runner.ts +5 -19
- package/src/runs/foreground/chain-clarify.ts +3 -3
- package/src/runs/foreground/chain-execution.ts +2 -2
- package/src/runs/foreground/execution.ts +1 -1
- package/src/runs/foreground/subagent-executor.ts +6 -6
- package/src/runs/shared/completion-guard.ts +1 -1
- package/src/runs/shared/pi-spawn.ts +32 -15
- package/src/runs/shared/subagent-prompt-runtime.ts +1 -1
- package/src/shared/fork-context.ts +22 -7
- package/src/shared/types.ts +3 -3
- package/src/shared/utils.ts +1 -1
- package/src/slash/slash-bridge.ts +2 -2
- package/src/slash/slash-commands.ts +10 -3
- package/src/slash/slash-live-state.ts +2 -2
- package/src/tui/render-helpers.ts +2 -2
- package/src/tui/render.ts +3 -3
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.
|
|
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
|
-
"@
|
|
56
|
-
"@
|
|
57
|
-
"@
|
|
58
|
-
"@
|
|
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
|
-
"@
|
|
61
|
+
"@earendil-works/pi-agent-core": {
|
|
62
62
|
"optional": true
|
|
63
63
|
},
|
|
64
|
-
"@
|
|
64
|
+
"@earendil-works/pi-ai": {
|
|
65
65
|
"optional": true
|
|
66
66
|
},
|
|
67
|
-
"@
|
|
67
|
+
"@earendil-works/pi-coding-agent": {
|
|
68
68
|
"optional": true
|
|
69
69
|
},
|
|
70
|
-
"@
|
|
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
|
-
"@
|
|
79
|
-
"@
|
|
80
|
-
"@
|
|
81
|
-
"@
|
|
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 "@
|
|
4
|
-
import type { ExtensionContext } from "@
|
|
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 "@
|
|
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
|
|
package/src/extension/index.ts
CHANGED
|
@@ -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 "@
|
|
19
|
-
import { type ExtensionAPI, type ExtensionContext, type ToolDefinition } from "@
|
|
20
|
-
import { Box, Container, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component } from "@
|
|
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:
|
|
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
|
|
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 "@
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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
|
|
51
|
-
if (
|
|
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 "@
|
|
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 "@
|
|
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 "@
|
|
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 "@
|
|
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
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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 "@
|
|
9
|
-
import type { Component, TUI } from "@
|
|
10
|
-
import { matchesKey, visibleWidth, truncateToWidth } from "@
|
|
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 "@
|
|
8
|
-
import type { ExtensionContext } from "@
|
|
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 "@
|
|
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 "@
|
|
5
|
-
import type { ExtensionAPI, ExtensionContext } from "@
|
|
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.
|
|
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,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
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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 =
|
|
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 "@
|
|
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
|
-
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
2
3
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
package/src/shared/types.ts
CHANGED
|
@@ -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 "@
|
|
7
|
+
import type { Message } from "@earendil-works/pi-ai";
|
|
8
8
|
import type { FSWatcher } from "node:fs";
|
|
9
|
-
import type { ExtensionContext } from "@
|
|
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("@
|
|
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;
|
package/src/shared/utils.ts
CHANGED
|
@@ -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 "@
|
|
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 "@
|
|
2
|
-
import type { ExtensionContext } from "@
|
|
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 "@
|
|
5
|
-
import { Key, matchesKey } from "@
|
|
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 "@
|
|
2
|
-
import type { Message } from "@
|
|
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 "@
|
|
2
|
-
import { truncateToWidth, visibleWidth } from "@
|
|
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 "@
|
|
7
|
-
import { getMarkdownTheme, type ExtensionContext } from "@
|
|
8
|
-
import { Container, Markdown, Spacer, Text, visibleWidth, type Component } from "@
|
|
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,
|