pi-cohort 5.3.0 → 5.3.2
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
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.3.2] - 2026-09-06
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Extension startup now retries transient `EPERM` failures while creating completed-result and async-run temporary directories, with a strict limit of three attempts and two one-second waits. ([#10](https://github.com/jjuraszek/pi-cohort/issues/10))
|
|
8
|
+
|
|
9
|
+
## [5.3.1] - 2026-09-01
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Async (detached) subagent runs no longer crash at boot with `Cannot find module 'typebox/compile'` on consumer installs: `typebox` is now a real runtime dependency (`^1.3.11`). This deliberately re-reverses 2.0.0's move of typebox to peerDependencies - that decision's "match pi's bundled packages" rationale only holds for code running under pi's jiti aliases, and the detached runner runs outside them (same reasoning as `jiti` itself being a real dependency). ([#9](https://github.com/jjuraszek/pi-cohort/issues/9))
|
|
14
|
+
- Acceptance evidence checks now treat present-but-empty `changedFiles`, `testsAddedOrUpdated`, `commandsRun`, and `validationOutput` arrays as reported evidence ("ran and touched nothing") instead of rejecting the run with `evidence missing`; an absent field still fails structurally. No-op async worker runs with all criteria passing now reach `checked` status. ([#9](https://github.com/jjuraszek/pi-cohort/issues/9))
|
|
15
|
+
|
|
3
16
|
## [5.3.0] - 2026-09-01
|
|
4
17
|
|
|
5
18
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cohort",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.2",
|
|
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",
|
|
@@ -60,8 +60,7 @@
|
|
|
60
60
|
"@earendil-works/pi-agent-core": "*",
|
|
61
61
|
"@earendil-works/pi-ai": "*",
|
|
62
62
|
"@earendil-works/pi-coding-agent": "*",
|
|
63
|
-
"@earendil-works/pi-tui": "*"
|
|
64
|
-
"typebox": "*"
|
|
63
|
+
"@earendil-works/pi-tui": "*"
|
|
65
64
|
},
|
|
66
65
|
"peerDependenciesMeta": {
|
|
67
66
|
"@earendil-works/pi-agent-core": {
|
|
@@ -75,19 +74,16 @@
|
|
|
75
74
|
},
|
|
76
75
|
"@earendil-works/pi-tui": {
|
|
77
76
|
"optional": true
|
|
78
|
-
},
|
|
79
|
-
"typebox": {
|
|
80
|
-
"optional": true
|
|
81
77
|
}
|
|
82
78
|
},
|
|
83
79
|
"dependencies": {
|
|
84
|
-
"jiti": "^2.7.0"
|
|
80
|
+
"jiti": "^2.7.0",
|
|
81
|
+
"typebox": "^1.3.11"
|
|
85
82
|
},
|
|
86
83
|
"devDependencies": {
|
|
87
84
|
"@earendil-works/pi-agent-core": "^0.74.0",
|
|
88
85
|
"@earendil-works/pi-ai": "^0.74.0",
|
|
89
86
|
"@earendil-works/pi-coding-agent": "^0.74.0",
|
|
90
|
-
"@earendil-works/pi-tui": "^0.74.0"
|
|
91
|
-
"typebox": "^1.1.24"
|
|
87
|
+
"@earendil-works/pi-tui": "^0.74.0"
|
|
92
88
|
}
|
|
93
89
|
}
|
package/src/extension/index.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { resolveCurrentSessionId } from "../shared/session-identity.ts";
|
|
|
24
24
|
import { cleanupOldChainDirs } from "../shared/settings.ts";
|
|
25
25
|
import { clearLegacyResultAnimationTimer, renderWidget, renderSubagentResult } from "../tui/render.ts";
|
|
26
26
|
import { SubagentParams } from "./schemas.ts";
|
|
27
|
+
import { mkdirWithEpermRetry } from "./mkdir-with-retry.ts";
|
|
27
28
|
import { createSubagentExecutor, type SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
|
|
28
29
|
import { createAsyncJobTracker } from "../runs/background/async-job-tracker.ts";
|
|
29
30
|
import { createResultWatcher } from "../runs/background/result-watcher.ts";
|
|
@@ -82,15 +83,9 @@ function expandTilde(p: string): string {
|
|
|
82
83
|
return p.startsWith("~/") ? path.join(os.homedir(), p.slice(2)) : p;
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
/**
|
|
86
|
-
* Create a directory and verify it is actually accessible.
|
|
87
|
-
* On Windows with Azure AD/Entra ID, directories created shortly after
|
|
88
|
-
* wake-from-sleep can end up with broken NTFS ACLs (null DACL) when the
|
|
89
|
-
* cloud SID cannot be resolved without network connectivity. This leaves
|
|
90
|
-
* the directory completely inaccessible to the creating user.
|
|
91
|
-
*/
|
|
86
|
+
/** Create a directory and verify it is actually accessible. */
|
|
92
87
|
function ensureAccessibleDir(dirPath: string): void {
|
|
93
|
-
|
|
88
|
+
mkdirWithEpermRetry(dirPath);
|
|
94
89
|
try {
|
|
95
90
|
fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
|
|
96
91
|
} catch {
|
|
@@ -99,7 +94,7 @@ function ensureAccessibleDir(dirPath: string): void {
|
|
|
99
94
|
} catch {
|
|
100
95
|
// Best effort: retry mkdir/access even if cleanup fails.
|
|
101
96
|
}
|
|
102
|
-
|
|
97
|
+
mkdirWithEpermRetry(dirPath);
|
|
103
98
|
fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
|
|
104
99
|
}
|
|
105
100
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
type MkdirWithEpermRetryDeps = {
|
|
4
|
+
mkdir?: (path: string) => void;
|
|
5
|
+
wait?: (milliseconds: number) => void;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const RETRY_DELAY_MS = 1000;
|
|
9
|
+
const MAX_ATTEMPTS = 3;
|
|
10
|
+
const waitArray = new Int32Array(new SharedArrayBuffer(4));
|
|
11
|
+
|
|
12
|
+
function sleep(delayMs: number): void {
|
|
13
|
+
Atomics.wait(waitArray, 0, 0, delayMs);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function mkdirWithEpermRetry(dirPath: string, deps: MkdirWithEpermRetryDeps = {}): void {
|
|
17
|
+
const mkdir = deps.mkdir ?? ((path) => mkdirSync(path, { recursive: true }));
|
|
18
|
+
const wait = deps.wait ?? sleep;
|
|
19
|
+
|
|
20
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) {
|
|
21
|
+
try {
|
|
22
|
+
mkdir(dirPath);
|
|
23
|
+
return;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (
|
|
26
|
+
!(typeof error === "object" && error !== null && "code" in error && error.code === "EPERM") ||
|
|
27
|
+
attempt === MAX_ATTEMPTS
|
|
28
|
+
) {
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
wait(RETRY_DELAY_MS);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -387,10 +387,10 @@ function checkCriteriaSatisfied(criteria: ResolvedAcceptanceGate[], report: Acce
|
|
|
387
387
|
|
|
388
388
|
function reportEvidencePresent(report: AcceptanceReport, kind: AcceptanceEvidenceKind): boolean {
|
|
389
389
|
switch (kind) {
|
|
390
|
-
case "changed-files": return isStringArray(report.changedFiles)
|
|
391
|
-
case "tests-added": return isStringArray(report.testsAddedOrUpdated)
|
|
392
|
-
case "commands-run": return Array.isArray(report.commandsRun)
|
|
393
|
-
case "validation-output": return isStringArray(report.validationOutput)
|
|
390
|
+
case "changed-files": return isStringArray(report.changedFiles);
|
|
391
|
+
case "tests-added": return isStringArray(report.testsAddedOrUpdated);
|
|
392
|
+
case "commands-run": return Array.isArray(report.commandsRun);
|
|
393
|
+
case "validation-output": return isStringArray(report.validationOutput);
|
|
394
394
|
case "residual-risks": return isStringArray(report.residualRisks);
|
|
395
395
|
case "no-staged-files": return report.noStagedFiles === true;
|
|
396
396
|
case "diff-summary": return typeof report.diffSummary === "string" && report.diffSummary.trim().length > 0;
|