pi-cohort 7.2.2 → 7.2.3
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 +6 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/runs/foreground/subagent-executor.ts +3 -2
- package/src/runs/shared/pi-spawn.ts +38 -33
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [7.2.3] - 2026-10-18
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Subagents start from SDK-hosted sessions (Pi Web, standalone SDK scripts) whose `process.argv[1]` is not Pi's CLI. `capturePiRuntime` still identifies the parent from `argv[1]` first; when that entry is not Pi's declared CLI, the executor supplies the installation the extension was loaded from (`getPackageDir()` from `@earendil-works/pi-coding-agent`), validated against that package's `bin.pi` through the same ownership check, so a sibling Pi earlier on `PATH` is never selected. Sessions launched through Pi's CLI capture exactly as before. (#22)
|
|
8
|
+
|
|
3
9
|
## [7.2.2] - 2026-10-16
|
|
4
10
|
|
|
5
11
|
### Fixed
|
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ pi install npm:pi-cohort
|
|
|
39
39
|
|
|
40
40
|
That is the only required step.
|
|
41
41
|
|
|
42
|
-
Children and detached coordinators use the originating parent's resolved Node executable and Pi entry path directly, bypassing `PATH`. Revival keeps that original runtime even when requested from a different parent installation.
|
|
42
|
+
Children and detached coordinators use the originating parent's resolved Node executable and Pi entry path directly, bypassing `PATH`. Revival keeps that original runtime even when requested from a different parent installation. When the parent is not launched through Pi's CLI (an SDK-hosted session such as Pi Web, where `process.argv[1]` is the host's entry), children use the Pi installation this extension was loaded from: the package directory Pi reports for itself, accepted only when it is `@earendil-works/pi-coding-agent` and its declared `bin.pi` resolves inside that package. Single-file Pi binaries and hosts whose `process.execPath` is not a Node binary remain unsupported. These paths are not a version lock: replacing Node or Pi in place changes what runs. If the original installation is removed, restore it at the recorded paths or start a new run; there is no fallback to the current parent's installation. Legacy records without an original runtime identity remain inspectable but cannot be revived - start a new run.
|
|
43
43
|
|
|
44
44
|
## Execution backend extension API
|
|
45
45
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cohort",
|
|
3
|
-
"version": "7.2.
|
|
3
|
+
"version": "7.2.3",
|
|
4
4
|
"description": "Delegate Pi work to focused child agents: code review, scouting, implementation, parallel audits, saved chains, and background jobs.",
|
|
5
5
|
"author": "Jacek Juraszek",
|
|
6
6
|
"license": "MIT",
|
|
@@ -3,7 +3,7 @@ import { capturePiRuntime, parsePiRuntime, type PiRuntimeIdentity } from "../sha
|
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
6
|
-
import type
|
|
6
|
+
import { getPackageDir, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
7
7
|
import type { AgentConfig, AgentScope } from "../../agents/agents.ts";
|
|
8
8
|
import { getArtifactsDir } from "../../shared/artifacts.ts";
|
|
9
9
|
import { ChainClarifyComponent, type ChainClarifyResult } from "./chain-clarify.ts";
|
|
@@ -2120,7 +2120,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2120
2120
|
let runtime: PiRuntimeIdentity | undefined;
|
|
2121
2121
|
let runtimeError: unknown;
|
|
2122
2122
|
try {
|
|
2123
|
-
|
|
2123
|
+
// SDK hosts are not launched through Pi's CLI; the installation this extension was loaded from is the fallback.
|
|
2124
|
+
runtime = (deps.captureRuntime ?? (() => capturePiRuntime({ installRoot: getPackageDir() })))();
|
|
2124
2125
|
} catch (error) {
|
|
2125
2126
|
runtimeError = error;
|
|
2126
2127
|
}
|
|
@@ -1,36 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
3
|
|
|
5
4
|
export const PI_CODING_AGENT_PACKAGE = "@earendil-works/pi-coding-agent";
|
|
6
5
|
|
|
7
|
-
export function findPiPackageRootFromEntry(entryPoint: string): string | undefined {
|
|
8
|
-
let dir = path.dirname(entryPoint);
|
|
9
|
-
while (true) {
|
|
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
|
-
if (dir === path.dirname(dir)) return undefined;
|
|
16
|
-
dir = path.dirname(dir);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function resolveInstalledPiPackageRoot(): string | undefined {
|
|
21
|
-
return findPiPackageRootFromEntry(fileURLToPath(import.meta.resolve(PI_CODING_AGENT_PACKAGE)));
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function resolvePiPackageRoot(): string | undefined {
|
|
25
|
-
try {
|
|
26
|
-
const entry = process.argv[1];
|
|
27
|
-
return entry ? findPiPackageRootFromEntry(fs.realpathSync(entry)) : undefined;
|
|
28
|
-
} catch {
|
|
29
|
-
// Non-launch package inspection is best-effort.
|
|
30
|
-
return undefined;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
6
|
export interface PiRuntimeIdentity {
|
|
35
7
|
readonly nodeExecPath: string;
|
|
36
8
|
readonly piEntryPath: string;
|
|
@@ -90,13 +62,46 @@ function checkAvailability(file: string, executable: boolean): void {
|
|
|
90
62
|
}
|
|
91
63
|
}
|
|
92
64
|
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
const piEntryPath = canonicalPath(deps.argv1 ?? process.argv[1], "Pi entry");
|
|
96
|
-
checkAvailability(nodeExecPath, true);
|
|
65
|
+
function identifyFromEntry(entry: string | undefined): { piEntryPath: string; piPackageRoot: string } {
|
|
66
|
+
const piEntryPath = canonicalPath(entry, "Pi entry");
|
|
97
67
|
checkAvailability(piEntryPath, false);
|
|
98
68
|
const piPackageRoot = canonicalPath(owningPackageRoot(piEntryPath), "Pi package root");
|
|
99
|
-
return {
|
|
69
|
+
return { piEntryPath, piPackageRoot };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function identifyFromInstallRoot(installRoot: string): { piEntryPath: string; piPackageRoot: string } {
|
|
73
|
+
const piPackageRoot = canonicalPath(installRoot, "Pi installation root");
|
|
74
|
+
const manifest = path.join(piPackageRoot, "package.json");
|
|
75
|
+
let bin: unknown;
|
|
76
|
+
try {
|
|
77
|
+
const pkg = JSON.parse(fs.readFileSync(manifest, "utf-8"));
|
|
78
|
+
if (pkg?.name !== PI_CODING_AGENT_PACKAGE) {
|
|
79
|
+
throw new PiRuntimeLaunchError(`${manifest}: installation is not ${PI_CODING_AGENT_PACKAGE}`);
|
|
80
|
+
}
|
|
81
|
+
bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.pi;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (error instanceof PiRuntimeLaunchError) throw error;
|
|
84
|
+
throw new PiRuntimeLaunchError(`${manifest}: ${String(error)}`);
|
|
85
|
+
}
|
|
86
|
+
if (typeof bin !== "string" || !bin) throw new PiRuntimeLaunchError(`${manifest}: missing declared Pi bin`);
|
|
87
|
+
const piEntryPath = canonicalPath(path.resolve(piPackageRoot, bin), "Declared Pi bin");
|
|
88
|
+
checkAvailability(piEntryPath, false);
|
|
89
|
+
const owner = canonicalPath(owningPackageRoot(piEntryPath), "Pi package root");
|
|
90
|
+
if (owner !== piPackageRoot) {
|
|
91
|
+
throw new PiRuntimeLaunchError(`${piEntryPath}: owning package ${owner} does not match installation ${piPackageRoot}`);
|
|
92
|
+
}
|
|
93
|
+
return { piEntryPath, piPackageRoot };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function capturePiRuntime(deps: { execPath?: string; argv1?: string; installRoot?: string } = {}): PiRuntimeIdentity {
|
|
97
|
+
const nodeExecPath = canonicalPath(deps.execPath ?? process.execPath, "Node executable");
|
|
98
|
+
checkAvailability(nodeExecPath, true);
|
|
99
|
+
try {
|
|
100
|
+
return { nodeExecPath, ...identifyFromEntry(deps.argv1 ?? process.argv[1]) };
|
|
101
|
+
} catch (error) {
|
|
102
|
+
if (!(error instanceof PiRuntimeLaunchError) || deps.installRoot === undefined) throw error;
|
|
103
|
+
return { nodeExecPath, ...identifyFromInstallRoot(deps.installRoot) };
|
|
104
|
+
}
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
export function parsePiRuntime(value: unknown, source: string): PiRuntimeIdentity {
|