@cydm/pie 2.6.6 → 2.6.7
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/dist/builtin/extensions/ask-user/index.js +3 -3
- package/dist/builtin/extensions/mcp-adapter/index.js +93 -38
- package/dist/builtin/extensions/plan-mode/index.js +3 -3
- package/dist/builtin/extensions/subagent/index.js +362 -6
- package/dist/builtin/extensions/todo/index.js +3 -3
- package/dist/builtin/skills/chrome-devtools-axi/chrome-devtools-axi-runtime.js +1 -1
- package/dist/builtin/skills/chrome-devtools-axi/{cli-QAILYGLM.js → cli-4ITIR57L.js} +31 -2
- package/dist/chunks/{chunk-G5B6D35K.js → chunk-7B4IIBVS.js} +10 -1
- package/dist/chunks/{chunk-KEARZX6F.js → chunk-PAC62XU4.js} +1 -1
- package/dist/chunks/{chunk-H6BAM2N4.js → chunk-T6GMEDIE.js} +476 -69
- package/dist/chunks/{chunk-OWSXALCG.js → chunk-VUKF7U7Q.js} +1 -1
- package/dist/chunks/{src-ZJXBS2DB.js → src-HPIBCE2I.js} +2 -2
- package/dist/chunks/{test-stream-TI2S25NY.js → test-stream-Y7WXELAJ.js} +1 -1
- package/dist/cli.js +2475 -2469
- package/package.json +2 -2
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
createAskUserCapability
|
|
4
|
-
} from "../../../chunks/chunk-
|
|
5
|
-
import "../../../chunks/chunk-
|
|
6
|
-
import "../../../chunks/chunk-
|
|
4
|
+
} from "../../../chunks/chunk-T6GMEDIE.js";
|
|
5
|
+
import "../../../chunks/chunk-PAC62XU4.js";
|
|
6
|
+
import "../../../chunks/chunk-7B4IIBVS.js";
|
|
7
7
|
import "../../../chunks/chunk-XXGYUYXI.js";
|
|
8
8
|
import "../../../chunks/chunk-LZQXQOPJ.js";
|
|
9
9
|
|
|
@@ -24461,6 +24461,47 @@ function redactSecrets(text, secrets) {
|
|
|
24461
24461
|
}
|
|
24462
24462
|
return sanitized;
|
|
24463
24463
|
}
|
|
24464
|
+
function escapeRegExpLiteral(literal2) {
|
|
24465
|
+
return literal2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
24466
|
+
}
|
|
24467
|
+
function percentEncodeAll(secret) {
|
|
24468
|
+
return [...Buffer.from(secret, "utf-8")].map((byte) => `%${byte.toString(16).toUpperCase().padStart(2, "0")}`).join("");
|
|
24469
|
+
}
|
|
24470
|
+
function shortEncodedSecretTokens(secrets) {
|
|
24471
|
+
const tokens = { base64: [], percent: [] };
|
|
24472
|
+
for (const secret of secrets) {
|
|
24473
|
+
try {
|
|
24474
|
+
const base64url2 = Buffer.from(secret, "utf-8").toString("base64url");
|
|
24475
|
+
if (base64url2 !== secret && base64url2.length > 0 && base64url2.length < 4) tokens.base64.push(base64url2);
|
|
24476
|
+
} catch {
|
|
24477
|
+
}
|
|
24478
|
+
try {
|
|
24479
|
+
const urlEncoded = encodeURIComponent(secret);
|
|
24480
|
+
if (urlEncoded !== secret && urlEncoded.length > 0 && urlEncoded.length < 4 && /%[0-9A-F]{2}/i.test(urlEncoded)) {
|
|
24481
|
+
tokens.percent.push(urlEncoded);
|
|
24482
|
+
}
|
|
24483
|
+
const percentAll = percentEncodeAll(secret);
|
|
24484
|
+
if (percentAll !== secret && percentAll.length > 0 && percentAll.length < 4) tokens.percent.push(percentAll);
|
|
24485
|
+
} catch {
|
|
24486
|
+
}
|
|
24487
|
+
}
|
|
24488
|
+
return tokens;
|
|
24489
|
+
}
|
|
24490
|
+
function collectPatternSpans(text, pattern, spans) {
|
|
24491
|
+
for (const match of text.matchAll(new RegExp(`(?=(${pattern.source}))`, pattern.flags))) {
|
|
24492
|
+
spans.push({ start: match.index, end: match.index + match[1].length });
|
|
24493
|
+
}
|
|
24494
|
+
}
|
|
24495
|
+
function collectShortEncodedTokens(text, secrets, spans) {
|
|
24496
|
+
const { base64: base642, percent } = shortEncodedSecretTokens(secrets);
|
|
24497
|
+
for (const token of base642) {
|
|
24498
|
+
const padding = "=".repeat((4 - token.length % 4) % 4);
|
|
24499
|
+
collectPatternSpans(text, new RegExp(`(?<![A-Za-z0-9+/_-])${escapeRegExpLiteral(token)}(?:${padding})?(?![A-Za-z0-9+/=_-])`, "g"), spans);
|
|
24500
|
+
}
|
|
24501
|
+
for (const token of percent) {
|
|
24502
|
+
collectPatternSpans(text, new RegExp(`(?<![%A-Za-z0-9])${escapeRegExpLiteral(token)}(?![%A-Za-z0-9])`, "gi"), spans);
|
|
24503
|
+
}
|
|
24504
|
+
}
|
|
24464
24505
|
function encodedSecretVariants(secrets) {
|
|
24465
24506
|
const variants = [...secrets];
|
|
24466
24507
|
for (const secret of secrets) {
|
|
@@ -24475,7 +24516,7 @@ function encodedSecretVariants(secrets) {
|
|
|
24475
24516
|
}
|
|
24476
24517
|
try {
|
|
24477
24518
|
const urlEncoded = encodeURIComponent(secret);
|
|
24478
|
-
if (urlEncoded !== secret && urlEncoded.length >= 4) {
|
|
24519
|
+
if (urlEncoded !== secret && urlEncoded.length >= 4 && urlEncoded !== percentEncodeAll(secret)) {
|
|
24479
24520
|
variants.push(urlEncoded);
|
|
24480
24521
|
const urlLowerHex = urlEncoded.replace(/%[0-9A-F]{2}/g, (m) => `%${m.slice(1).toLowerCase()}`);
|
|
24481
24522
|
if (urlLowerHex !== urlEncoded) variants.push(urlLowerHex);
|
|
@@ -24517,9 +24558,9 @@ function hexSecretVariants(secrets) {
|
|
|
24517
24558
|
return variants;
|
|
24518
24559
|
}
|
|
24519
24560
|
var MIN_SECRET_FRAGMENT = 8;
|
|
24520
|
-
function
|
|
24561
|
+
function collectSecretFragments(text, secrets, spans) {
|
|
24521
24562
|
const candidates = secrets.filter((secret) => secret.length >= MIN_SECRET_FRAGMENT);
|
|
24522
|
-
if (candidates.length === 0 || text.length < MIN_SECRET_FRAGMENT) return
|
|
24563
|
+
if (candidates.length === 0 || text.length < MIN_SECRET_FRAGMENT) return;
|
|
24523
24564
|
const grams = /* @__PURE__ */ new Map();
|
|
24524
24565
|
for (const secret of candidates) {
|
|
24525
24566
|
for (let offset = 0; offset + MIN_SECRET_FRAGMENT <= secret.length; offset++) {
|
|
@@ -24529,9 +24570,7 @@ function redactSecretFragments(text, secrets) {
|
|
|
24529
24570
|
else grams.set(gram, [{ secret, offset }]);
|
|
24530
24571
|
}
|
|
24531
24572
|
}
|
|
24532
|
-
let
|
|
24533
|
-
let index = 0;
|
|
24534
|
-
while (index < text.length) {
|
|
24573
|
+
for (let index = 0; index < text.length; index++) {
|
|
24535
24574
|
const bucket = grams.get(text.slice(index, index + MIN_SECRET_FRAGMENT));
|
|
24536
24575
|
let best = 0;
|
|
24537
24576
|
if (bucket) {
|
|
@@ -24542,61 +24581,78 @@ function redactSecretFragments(text, secrets) {
|
|
|
24542
24581
|
if (length > best) best = length;
|
|
24543
24582
|
}
|
|
24544
24583
|
}
|
|
24545
|
-
if (best > 0) {
|
|
24546
|
-
out += "[redacted]";
|
|
24547
|
-
index += best;
|
|
24548
|
-
} else {
|
|
24549
|
-
out += text[index];
|
|
24550
|
-
index += 1;
|
|
24551
|
-
}
|
|
24584
|
+
if (best > 0) spans.push({ start: index, end: index + best });
|
|
24552
24585
|
}
|
|
24553
|
-
return out;
|
|
24554
24586
|
}
|
|
24555
|
-
function
|
|
24556
|
-
if (secrets.length === 0 || text.length === 0) return
|
|
24587
|
+
function collectLeadingSecretSuffix(text, secrets, spans) {
|
|
24588
|
+
if (secrets.length === 0 || text.length === 0) return;
|
|
24557
24589
|
for (const secret of secrets) {
|
|
24558
24590
|
for (let keep = Math.min(secret.length - 1, text.length); keep >= 4; keep--) {
|
|
24559
24591
|
if (text.startsWith(secret.slice(secret.length - keep))) {
|
|
24560
|
-
|
|
24592
|
+
spans.push({ start: 0, end: keep });
|
|
24593
|
+
break;
|
|
24561
24594
|
}
|
|
24562
24595
|
}
|
|
24563
24596
|
}
|
|
24564
|
-
return text;
|
|
24565
24597
|
}
|
|
24566
|
-
function
|
|
24567
|
-
if (secrets.length === 0 || text.length === 0) return
|
|
24598
|
+
function collectTrailingSecretPrefix(text, secrets, spans) {
|
|
24599
|
+
if (secrets.length === 0 || text.length === 0) return;
|
|
24568
24600
|
for (const secret of secrets) {
|
|
24569
24601
|
for (let keep = Math.min(secret.length - 1, text.length); keep >= 4; keep--) {
|
|
24570
24602
|
if (text.endsWith(secret.slice(0, keep))) {
|
|
24571
|
-
|
|
24603
|
+
spans.push({ start: text.length - keep, end: text.length });
|
|
24604
|
+
break;
|
|
24572
24605
|
}
|
|
24573
24606
|
}
|
|
24574
24607
|
}
|
|
24575
|
-
return text;
|
|
24576
24608
|
}
|
|
24577
24609
|
function redactAllSecrets(text, secrets) {
|
|
24578
24610
|
const encoded = encodedSecretVariants(secrets);
|
|
24579
24611
|
const wholeVariants = [...encoded, ...hexSecretVariants(secrets), ...unicodeEscapeVariants(secrets)];
|
|
24580
|
-
|
|
24581
|
-
|
|
24582
|
-
|
|
24583
|
-
|
|
24584
|
-
|
|
24585
|
-
|
|
24586
|
-
|
|
24587
|
-
|
|
24588
|
-
|
|
24589
|
-
);
|
|
24612
|
+
const spans = [];
|
|
24613
|
+
for (const variant of new Set(wholeVariants)) {
|
|
24614
|
+
if (!variant) continue;
|
|
24615
|
+
for (let start = text.indexOf(variant); start !== -1; start = text.indexOf(variant, start + 1)) {
|
|
24616
|
+
spans.push({ start, end: start + variant.length });
|
|
24617
|
+
}
|
|
24618
|
+
}
|
|
24619
|
+
collectCaseInsensitiveEncodedSecrets(text, secrets, spans);
|
|
24620
|
+
collectShortEncodedTokens(text, secrets, spans);
|
|
24621
|
+
collectSecretFragments(text, encoded, spans);
|
|
24622
|
+
collectLeadingSecretSuffix(text, encoded, spans);
|
|
24623
|
+
collectTrailingSecretPrefix(text, encoded, spans);
|
|
24624
|
+
spans.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
24625
|
+
const merged = [];
|
|
24626
|
+
for (const span of spans) {
|
|
24627
|
+
const previous = merged[merged.length - 1];
|
|
24628
|
+
if (previous && span.start < previous.end) previous.end = Math.max(previous.end, span.end);
|
|
24629
|
+
else merged.push({ ...span });
|
|
24630
|
+
}
|
|
24631
|
+
let out = "";
|
|
24632
|
+
let cursor = 0;
|
|
24633
|
+
for (const { start, end } of merged) {
|
|
24634
|
+
out += text.slice(cursor, start) + "[redacted]";
|
|
24635
|
+
cursor = end;
|
|
24636
|
+
}
|
|
24637
|
+
return out + text.slice(cursor);
|
|
24590
24638
|
}
|
|
24591
|
-
function
|
|
24592
|
-
const escapeRegExpLiteral = (literal2) => literal2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
24593
|
-
let sanitized = text;
|
|
24639
|
+
function collectCaseInsensitiveEncodedSecrets(text, secrets, spans) {
|
|
24594
24640
|
for (const secret of secrets) {
|
|
24595
24641
|
const patterns = [];
|
|
24596
24642
|
try {
|
|
24597
24643
|
const urlEncoded = encodeURIComponent(secret);
|
|
24598
24644
|
if (urlEncoded !== secret && urlEncoded.length >= 4 && /%[0-9A-F]{2}/i.test(urlEncoded)) {
|
|
24599
|
-
|
|
24645
|
+
const urlPattern = urlEncoded.split(/(%[0-9A-F]{2})/g).map(
|
|
24646
|
+
(part) => /^%[0-9A-F]{2}$/.test(part) ? part.replace(/[A-F]/g, (digit) => `[${digit}${digit.toLowerCase()}]`) : escapeRegExpLiteral(part)
|
|
24647
|
+
).join("");
|
|
24648
|
+
collectPatternSpans(text, new RegExp(urlPattern, "g"), spans);
|
|
24649
|
+
}
|
|
24650
|
+
} catch {
|
|
24651
|
+
}
|
|
24652
|
+
try {
|
|
24653
|
+
const percentAll = percentEncodeAll(secret);
|
|
24654
|
+
if (percentAll !== secret && percentAll.length >= 4 && /%[0-9A-F]{2}/i.test(percentAll)) {
|
|
24655
|
+
patterns.push(percentAll);
|
|
24600
24656
|
}
|
|
24601
24657
|
} catch {
|
|
24602
24658
|
}
|
|
@@ -24610,10 +24666,9 @@ function redactCaseInsensitiveEncodedSecrets(text, secrets) {
|
|
|
24610
24666
|
const escaped = unicodeEscapeAll(secret);
|
|
24611
24667
|
if (escaped !== secret && escaped.length >= 4) patterns.push(escaped);
|
|
24612
24668
|
for (const pattern of patterns) {
|
|
24613
|
-
|
|
24669
|
+
collectPatternSpans(text, new RegExp(escapeRegExpLiteral(pattern), "gi"), spans);
|
|
24614
24670
|
}
|
|
24615
24671
|
}
|
|
24616
|
-
return sanitized;
|
|
24617
24672
|
}
|
|
24618
24673
|
function redactStderrTail(tail, secrets) {
|
|
24619
24674
|
return redactAllSecrets(tail, secrets);
|
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
isPlanModeSafeCommand,
|
|
8
8
|
markCompletedPlanSteps,
|
|
9
9
|
restoreExecutionState
|
|
10
|
-
} from "../../../chunks/chunk-
|
|
11
|
-
import "../../../chunks/chunk-
|
|
12
|
-
import "../../../chunks/chunk-
|
|
10
|
+
} from "../../../chunks/chunk-T6GMEDIE.js";
|
|
11
|
+
import "../../../chunks/chunk-PAC62XU4.js";
|
|
12
|
+
import "../../../chunks/chunk-7B4IIBVS.js";
|
|
13
13
|
import {
|
|
14
14
|
Type
|
|
15
15
|
} from "../../../chunks/chunk-XXGYUYXI.js";
|
|
@@ -1,20 +1,372 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
createCliHostCapabilities
|
|
4
|
-
} from "../../../chunks/chunk-
|
|
4
|
+
} from "../../../chunks/chunk-VUKF7U7Q.js";
|
|
5
5
|
import {
|
|
6
|
+
SUBAGENT_SESSION_RECORD_VERSION,
|
|
6
7
|
createSharedFileSystemTools,
|
|
7
8
|
createSubagentCapability
|
|
8
|
-
} from "../../../chunks/chunk-
|
|
9
|
-
import "../../../chunks/chunk-
|
|
10
|
-
import "../../../chunks/chunk-
|
|
9
|
+
} from "../../../chunks/chunk-T6GMEDIE.js";
|
|
10
|
+
import "../../../chunks/chunk-PAC62XU4.js";
|
|
11
|
+
import "../../../chunks/chunk-7B4IIBVS.js";
|
|
11
12
|
import "../../../chunks/chunk-5353DEEC.js";
|
|
12
13
|
import "../../../chunks/chunk-XXGYUYXI.js";
|
|
13
14
|
import "../../../chunks/chunk-LZQXQOPJ.js";
|
|
14
15
|
|
|
16
|
+
// src/files/subagent-session-store.ts
|
|
17
|
+
import { execFileSync } from "node:child_process";
|
|
18
|
+
import fs from "node:fs";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
var MAX_RECORD_BYTES = 4 * 1024 * 1024;
|
|
21
|
+
function isSafeSessionId(id) {
|
|
22
|
+
return /^[A-Za-z0-9._-]+$/.test(id);
|
|
23
|
+
}
|
|
24
|
+
var RECLAIM_ORPHAN_MS = 6e4;
|
|
25
|
+
var MALFORMED_LOCK_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
26
|
+
function defaultReadProcessStartTime(pid) {
|
|
27
|
+
if (!Number.isInteger(pid) || pid <= 0) return void 0;
|
|
28
|
+
if (process.platform === "win32") {
|
|
29
|
+
try {
|
|
30
|
+
const output = execFileSync(
|
|
31
|
+
"powershell",
|
|
32
|
+
[
|
|
33
|
+
"-NoProfile",
|
|
34
|
+
"-NonInteractive",
|
|
35
|
+
"-Command",
|
|
36
|
+
`$p = Get-CimInstance Win32_Process -Filter "ProcessId=${pid}"; if ($p) { $p.CreationDate.ToString('o') }`
|
|
37
|
+
],
|
|
38
|
+
{ encoding: "utf-8", timeout: 4e3 }
|
|
39
|
+
);
|
|
40
|
+
const line = output.split(/\r?\n/).find((entry) => entry.trim().length > 0);
|
|
41
|
+
return line?.trim();
|
|
42
|
+
} catch {
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const output = execFileSync("ps", ["-p", String(pid), "-o", "lstart="], {
|
|
48
|
+
encoding: "utf-8",
|
|
49
|
+
timeout: 1e3,
|
|
50
|
+
env: { ...process.env, LC_ALL: "C", LANG: "C", TZ: "UTC" }
|
|
51
|
+
});
|
|
52
|
+
const trimmed = output.trim();
|
|
53
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
54
|
+
} catch {
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function leaseOwnerAlive(owner, readStartTime) {
|
|
59
|
+
if (!Number.isInteger(owner.pid) || owner.pid <= 0) return void 0;
|
|
60
|
+
try {
|
|
61
|
+
process.kill(owner.pid, 0);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error.code === "ESRCH") return false;
|
|
64
|
+
}
|
|
65
|
+
if (typeof owner.startedAt !== "string" || owner.startedAt.length === 0) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
const current = readStartTime(owner.pid);
|
|
69
|
+
return current === void 0 || current === owner.startedAt;
|
|
70
|
+
}
|
|
71
|
+
function publishLinkedExclusive(targetPath, content, stagingToken) {
|
|
72
|
+
const staging = `${targetPath}.stage-${stagingToken}`;
|
|
73
|
+
let staged = false;
|
|
74
|
+
try {
|
|
75
|
+
fs.writeFileSync(staging, content, { mode: 384, flag: "wx" });
|
|
76
|
+
staged = true;
|
|
77
|
+
fs.linkSync(staging, targetPath);
|
|
78
|
+
} finally {
|
|
79
|
+
if (staged) {
|
|
80
|
+
try {
|
|
81
|
+
fs.unlinkSync(staging);
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function publishReclaimMarker(reclaimPath, marker) {
|
|
88
|
+
publishLinkedExclusive(reclaimPath, JSON.stringify(marker), marker.token);
|
|
89
|
+
}
|
|
90
|
+
function reclaimMarkerOrphaned(content, mtimeMs, now, readStartTime) {
|
|
91
|
+
try {
|
|
92
|
+
const marker = JSON.parse(content);
|
|
93
|
+
const alive = leaseOwnerAlive(marker, readStartTime);
|
|
94
|
+
if (alive !== void 0) return !alive;
|
|
95
|
+
} catch {
|
|
96
|
+
}
|
|
97
|
+
return now() - mtimeMs > RECLAIM_ORPHAN_MS;
|
|
98
|
+
}
|
|
99
|
+
function removeOrphanedReclaimMarker(reclaimPath, now, readStartTime) {
|
|
100
|
+
let content;
|
|
101
|
+
let mtimeMs;
|
|
102
|
+
try {
|
|
103
|
+
content = fs.readFileSync(reclaimPath, "utf8");
|
|
104
|
+
mtimeMs = fs.statSync(reclaimPath).mtimeMs;
|
|
105
|
+
} catch {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
if (!reclaimMarkerOrphaned(content, mtimeMs, now, readStartTime)) return false;
|
|
109
|
+
let current;
|
|
110
|
+
try {
|
|
111
|
+
current = fs.readFileSync(reclaimPath, "utf8");
|
|
112
|
+
} catch {
|
|
113
|
+
current = void 0;
|
|
114
|
+
}
|
|
115
|
+
if (current !== content) return false;
|
|
116
|
+
try {
|
|
117
|
+
fs.unlinkSync(reclaimPath);
|
|
118
|
+
} catch {
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
function createSubagentSessionStore(sessionsDir, log, deps) {
|
|
123
|
+
const now = deps?.now ?? Date.now;
|
|
124
|
+
const readStartTime = deps?.readProcessStartTime ?? defaultReadProcessStartTime;
|
|
125
|
+
let selfStartTime;
|
|
126
|
+
let selfStartTimeRead = false;
|
|
127
|
+
const ownStartTime = () => {
|
|
128
|
+
if (!selfStartTimeRead) {
|
|
129
|
+
selfStartTime = readStartTime(process.pid);
|
|
130
|
+
selfStartTimeRead = true;
|
|
131
|
+
}
|
|
132
|
+
return selfStartTime;
|
|
133
|
+
};
|
|
134
|
+
const recordPath = (id) => {
|
|
135
|
+
if (!isSafeSessionId(id)) return void 0;
|
|
136
|
+
const filePath = path.join(sessionsDir, `${id}.json`);
|
|
137
|
+
return path.dirname(filePath) === sessionsDir ? filePath : void 0;
|
|
138
|
+
};
|
|
139
|
+
const recordLeaseHeld = (filePath) => {
|
|
140
|
+
let markerStat;
|
|
141
|
+
let markerContent;
|
|
142
|
+
try {
|
|
143
|
+
markerStat = fs.statSync(`${filePath}.lock.reclaim`);
|
|
144
|
+
markerContent = fs.readFileSync(`${filePath}.lock.reclaim`, "utf8");
|
|
145
|
+
} catch (error) {
|
|
146
|
+
const code = error.code;
|
|
147
|
+
if (code === "ENOENT") {
|
|
148
|
+
} else return true;
|
|
149
|
+
}
|
|
150
|
+
if (markerContent !== void 0 && !reclaimMarkerOrphaned(markerContent, markerStat.mtimeMs, now, readStartTime)) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
let lockStat;
|
|
154
|
+
let content;
|
|
155
|
+
try {
|
|
156
|
+
lockStat = fs.statSync(`${filePath}.lock`);
|
|
157
|
+
content = fs.readFileSync(`${filePath}.lock`, "utf8");
|
|
158
|
+
} catch (error) {
|
|
159
|
+
const code = error.code;
|
|
160
|
+
if (code === "ENOENT") return false;
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const lock = JSON.parse(content);
|
|
165
|
+
const alive = leaseOwnerAlive(lock, readStartTime);
|
|
166
|
+
if (alive !== void 0) return alive;
|
|
167
|
+
} catch {
|
|
168
|
+
}
|
|
169
|
+
return now() - lockStat.mtimeMs <= MALFORMED_LOCK_MAX_AGE_MS;
|
|
170
|
+
};
|
|
171
|
+
const acquireLease = (id) => {
|
|
172
|
+
const filePath = recordPath(id);
|
|
173
|
+
if (!filePath) throw new Error(`Invalid subagent session id "${id}"`);
|
|
174
|
+
const lockPath = `${filePath}.lock`;
|
|
175
|
+
fs.mkdirSync(sessionsDir, { recursive: true });
|
|
176
|
+
const token = `${process.pid}-${now()}-${Math.random()}`;
|
|
177
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
178
|
+
try {
|
|
179
|
+
const owner = { pid: process.pid, startedAt: ownStartTime(), timestamp: now(), token };
|
|
180
|
+
publishLinkedExclusive(lockPath, JSON.stringify(owner), token);
|
|
181
|
+
return () => {
|
|
182
|
+
try {
|
|
183
|
+
if (JSON.parse(fs.readFileSync(lockPath, "utf8")).token === token) fs.unlinkSync(lockPath);
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
} catch (error) {
|
|
188
|
+
if (error.code !== "EEXIST") throw error;
|
|
189
|
+
const reclaimPath = `${lockPath}.reclaim`;
|
|
190
|
+
const publishMarker = () => {
|
|
191
|
+
publishReclaimMarker(reclaimPath, { pid: process.pid, startedAt: ownStartTime(), token });
|
|
192
|
+
};
|
|
193
|
+
try {
|
|
194
|
+
publishMarker();
|
|
195
|
+
} catch (reclaimError) {
|
|
196
|
+
if (reclaimError.code !== "EEXIST") throw reclaimError;
|
|
197
|
+
if (!removeOrphanedReclaimMarker(reclaimPath, now, readStartTime)) {
|
|
198
|
+
throw new Error(`Subagent session "${id}" is busy (session lease held by another process).`);
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
publishMarker();
|
|
202
|
+
} catch {
|
|
203
|
+
throw new Error(`Subagent session "${id}" is busy (session lease held by another process).`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const stat = fs.statSync(lockPath);
|
|
208
|
+
let lockContent;
|
|
209
|
+
try {
|
|
210
|
+
lockContent = fs.readFileSync(lockPath, "utf8");
|
|
211
|
+
} catch (readError) {
|
|
212
|
+
if (readError.code === "ENOENT") continue;
|
|
213
|
+
throw new Error(`Subagent session "${id}" is busy (session lease is unreadable).`);
|
|
214
|
+
}
|
|
215
|
+
let stale = false;
|
|
216
|
+
try {
|
|
217
|
+
const lock = JSON.parse(lockContent);
|
|
218
|
+
const alive = leaseOwnerAlive(lock, readStartTime);
|
|
219
|
+
stale = alive === void 0 ? now() - stat.mtimeMs > MALFORMED_LOCK_MAX_AGE_MS : !alive;
|
|
220
|
+
} catch {
|
|
221
|
+
stale = now() - stat.mtimeMs > MALFORMED_LOCK_MAX_AGE_MS;
|
|
222
|
+
}
|
|
223
|
+
if (!stale) throw new Error(`Subagent session "${id}" is busy (session lease held by another process).`);
|
|
224
|
+
let current;
|
|
225
|
+
try {
|
|
226
|
+
current = fs.readFileSync(lockPath, "utf8");
|
|
227
|
+
} catch {
|
|
228
|
+
current = void 0;
|
|
229
|
+
}
|
|
230
|
+
if (current !== lockContent) {
|
|
231
|
+
throw new Error(`Subagent session "${id}" is busy (session lease changed during reclamation).`);
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
fs.unlinkSync(lockPath);
|
|
235
|
+
} catch {
|
|
236
|
+
}
|
|
237
|
+
} finally {
|
|
238
|
+
try {
|
|
239
|
+
if (JSON.parse(fs.readFileSync(reclaimPath, "utf8")).token === token) fs.unlinkSync(reclaimPath);
|
|
240
|
+
} catch {
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
throw new Error(`Subagent session "${id}" is busy (could not acquire session lease).`);
|
|
246
|
+
};
|
|
247
|
+
return {
|
|
248
|
+
acquire(id) {
|
|
249
|
+
return acquireLease(id);
|
|
250
|
+
},
|
|
251
|
+
load(id) {
|
|
252
|
+
const filePath = recordPath(id);
|
|
253
|
+
if (!filePath) return void 0;
|
|
254
|
+
let content;
|
|
255
|
+
try {
|
|
256
|
+
content = fs.readFileSync(filePath, "utf8");
|
|
257
|
+
} catch (error) {
|
|
258
|
+
const code = error.code;
|
|
259
|
+
if (code === "ENOENT" || code === "ENOTDIR") return void 0;
|
|
260
|
+
throw error;
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
const parsed = JSON.parse(content);
|
|
264
|
+
if (parsed && typeof parsed === "object" && typeof parsed.version === "number" && parsed.version !== SUBAGENT_SESSION_RECORD_VERSION) {
|
|
265
|
+
throw new Error(`session record version ${parsed.version} is not readable by this build (supported version ${SUBAGENT_SESSION_RECORD_VERSION}); it may be history written by a newer Pie`);
|
|
266
|
+
}
|
|
267
|
+
if (!parsed || typeof parsed !== "object" || parsed.version !== SUBAGENT_SESSION_RECORD_VERSION || parsed.id !== id || !Array.isArray(parsed.messages)) {
|
|
268
|
+
return void 0;
|
|
269
|
+
}
|
|
270
|
+
return parsed;
|
|
271
|
+
} catch (error) {
|
|
272
|
+
if (error instanceof SyntaxError) {
|
|
273
|
+
return void 0;
|
|
274
|
+
}
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
save(record) {
|
|
279
|
+
const filePath = recordPath(record.id);
|
|
280
|
+
if (!filePath) return false;
|
|
281
|
+
try {
|
|
282
|
+
const payload = JSON.stringify(record);
|
|
283
|
+
if (Buffer.byteLength(payload, "utf8") > MAX_RECORD_BYTES) {
|
|
284
|
+
log?.(`[subagent-sessions] skipping persist of ${record.id}: over the ${MAX_RECORD_BYTES}-byte cap`);
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
fs.mkdirSync(sessionsDir, { recursive: true });
|
|
288
|
+
const tmpPath = `${filePath}.tmp-${process.pid}`;
|
|
289
|
+
try {
|
|
290
|
+
const fd = fs.openSync(tmpPath, "w", 384);
|
|
291
|
+
try {
|
|
292
|
+
fs.writeFileSync(fd, payload);
|
|
293
|
+
fs.fsyncSync(fd);
|
|
294
|
+
} finally {
|
|
295
|
+
fs.closeSync(fd);
|
|
296
|
+
}
|
|
297
|
+
fs.renameSync(tmpPath, filePath);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
try {
|
|
300
|
+
fs.unlinkSync(tmpPath);
|
|
301
|
+
} catch {
|
|
302
|
+
}
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
const dirFd = fs.openSync(sessionsDir, "r");
|
|
307
|
+
try {
|
|
308
|
+
fs.fsyncSync(dirFd);
|
|
309
|
+
} finally {
|
|
310
|
+
fs.closeSync(dirFd);
|
|
311
|
+
}
|
|
312
|
+
} catch (error) {
|
|
313
|
+
const code = error.code;
|
|
314
|
+
if (!(code === "EISDIR" || code === "EPERM" || code === "EINVAL" || code === "ENOTSUP" || code === "EOPNOTSUPP" || code === "EBUSY")) {
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return true;
|
|
319
|
+
} catch (error) {
|
|
320
|
+
log?.(`[subagent-sessions] persist of ${record.id} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
sweep({ maxAgeMs, maxRecords }) {
|
|
325
|
+
try {
|
|
326
|
+
if (!fs.existsSync(sessionsDir)) return;
|
|
327
|
+
const nowMs = now();
|
|
328
|
+
const entries = fs.readdirSync(sessionsDir).filter((file) => file.endsWith(".json")).map((file) => {
|
|
329
|
+
try {
|
|
330
|
+
const stat = fs.statSync(path.join(sessionsDir, file));
|
|
331
|
+
return { file, mtimeMs: stat.mtimeMs, dev: stat.dev, ino: stat.ino };
|
|
332
|
+
} catch {
|
|
333
|
+
return { file, mtimeMs: 0, dev: -1, ino: -1 };
|
|
334
|
+
}
|
|
335
|
+
}).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
336
|
+
entries.forEach((entry, index) => {
|
|
337
|
+
if (index < maxRecords && nowMs - entry.mtimeMs <= maxAgeMs) return;
|
|
338
|
+
const filePath = path.join(sessionsDir, entry.file);
|
|
339
|
+
if (recordLeaseHeld(filePath)) return;
|
|
340
|
+
let release;
|
|
341
|
+
try {
|
|
342
|
+
release = acquireLease(entry.file.slice(0, -".json".length));
|
|
343
|
+
} catch {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
try {
|
|
348
|
+
const stat = fs.statSync(filePath);
|
|
349
|
+
if (stat.dev !== entry.dev || stat.ino !== entry.ino || stat.mtimeMs !== entry.mtimeMs) return;
|
|
350
|
+
} catch {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
try {
|
|
354
|
+
fs.unlinkSync(filePath);
|
|
355
|
+
} catch {
|
|
356
|
+
}
|
|
357
|
+
} finally {
|
|
358
|
+
void Promise.resolve(release()).catch(() => void 0);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
} catch {
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
15
367
|
// builtin/extensions/subagent/index.ts
|
|
16
368
|
import * as os from "os";
|
|
17
|
-
import * as
|
|
369
|
+
import * as path2 from "path";
|
|
18
370
|
function subagentExtension(ctx) {
|
|
19
371
|
ctx.log("Subagent extension loaded (transparent parallel mode)");
|
|
20
372
|
const resolveRuntimePair = () => {
|
|
@@ -25,6 +377,10 @@ function subagentExtension(ctx) {
|
|
|
25
377
|
};
|
|
26
378
|
const skills = ctx.skills || [];
|
|
27
379
|
const capability = createSubagentCapability({
|
|
380
|
+
// Persisted alongside the CLI's own session state: a sessionId from an
|
|
381
|
+
// earlier result stays recallable across restarts and /resume. Tools and
|
|
382
|
+
// credentials are re-resolved at recall time, never persisted.
|
|
383
|
+
sessionStore: createSubagentSessionStore(path2.join(os.homedir(), ".pie", "subagent-sessions"), (message) => ctx.log(message)),
|
|
28
384
|
get apiKey() {
|
|
29
385
|
return resolveRuntimePair().apiKey;
|
|
30
386
|
},
|
|
@@ -47,7 +403,7 @@ function subagentExtension(ctx) {
|
|
|
47
403
|
if (mode === "edit") {
|
|
48
404
|
throw new Error("Edit delegation requires the parent's executable runtime tools; fallback tools are read-mode only.");
|
|
49
405
|
}
|
|
50
|
-
const configDir =
|
|
406
|
+
const configDir = path2.join(os.homedir(), ".pie");
|
|
51
407
|
const fsOptions = ctx.yoloMode ? { allowlistedDirs: ["/"] } : {
|
|
52
408
|
accessRoots: [
|
|
53
409
|
ctx.cwd,
|
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
executeManageTodoList,
|
|
9
9
|
executionStateToTodos,
|
|
10
10
|
restoreExecutionState
|
|
11
|
-
} from "../../../chunks/chunk-
|
|
12
|
-
import "../../../chunks/chunk-
|
|
13
|
-
import "../../../chunks/chunk-
|
|
11
|
+
} from "../../../chunks/chunk-T6GMEDIE.js";
|
|
12
|
+
import "../../../chunks/chunk-PAC62XU4.js";
|
|
13
|
+
import "../../../chunks/chunk-7B4IIBVS.js";
|
|
14
14
|
import "../../../chunks/chunk-XXGYUYXI.js";
|
|
15
15
|
import "../../../chunks/chunk-LZQXQOPJ.js";
|
|
16
16
|
|
|
@@ -21,6 +21,6 @@ function tryFastPath(argv, options) {
|
|
|
21
21
|
|
|
22
22
|
// vendor/chrome-devtools-axi/bin/chrome-devtools-axi.ts
|
|
23
23
|
if (!tryFastPath(process.argv.slice(2), { version: VERSION })) {
|
|
24
|
-
const { main } = await import("./cli-
|
|
24
|
+
const { main } = await import("./cli-4ITIR57L.js");
|
|
25
25
|
await main(process.argv.slice(2));
|
|
26
26
|
}
|