@tekmidian/pai 0.66.0 → 0.66.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/dist/{chain-D4PuQ2c-.mjs → chain-CZfKIEq9.mjs} +2 -2
- package/dist/{chain-D4PuQ2c-.mjs.map → chain-CZfKIEq9.mjs.map} +1 -1
- package/dist/cli/index.mjs +5 -5
- package/dist/cli/program.mjs +5 -5
- package/dist/daemon/index.mjs +3 -3
- package/dist/{daemon-BEoVgVfJ.mjs → daemon-DXlaVCkw.mjs} +3 -3
- package/dist/{daemon-BEoVgVfJ.mjs.map → daemon-DXlaVCkw.mjs.map} +1 -1
- package/dist/{daemon-DPAYU9hp.mjs → daemon-DyORmxZu.mjs} +3 -3
- package/dist/daemon-mcp/index.mjs +6 -4
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/{fallback-B5svk75f.mjs → fallback-CIiq2Raw.mjs} +6 -4
- package/dist/fallback-CIiq2Raw.mjs.map +1 -0
- package/dist/hooks/block-sleep-poll.mjs.map +2 -2
- package/dist/hooks/load-project-context.mjs.map +2 -2
- package/dist/hooks/worker-guard.mjs +5 -0
- package/dist/hooks/worker-guard.mjs.map +2 -2
- package/dist/hooks/worker-status-line.mjs +1 -0
- package/dist/hooks/worker-status-line.mjs.map +2 -2
- package/dist/{planner-EJPZnnif.mjs → planner-Dm5Acyo1.mjs} +3 -3
- package/dist/{planner-EJPZnnif.mjs.map → planner-Dm5Acyo1.mjs.map} +1 -1
- package/dist/{program-DSETzrWg.mjs → program-JJ0TwSHI.mjs} +9 -6
- package/dist/{program-DSETzrWg.mjs.map → program-JJ0TwSHI.mjs.map} +1 -1
- package/dist/{run-Z4akoa8G.mjs → run-BCpbxqA5.mjs} +168 -6
- package/dist/run-BCpbxqA5.mjs.map +1 -0
- package/dist/{session-keepalive-BDhytXEo.mjs → session-keepalive-DxjTHGcK.mjs} +2 -2
- package/dist/{session-keepalive-BDhytXEo.mjs.map → session-keepalive-DxjTHGcK.mjs.map} +1 -1
- package/package.json +1 -1
- package/src/hooks/ts/lib/worker-guard.test.ts +12 -0
- package/src/hooks/ts/lib/worker-guard.ts +9 -0
- package/dist/fallback-B5svk75f.mjs.map +0 -1
- package/dist/run-Z4akoa8G.mjs.map +0 -1
|
@@ -1774,6 +1774,126 @@ function itermForClaudeSession(logDir, claudeSession, now = Date.now()) {
|
|
|
1774
1774
|
return term ? itermUuid(term) : null;
|
|
1775
1775
|
}
|
|
1776
1776
|
|
|
1777
|
+
//#endregion
|
|
1778
|
+
//#region src/workers/endreason.ts
|
|
1779
|
+
/**
|
|
1780
|
+
* Who or what ended a worker. One place decides the reason; the runner's
|
|
1781
|
+
* signal handlers and the deadline record it, `pai worker kill` and the daemon
|
|
1782
|
+
* leave a kill-request marker (`<logDir>/<id>.kill`) before signalling so the
|
|
1783
|
+
* runner can tell their stops from an external one.
|
|
1784
|
+
*/
|
|
1785
|
+
const killMarkerPath = (logDir, id) => join(logDir, `${id}.kill`);
|
|
1786
|
+
/** Best description of the invoking session: PAI parent/AIBroker session env, else the Claude Code session id. */
|
|
1787
|
+
function callerDescription(env = process.env) {
|
|
1788
|
+
const name = env.PAI_WORKER_PARENT ?? env.PAI_SESSION_NAME ?? env.AIBROKER_SESSION_NAME ?? env.AIBROKER_SESSION;
|
|
1789
|
+
if (name) return name;
|
|
1790
|
+
if (env.CLAUDE_CODE_SESSION_ID) return `claude-session:${env.CLAUDE_CODE_SESSION_ID}`;
|
|
1791
|
+
return env.CLAUDECODE ? "claude-code" : "shell";
|
|
1792
|
+
}
|
|
1793
|
+
/** Write the kill-request marker and the WORKER-KILL ledger line; call right before signalling. */
|
|
1794
|
+
function requestKill(logDir, ledger, id, by = callerDescription()) {
|
|
1795
|
+
const req = {
|
|
1796
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1797
|
+
by,
|
|
1798
|
+
pid: process.pid,
|
|
1799
|
+
ppid: process.ppid,
|
|
1800
|
+
cwd: process.cwd()
|
|
1801
|
+
};
|
|
1802
|
+
writeFileSync(killMarkerPath(logDir, id), JSON.stringify(req));
|
|
1803
|
+
appendLedger(ledger, "WORKER-KILL", {
|
|
1804
|
+
id,
|
|
1805
|
+
by,
|
|
1806
|
+
pid: req.pid,
|
|
1807
|
+
ppid: req.ppid,
|
|
1808
|
+
cwd: req.cwd
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
function readMarker(logDir, id) {
|
|
1812
|
+
const p = killMarkerPath(logDir, id);
|
|
1813
|
+
if (!existsSync(p)) return null;
|
|
1814
|
+
try {
|
|
1815
|
+
return JSON.parse(readFileSync(p, "utf8"));
|
|
1816
|
+
} catch {
|
|
1817
|
+
return {
|
|
1818
|
+
ts: "",
|
|
1819
|
+
by: "unknown",
|
|
1820
|
+
pid: 0,
|
|
1821
|
+
ppid: 0,
|
|
1822
|
+
cwd: ""
|
|
1823
|
+
};
|
|
1824
|
+
} finally {
|
|
1825
|
+
try {
|
|
1826
|
+
unlinkSync(p);
|
|
1827
|
+
} catch {}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
function pidAlive(pid) {
|
|
1831
|
+
try {
|
|
1832
|
+
process.kill(pid, 0);
|
|
1833
|
+
return true;
|
|
1834
|
+
} catch (e) {
|
|
1835
|
+
return e.code === "EPERM";
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
/** Command name of a pid (`ps -o comm=`), or undefined when it is gone or ps fails. */
|
|
1839
|
+
function commOf(pid) {
|
|
1840
|
+
try {
|
|
1841
|
+
return execFileSync("ps", [
|
|
1842
|
+
"-o",
|
|
1843
|
+
"comm=",
|
|
1844
|
+
"-p",
|
|
1845
|
+
String(pid)
|
|
1846
|
+
], { encoding: "utf8" }).trim() || void 0;
|
|
1847
|
+
} catch {
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
/** Parent facts recorded at start, for the end report ("parent: claude"). */
|
|
1852
|
+
function parentInfo() {
|
|
1853
|
+
const parentPid = process.ppid;
|
|
1854
|
+
return {
|
|
1855
|
+
parentPid,
|
|
1856
|
+
parentComm: commOf(parentPid),
|
|
1857
|
+
parentBackground: process.env.CLAUDECODE ? true : void 0
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
/**
|
|
1861
|
+
* The single decision. `signal` = the signal the runner received (absent for a
|
|
1862
|
+
* deadline stop or a normal exit); `timedOut` = the deadline fired. Reads (and
|
|
1863
|
+
* consumes) the kill marker.
|
|
1864
|
+
*/
|
|
1865
|
+
function decideEndReason(a) {
|
|
1866
|
+
if (a.timedOut) return { reason: "deadline" };
|
|
1867
|
+
if (!a.signal) return { reason: "exit" };
|
|
1868
|
+
const marker = readMarker(a.logDir, a.id);
|
|
1869
|
+
if (marker) return {
|
|
1870
|
+
reason: "pai-worker-kill",
|
|
1871
|
+
by: marker.by,
|
|
1872
|
+
note: `${a.signal} requested by pid ${marker.pid}`
|
|
1873
|
+
};
|
|
1874
|
+
const alive = (a.isAlive ?? pidAlive)(a.parentPid ?? process.ppid);
|
|
1875
|
+
const parent = `parent ${a.parentPid ?? "?"}${a.parentComm ? ` (${a.parentComm})` : ""}`;
|
|
1876
|
+
if (!alive) return {
|
|
1877
|
+
reason: "parent-gone",
|
|
1878
|
+
note: `${a.signal}; ${parent} not alive`
|
|
1879
|
+
};
|
|
1880
|
+
return {
|
|
1881
|
+
reason: "external-signal",
|
|
1882
|
+
note: `${a.signal}; sender unknown (not deadline, not pai worker kill); a Claude Code background task stop (TaskStop, Bash timeout, session end) is the usual source; ${parent} alive` + (a.parentBackground ? "; started from a Claude Code session" : "")
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
/** The status/ledger fields for an end. */
|
|
1886
|
+
function endFields(e) {
|
|
1887
|
+
return {
|
|
1888
|
+
reason: e.reason,
|
|
1889
|
+
...e.by ? { by: e.by } : {}
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
/** "external-signal", "pai-worker-kill by <caller>" for a finished worker; null when nothing was recorded. */
|
|
1893
|
+
function endLabel(s) {
|
|
1894
|
+
return s.endReason ? `${s.endReason}${s.endBy ? ` by ${s.endBy}` : ""}` : null;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1777
1897
|
//#endregion
|
|
1778
1898
|
//#region src/workers/controls.ts
|
|
1779
1899
|
/**
|
|
@@ -3090,6 +3210,7 @@ function fullContractPrompt(format) {
|
|
|
3090
3210
|
"116 s). Ending your turn ends this process, so a background job gets no later wake-up here:",
|
|
3091
3211
|
"run_in_background is denied. Run the long command in the FOREGROUND with a Bash timeout up to 600000 ms",
|
|
3092
3212
|
"and split longer jobs into steps that each fit it.",
|
|
3213
|
+
"Kill only processes you started, by PID (`cmd & echo $! > /tmp/x.pid`, then `kill $(cat /tmp/x.pid)`); never `pkill`/`killall`/`kill $(pgrep ...)` — other workers' command lines contain your spec text.",
|
|
3093
3214
|
...fileNotInlineBlock(),
|
|
3094
3215
|
"",
|
|
3095
3216
|
`Operator messages: a user turn starting with ${OPERATOR_MARK} was typed by the operator while you`,
|
|
@@ -3774,7 +3895,7 @@ async function runWorker(opts) {
|
|
|
3774
3895
|
if (gc?.length) process.stderr.write(`pai worker: archived ${gc.length} leftover worktree(s) (pai worker gc)\n`);
|
|
3775
3896
|
} catch {}
|
|
3776
3897
|
if (opts.className === "plan" && !opts._planner) {
|
|
3777
|
-
const { runPlanner } = await import("./planner-
|
|
3898
|
+
const { runPlanner } = await import("./planner-Dm5Acyo1.mjs");
|
|
3778
3899
|
return runPlanner(opts);
|
|
3779
3900
|
}
|
|
3780
3901
|
const parent = launchParent(opts.parent);
|
|
@@ -4003,6 +4124,22 @@ function fireDeadline(logDir, status, ledger, limit) {
|
|
|
4003
4124
|
minutes: limit.minutes
|
|
4004
4125
|
});
|
|
4005
4126
|
}
|
|
4127
|
+
/** Decide why a signal ended the run and put it on the status; the caller writes WORKER-END. */
|
|
4128
|
+
function recordSignalEnd(logDir, status, signal) {
|
|
4129
|
+
const end = decideEndReason({
|
|
4130
|
+
logDir,
|
|
4131
|
+
id: status.id,
|
|
4132
|
+
signal,
|
|
4133
|
+
timedOut: status.timedOut,
|
|
4134
|
+
parentPid: status.parentPid,
|
|
4135
|
+
parentComm: status.parentComm,
|
|
4136
|
+
parentBackground: status.parentBackground
|
|
4137
|
+
});
|
|
4138
|
+
status.endReason = end.reason;
|
|
4139
|
+
status.endBy = end.by;
|
|
4140
|
+
status.endNote = end.note;
|
|
4141
|
+
return end;
|
|
4142
|
+
}
|
|
4006
4143
|
async function finaliseRun(f) {
|
|
4007
4144
|
const { logDir, ledger, ok, rc } = f;
|
|
4008
4145
|
let { status, report } = f;
|
|
@@ -4044,12 +4181,19 @@ async function finaliseRun(f) {
|
|
|
4044
4181
|
reason: orphanReason
|
|
4045
4182
|
});
|
|
4046
4183
|
}
|
|
4184
|
+
const end = decideEndReason({
|
|
4185
|
+
logDir,
|
|
4186
|
+
id: wid,
|
|
4187
|
+
timedOut: f.timedOut !== void 0
|
|
4188
|
+
});
|
|
4189
|
+
status.endReason = end.reason;
|
|
4047
4190
|
saveStatus(logDir, status);
|
|
4048
4191
|
appendLedger(ledger, "WORKER-END", {
|
|
4049
4192
|
id: wid,
|
|
4050
4193
|
...f.end,
|
|
4051
4194
|
rc,
|
|
4052
|
-
secs: f.secs
|
|
4195
|
+
secs: f.secs,
|
|
4196
|
+
...endFields(end)
|
|
4053
4197
|
});
|
|
4054
4198
|
if (f.worktree) status = recordWorktree(logDir, status, f.worktree, ok);
|
|
4055
4199
|
const early = await f.beforeHandoff?.(status, report);
|
|
@@ -4297,6 +4441,7 @@ async function executeRun(a) {
|
|
|
4297
4441
|
let status = {
|
|
4298
4442
|
id: wid,
|
|
4299
4443
|
pid: process.pid,
|
|
4444
|
+
...parentInfo(),
|
|
4300
4445
|
label,
|
|
4301
4446
|
cwd,
|
|
4302
4447
|
term,
|
|
@@ -4409,7 +4554,8 @@ async function executeRun(a) {
|
|
|
4409
4554
|
status.state = "killed";
|
|
4410
4555
|
status.rc = 143;
|
|
4411
4556
|
status.secs = Math.floor((Date.now() - t0) / 1e3);
|
|
4412
|
-
|
|
4557
|
+
const end = recordSignalEnd(logDir, status, sig);
|
|
4558
|
+
status.last = `killed by signal ${sig} (${end.reason}${end.by ? ` by ${end.by}` : ""})`;
|
|
4413
4559
|
saveStatus(logDir, status);
|
|
4414
4560
|
appendLedger(ledger, "WORKER-END", {
|
|
4415
4561
|
id: wid,
|
|
@@ -4419,6 +4565,8 @@ async function executeRun(a) {
|
|
|
4419
4565
|
rc: 143,
|
|
4420
4566
|
secs: status.secs,
|
|
4421
4567
|
killed: 1,
|
|
4568
|
+
...endFields(end),
|
|
4569
|
+
note: end.note,
|
|
4422
4570
|
label
|
|
4423
4571
|
});
|
|
4424
4572
|
if (worktree) {
|
|
@@ -4684,6 +4832,7 @@ async function executeCodexRun(a) {
|
|
|
4684
4832
|
let status = {
|
|
4685
4833
|
id: wid,
|
|
4686
4834
|
pid: process.pid,
|
|
4835
|
+
...parentInfo(),
|
|
4687
4836
|
label,
|
|
4688
4837
|
cwd,
|
|
4689
4838
|
term,
|
|
@@ -4776,8 +4925,21 @@ async function executeCodexRun(a) {
|
|
|
4776
4925
|
status.state = "killed";
|
|
4777
4926
|
status.rc = 143;
|
|
4778
4927
|
status.secs = Math.floor((Date.now() - t0) / 1e3);
|
|
4779
|
-
|
|
4928
|
+
const end = recordSignalEnd(logDir, status, sig);
|
|
4929
|
+
status.last = `killed by signal ${sig} (${end.reason}${end.by ? ` by ${end.by}` : ""})`;
|
|
4780
4930
|
saveStatus(logDir, status);
|
|
4931
|
+
appendLedger(ledger, "WORKER-END", {
|
|
4932
|
+
id: wid,
|
|
4933
|
+
provider: target.providerName,
|
|
4934
|
+
mode: "headless",
|
|
4935
|
+
model,
|
|
4936
|
+
rc: 143,
|
|
4937
|
+
secs: status.secs,
|
|
4938
|
+
killed: 1,
|
|
4939
|
+
...endFields(end),
|
|
4940
|
+
note: end.note,
|
|
4941
|
+
label
|
|
4942
|
+
});
|
|
4781
4943
|
if (worktree) {
|
|
4782
4944
|
salvageOnExit(ledger, wid, label, worktree.dir, SIGNAL_SALVAGE_MS);
|
|
4783
4945
|
try {
|
|
@@ -4992,5 +5154,5 @@ function resultFromOutput(out) {
|
|
|
4992
5154
|
}
|
|
4993
5155
|
|
|
4994
5156
|
//#endregion
|
|
4995
|
-
export {
|
|
4996
|
-
//# sourceMappingURL=run-
|
|
5157
|
+
export { parseRunnerArgs as $, mergeWorker as A, contextPercent as B, resolveSession as C, discardWorker as D, workerInScope as E, ledgerSummary as F, loadStatuses as G, isChatPane as H, UNLABELED as I, ownsPid as J, newWorkerId as K, ageOf as L, verifyWorker as M, worktreesDir as N, gcWorktrees as O, appendLedger as P, longInlinePromptHint as Q, alive as R, itermUuid as S, sessionTag as T, isLive as U, elapsedOf as V, loadStatus as W, setWorkerLabel as X, saveStatus as Y, waitForTerminalStatus as Z, callerDescription as _, deadlineLabel as a, currentTabKey as b, handoffFromInside as c, workerDepth as d, shortText as et, checkPaneForWorker as f, markControlsHeld as g, clickrControlsArgv as h, usageContextTokens as i, expandMcpNames as it, uncommittedPaths as j, git as k, readInbox as l, openPaneForWorker as m, runWorker as n, projectLaunchConfig as nt, parseWorkerReport as o, openFollowPane as p, nowStamp as q, testProvider as r, describeMcp as rt, renderReport as s, printResult as t, pinNotices as tt, sayToWorker as u, endLabel as v, sessionMapPath as w, itermForClaudeSession as x, requestKill as y, contextLabel as z };
|
|
5158
|
+
//# sourceMappingURL=run-BCpbxqA5.mjs.map
|