@tekmidian/pai 0.42.0 → 0.42.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-DGXNoTwc.mjs → chain-DsvSHnO9.mjs} +68 -37
- package/dist/chain-DsvSHnO9.mjs.map +1 -0
- package/dist/cli/index.mjs +8 -8
- package/dist/cli/program.mjs +8 -8
- package/dist/{config-DgzR5sfb.mjs → config-Bi_F6lXf.mjs} +31 -2
- package/dist/config-Bi_F6lXf.mjs.map +1 -0
- package/dist/{context-handover-cache-dUnSZwaA.mjs → context-handover-cache--bm6Siim.mjs} +27 -8
- package/dist/context-handover-cache--bm6Siim.mjs.map +1 -0
- package/dist/daemon/index.mjs +6 -6
- package/dist/{daemon-CKBkhOsZ.mjs → daemon-BOAPyL_b.mjs} +6 -6
- package/dist/{daemon-DsvP8KMy.mjs → daemon-DvXih78C.mjs} +11 -9
- package/dist/daemon-DvXih78C.mjs.map +1 -0
- package/dist/daemon-mcp/index.mjs +5 -5
- package/dist/{fallback-5UJO-RyX.mjs → fallback-8bzmg6No.mjs} +6 -6
- package/dist/fallback-8bzmg6No.mjs.map +1 -0
- package/dist/hooks/route-agents-to-worker.mjs.map +2 -2
- package/dist/hooks/worker-proxy.mjs.map +2 -2
- package/dist/hooks/worker-status-line.mjs +4 -1
- package/dist/hooks/worker-status-line.mjs.map +2 -2
- package/dist/hooks/worker-supervision.mjs.map +2 -2
- package/dist/{main-resolver-DXGuygFG.mjs → main-resolver-CqppP_Bw.mjs} +2 -2
- package/dist/{main-resolver-BgIrC-D9.mjs → main-resolver-Dip2VLB5.mjs} +2 -2
- package/dist/{main-resolver-BgIrC-D9.mjs.map → main-resolver-Dip2VLB5.mjs.map} +1 -1
- package/dist/{planner-BLDJt3ZS.mjs → planner-ogy-Zy4O.mjs} +5 -5
- package/dist/{planner-BLDJt3ZS.mjs.map → planner-ogy-Zy4O.mjs.map} +1 -1
- package/dist/{program-CruRnLtc.mjs → program-B7N5OIJR.mjs} +10 -10
- package/dist/{program-CruRnLtc.mjs.map → program-B7N5OIJR.mjs.map} +1 -1
- package/dist/{scope-NIwCGxRI.mjs → scope-CfYnjy5R.mjs} +12 -3
- package/dist/scope-CfYnjy5R.mjs.map +1 -0
- package/dist/{server-Cp7tNzNC.mjs → server-DvN-IFIV.mjs} +26 -4
- package/dist/server-DvN-IFIV.mjs.map +1 -0
- package/dist/{work-queue-worker-D1p2V-ae.mjs → work-queue-worker-19r0cpH4.mjs} +4 -4
- package/dist/{work-queue-worker-7Vkli4Yh.mjs → work-queue-worker-DXh_FnT5.mjs} +2 -2
- package/dist/{work-queue-worker-7Vkli4Yh.mjs.map → work-queue-worker-DXh_FnT5.mjs.map} +1 -1
- package/docs/merge-demo.md +1 -0
- package/package.json +1 -1
- package/dist/chain-DGXNoTwc.mjs.map +0 -1
- package/dist/config-DgzR5sfb.mjs.map +0 -1
- package/dist/context-handover-cache-dUnSZwaA.mjs.map +0 -1
- package/dist/daemon-DsvP8KMy.mjs.map +0 -1
- package/dist/fallback-5UJO-RyX.mjs.map +0 -1
- package/dist/scope-NIwCGxRI.mjs.map +0 -1
- package/dist/server-Cp7tNzNC.mjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as WorkersConfigError,
|
|
2
|
-
import { _ as workersLogDir, d as setCooldown, f as
|
|
3
|
-
import {
|
|
1
|
+
import { a as WorkersConfigError, g as resolveModelCapability, h as readWorkersSection, m as providerKeyPath, o as assertProviderRunnable, s as classModelCapability, u as isModelCapability } from "./config-Bi_F6lXf.mjs";
|
|
2
|
+
import { _ as workersLogDir, d as setCooldown, f as ensureNoMcpConfig, g as statusPath, h as panesDir, i as buildRunEnv, m as ledgerPath, n as ensureProxyRunning, o as isQuotaFailure, p as eventsPath, s as nextAutoProvider, t as DEFAULT_PROXY_PORT, u as resolveTarget } from "./server-DvN-IFIV.mjs";
|
|
3
|
+
import { C as stripPromptValues, S as shortText, _ as loadStatuses, a as resolveSpawnerSession, b as saveStatus, d as alive, g as loadStatus, i as resolveSession, l as UNLABELED, m as describeTool, o as scopeKey, r as itermUuid, v as newWorkerId, x as parseRunnerArgs, y as nowStamp } from "./scope-CfYnjy5R.mjs";
|
|
4
4
|
import { appendFileSync, closeSync, existsSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
5
5
|
import { homedir, tmpdir } from "node:os";
|
|
6
6
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
@@ -87,10 +87,14 @@ function ledgerSummary(path, scope, lastN = 15, now = /* @__PURE__ */ new Date()
|
|
|
87
87
|
* one splits the lowest live worker pane horizontally, so panes stack top to
|
|
88
88
|
* bottom. The split never sizes the new session — that would grow the whole
|
|
89
89
|
* window; instead the window's bounds are read before the split and restored
|
|
90
|
-
* right after, so the panes share the space the window already had.
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
90
|
+
* right after, so the panes share the space the window already had. The follow
|
|
91
|
+
* command is part of the split itself — iTerm creates the new session already
|
|
92
|
+
* running it — so no text is ever typed into any session afterwards; a separate
|
|
93
|
+
* typing step raced the operator's keystrokes (focus briefly sits on the new
|
|
94
|
+
* pane after a split, and in one observed failure the command landed in the
|
|
95
|
+
* operator's own shell). A split still makes the new session the tab's active
|
|
96
|
+
* one — the scripts re-select the launching session right after, so a pane
|
|
97
|
+
* opening never steals the keystrokes the operator is typing. Each
|
|
94
98
|
* pane runs `pai worker follow <id> --auto-exit <n>` under the `pai-worker`
|
|
95
99
|
* dynamic profile (Close Sessions On End), so panes disappear by themselves.
|
|
96
100
|
*
|
|
@@ -145,34 +149,35 @@ const WORKER_SPLIT_SCRIPT = `on run(argv)
|
|
|
145
149
|
end if
|
|
146
150
|
end if
|
|
147
151
|
end repeat
|
|
152
|
+
-- the follow command is part of the split itself: the
|
|
153
|
+
-- pane is born already running it, so no text is ever
|
|
154
|
+
-- typed into any session — a typing step raced the
|
|
155
|
+
-- operator's keystrokes and could even land in the
|
|
156
|
+
-- operator's own session
|
|
148
157
|
if profileName is "" then
|
|
149
158
|
if splitS is missing value then
|
|
150
159
|
tell s
|
|
151
|
-
set newS to split vertically with default profile
|
|
160
|
+
set newS to split vertically with default profile command followCmd
|
|
152
161
|
end tell
|
|
153
162
|
else
|
|
154
163
|
tell splitS
|
|
155
|
-
set newS to split horizontally with default profile
|
|
164
|
+
set newS to split horizontally with default profile command followCmd
|
|
156
165
|
end tell
|
|
157
166
|
end if
|
|
158
167
|
else
|
|
159
168
|
if splitS is missing value then
|
|
160
169
|
tell s
|
|
161
|
-
set newS to split vertically with profile profileName
|
|
170
|
+
set newS to split vertically with profile profileName command followCmd
|
|
162
171
|
end tell
|
|
163
172
|
else
|
|
164
173
|
tell splitS
|
|
165
|
-
set newS to split horizontally with profile profileName
|
|
174
|
+
set newS to split horizontally with profile profileName command followCmd
|
|
166
175
|
end tell
|
|
167
176
|
end if
|
|
168
177
|
end if
|
|
169
|
-
tell newS
|
|
170
|
-
write text followCmd
|
|
171
|
-
end tell
|
|
172
178
|
-- a split makes the new session the tab's active one:
|
|
173
|
-
-- re-select the launching session so
|
|
174
|
-
--
|
|
175
|
-
-- pane's chat prompt
|
|
179
|
+
-- re-select the launching session so focus returns to
|
|
180
|
+
-- where the operator was typing, never the new pane
|
|
176
181
|
try
|
|
177
182
|
select s
|
|
178
183
|
end try
|
|
@@ -253,11 +258,11 @@ const SPLIT_SCRIPT = `on run(argv)
|
|
|
253
258
|
repeat with t in tabs of w
|
|
254
259
|
repeat with s in sessions of t
|
|
255
260
|
if id of s is targetID then
|
|
261
|
+
-- the follow command is part of the split itself (see
|
|
262
|
+
-- WORKER_SPLIT_SCRIPT): nothing is ever typed into a
|
|
263
|
+
-- session afterwards
|
|
256
264
|
tell s
|
|
257
|
-
set newS to split vertically with default profile
|
|
258
|
-
end tell
|
|
259
|
-
tell newS
|
|
260
|
-
write text followCmd
|
|
265
|
+
set newS to split vertically with default profile command followCmd
|
|
261
266
|
end tell
|
|
262
267
|
-- keep the tab's active session where it was (see
|
|
263
268
|
-- WORKER_SPLIT_SCRIPT)
|
|
@@ -490,6 +495,23 @@ function saveRegistry(path, reg) {
|
|
|
490
495
|
writeFileSync(tmp, JSON.stringify(reg, null, 1), "utf8");
|
|
491
496
|
renameSync(tmp, path);
|
|
492
497
|
}
|
|
498
|
+
/**
|
|
499
|
+
* The command a worker pane runs: one exec-able line with no shell syntax —
|
|
500
|
+
* iTerm's split command parameter execs without a shell (proven live: panes
|
|
501
|
+
* survive `sleep 30` and a single-quoted `/bin/sh -c`, die instantly on
|
|
502
|
+
* `export …; …` or a redirect), so the command must be one exec-able line.
|
|
503
|
+
* Absolute node on the CLI entry file also sidesteps the panes' bare launchd
|
|
504
|
+
* PATH (no pai, no node) and the `#!/usr/bin/env node` shebang.
|
|
505
|
+
*/
|
|
506
|
+
function followCommand(wid, autoExitSecs) {
|
|
507
|
+
return `${process.execPath} ${absoluteCliPath()} worker follow ${wid} --auto-exit ${autoExitSecs}`;
|
|
508
|
+
}
|
|
509
|
+
/** Absolute path of the running pai CLI; falls back to PATH lookup at spawn. */
|
|
510
|
+
function absoluteCliPath() {
|
|
511
|
+
const argv1 = process.argv[1] ?? "";
|
|
512
|
+
if (argv1.endsWith("pai")) return argv1;
|
|
513
|
+
return "pai";
|
|
514
|
+
}
|
|
493
515
|
/** Open (or only report on) the stacked follow pane of one worker. */
|
|
494
516
|
async function openPaneForWorker(logDir, config, wid, term) {
|
|
495
517
|
if (workerPaneOpen(wid)) return `pane for ${wid} already open`;
|
|
@@ -497,7 +519,7 @@ async function openPaneForWorker(logDir, config, wid, term) {
|
|
|
497
519
|
const regPath = join(panesDir(logDir), `${scopeKey(term)}.json`);
|
|
498
520
|
const reg = loadRegistry(regPath);
|
|
499
521
|
const profile = await followProfile(config.pane.fontSize);
|
|
500
|
-
const cmd =
|
|
522
|
+
const cmd = followCommand(wid, config.pane.autoExitSecs);
|
|
501
523
|
const p = await osascript(WORKER_SPLIT_SCRIPT, [
|
|
502
524
|
uid,
|
|
503
525
|
[...reg].reverse().map((e) => e.session).join(","),
|
|
@@ -571,7 +593,7 @@ async function openFollowPane(logDir, _config, term, checkOnly) {
|
|
|
571
593
|
if (out === "notrunning") throw new Error("pai worker pane: iTerm2 is not running");
|
|
572
594
|
if ([...new Set(out.split(",").map((t) => t.trim()).filter(Boolean))].filter((t) => followTtys().has(t)).length > 0) return "follow pane already open";
|
|
573
595
|
if (checkOnly) return "no follow pane";
|
|
574
|
-
const s = await osascript(SPLIT_SCRIPT, [uid,
|
|
596
|
+
const s = await osascript(SPLIT_SCRIPT, [uid, `${process.execPath} ${absoluteCliPath()} worker follow`]);
|
|
575
597
|
const sout = s.stdout.trim();
|
|
576
598
|
if (sout === "opened") return "follow pane opened";
|
|
577
599
|
if (sout === "notfound") throw new Error("pai worker pane: no open iTerm2 session matches ITERM_SESSION_ID");
|
|
@@ -1627,8 +1649,12 @@ function codexInstalled() {
|
|
|
1627
1649
|
* demand, the provider name in the URL path); codex-engine providers run
|
|
1628
1650
|
* the Codex CLI.
|
|
1629
1651
|
* - headless (-p): strict empty MCP config unless the caller brings one or
|
|
1630
|
-
* names servers via --mcp / a role (then a filtered <id>.mcp.json),
|
|
1631
|
-
*
|
|
1652
|
+
* names servers via --mcp / a role (then a filtered <id>.mcp.json), the
|
|
1653
|
+
* core tool grants the caller did not bring (a headless run cannot
|
|
1654
|
+
* approve a permission-gated tool mid-flight — with no grant at all,
|
|
1655
|
+
* claude drops the file/shell tools entirely and the worker is
|
|
1656
|
+
* tool-blind), PAI_WORKER=1 so PAI's per-session hooks leave it alone,
|
|
1657
|
+
* the worker
|
|
1632
1658
|
* contract appended to the system prompt, `--input-format stream-json`
|
|
1633
1659
|
* with the prompt as the first stdin user message (the operator socket
|
|
1634
1660
|
* can add more mid-run), stream-json mirroring (every line stamped `_ts`)
|
|
@@ -1641,15 +1667,6 @@ function codexInstalled() {
|
|
|
1641
1667
|
* Auto-routed runs that die of a quota error before the first tool call are
|
|
1642
1668
|
* restarted on the next provider in routing order (WORKER-REROUTE ledger line).
|
|
1643
1669
|
*/
|
|
1644
|
-
/** The strict empty MCP config for headless workers, written on demand. */
|
|
1645
|
-
function ensureNoMcpConfig(logDir) {
|
|
1646
|
-
const path = noMcpConfigPath(logDir);
|
|
1647
|
-
if (!existsSync(path)) {
|
|
1648
|
-
mkdirSync(logDir, { recursive: true });
|
|
1649
|
-
writeFileSync(path, "{ \"mcpServers\": {} }\n", "utf8");
|
|
1650
|
-
}
|
|
1651
|
-
return path;
|
|
1652
|
-
}
|
|
1653
1670
|
/** A stream-json user message for the child's stdin. */
|
|
1654
1671
|
function stdinUserMessage(text) {
|
|
1655
1672
|
return JSON.stringify({
|
|
@@ -1725,7 +1742,7 @@ async function runWorker(opts) {
|
|
|
1725
1742
|
const logDir = workersLogDir(config);
|
|
1726
1743
|
mkdirSync(logDir, { recursive: true });
|
|
1727
1744
|
if (opts.className === "plan" && !opts._planner) {
|
|
1728
|
-
const { runPlanner } = await import("./planner-
|
|
1745
|
+
const { runPlanner } = await import("./planner-ogy-Zy4O.mjs");
|
|
1729
1746
|
return runPlanner(opts);
|
|
1730
1747
|
}
|
|
1731
1748
|
const parent = launchParent(opts.parent);
|
|
@@ -1784,6 +1801,19 @@ async function runWorker(opts) {
|
|
|
1784
1801
|
throw e;
|
|
1785
1802
|
}
|
|
1786
1803
|
}
|
|
1804
|
+
/**
|
|
1805
|
+
* The core tools a headless run grants when the caller brings none of its
|
|
1806
|
+
* own. Permission-gated tools (Bash, Read, Write, …) are never offered to a
|
|
1807
|
+
* headless session that has no allow rule for them — the child starts with
|
|
1808
|
+
* only the tools that never ask (tool search, web, cron, tasks) and cannot
|
|
1809
|
+
* touch a file or shell (2026-09-18). A caller's own --allowedTools is a
|
|
1810
|
+
* deliberate restriction and passes through untouched.
|
|
1811
|
+
*/
|
|
1812
|
+
const DEFAULT_WORKER_TOOLS = "Read,Edit,Write,Bash,Grep,Glob";
|
|
1813
|
+
/** The --allowedTools args a headless run needs; [] when the caller granted. */
|
|
1814
|
+
function headlessToolGrants(allowedTools) {
|
|
1815
|
+
return allowedTools.length ? [] : ["--allowedTools", DEFAULT_WORKER_TOOLS];
|
|
1816
|
+
}
|
|
1787
1817
|
async function executeRun(a) {
|
|
1788
1818
|
const { config, logDir, target, model, label, parsed, noPane } = a;
|
|
1789
1819
|
const headless = parsed.headless;
|
|
@@ -1872,9 +1902,10 @@ async function executeRun(a) {
|
|
|
1872
1902
|
];
|
|
1873
1903
|
}
|
|
1874
1904
|
const restArgs = headless ? stripPromptValues(parsed.rest) : parsed.rest;
|
|
1905
|
+
const toolArgs = headless ? headlessToolGrants(parsed.allowedTools) : [];
|
|
1875
1906
|
const cmd = ["claude"];
|
|
1876
1907
|
if (!parsed.callerModel) cmd.push("--model", model);
|
|
1877
|
-
cmd.push(...mcpArgs, ...restArgs);
|
|
1908
|
+
cmd.push(...mcpArgs, ...toolArgs, ...restArgs);
|
|
1878
1909
|
if (headless) {
|
|
1879
1910
|
cmd.push("--output-format", "stream-json", "--verbose", "--input-format", "stream-json");
|
|
1880
1911
|
if (!parsed.callerSystemPrompt) cmd.push("--append-system-prompt", WORKER_CONTRACT_PROMPT);
|
|
@@ -2567,4 +2598,4 @@ async function runChain(opts, deps = {}) {
|
|
|
2567
2598
|
|
|
2568
2599
|
//#endregion
|
|
2569
2600
|
export { openPaneForWorker as _, testProvider as a, renderReport as c, handoffFromInside as d, readInbox as f, openFollowPane as g, checkPaneForWorker as h, runWorker as i, discardWorker as l, workerDepth as m, swapPromptArg as n, describeMcp as o, sayToWorker as p, printResult as r, parseWorkerReport as s, runChain as t, mergeWorker as u, appendLedger as v, ledgerSummary as y };
|
|
2570
|
-
//# sourceMappingURL=chain-
|
|
2601
|
+
//# sourceMappingURL=chain-DsvSHnO9.mjs.map
|