@tekmidian/pai 0.36.1 → 0.37.0
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/{auto-route-Byf8ENXj.mjs → auto-route-DM7GhJ8y.mjs} +2 -2
- package/dist/{auto-route-Byf8ENXj.mjs.map → auto-route-DM7GhJ8y.mjs.map} +1 -1
- package/dist/cli/index.mjs +5 -4
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/program.d.mts.map +1 -1
- package/dist/cli/program.mjs +5 -4
- package/dist/{clusters-wZgTCYCB.mjs → clusters-Do4tEGyc.mjs} +1 -1
- package/dist/{clusters-wZgTCYCB.mjs.map → clusters-Do4tEGyc.mjs.map} +1 -1
- package/dist/{context-handover-cache-PtNvj_8D.mjs → context-handover-cache-BpUojjsi.mjs} +2 -2
- package/dist/{context-handover-cache-PtNvj_8D.mjs.map → context-handover-cache-BpUojjsi.mjs.map} +1 -1
- package/dist/daemon/index.mjs +8 -8
- package/dist/{daemon-BZ93KRBo.mjs → daemon-CGg1VCbA.mjs} +25 -25
- package/dist/{daemon-BZ93KRBo.mjs.map → daemon-CGg1VCbA.mjs.map} +1 -1
- package/dist/daemon-DsGGiIJM.mjs +20 -0
- package/dist/daemon-mcp/index.mjs +428 -1
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/{detector--Gg5JRN5.mjs → detector-CMap-9vw.mjs} +1 -1
- package/dist/{detector--Gg5JRN5.mjs.map → detector-CMap-9vw.mjs.map} +1 -1
- package/dist/detector-DtLExmHN.mjs +5 -0
- package/dist/factory-BXzqRYVZ.mjs +3 -0
- package/dist/{factory-Bsp7xOpO.mjs → factory-DD2T33C9.mjs} +5 -5
- package/dist/{factory-Bsp7xOpO.mjs.map → factory-DD2T33C9.mjs.map} +1 -1
- package/dist/hooks/context-compression-hook.mjs +164 -14
- package/dist/hooks/context-compression-hook.mjs.map +4 -4
- package/dist/hooks/initialize-session.mjs +6 -0
- package/dist/hooks/initialize-session.mjs.map +3 -3
- package/dist/hooks/load-core-context.mjs +6 -0
- package/dist/hooks/load-core-context.mjs.map +3 -3
- package/dist/hooks/load-project-context.mjs +6 -0
- package/dist/hooks/load-project-context.mjs.map +3 -3
- package/dist/hooks/post-compact-inject.mjs +48 -7
- package/dist/hooks/post-compact-inject.mjs.map +4 -4
- package/dist/hooks/route-agents-to-worker.mjs +456 -0
- package/dist/hooks/route-agents-to-worker.mjs.map +7 -0
- package/dist/hooks/status-line.mjs +400 -0
- package/dist/hooks/status-line.mjs.map +7 -0
- package/dist/hooks/stop-hook.mjs +6 -0
- package/dist/hooks/stop-hook.mjs.map +3 -3
- package/dist/hooks/subagent-stop-hook.mjs +6 -0
- package/dist/hooks/subagent-stop-hook.mjs.map +3 -3
- package/dist/hooks/sync-todo-to-md.mjs +6 -0
- package/dist/hooks/sync-todo-to-md.mjs.map +3 -3
- package/dist/hooks/update-tab-on-action.mjs +6 -0
- package/dist/hooks/update-tab-on-action.mjs.map +3 -3
- package/dist/hooks/update-tab-titles.mjs +6 -0
- package/dist/hooks/update-tab-titles.mjs.map +3 -3
- package/dist/hooks/worker-proxy.mjs +796 -0
- package/dist/hooks/worker-proxy.mjs.map +7 -0
- package/dist/hooks/worker-status-line.mjs +483 -0
- package/dist/hooks/worker-status-line.mjs.map +7 -0
- package/dist/{indexer-backend-nQZuEx6N.mjs → indexer-backend-Cox9BCo-.mjs} +1 -1
- package/dist/{indexer-backend-nQZuEx6N.mjs.map → indexer-backend-Cox9BCo-.mjs.map} +1 -1
- package/dist/{kg-entity-DbOMPdF9.mjs → kg-entity-r8duqhi9.mjs} +1 -1
- package/dist/{kg-entity-DbOMPdF9.mjs.map → kg-entity-r8duqhi9.mjs.map} +1 -1
- package/dist/{latent-ideas-Bn6A5-5P.mjs → latent-ideas-BC1oINZ-.mjs} +2 -2
- package/dist/{latent-ideas-Bn6A5-5P.mjs.map → latent-ideas-BC1oINZ-.mjs.map} +1 -1
- package/dist/{link-boost-fYjUnxCN.mjs → link-boost-QFLrJwD6.mjs} +1 -1
- package/dist/{link-boost-fYjUnxCN.mjs.map → link-boost-QFLrJwD6.mjs.map} +1 -1
- package/dist/{main-resolver-BAbhKpeX.mjs → main-resolver-DlaLOFBA.mjs} +1 -1
- package/dist/{main-resolver-BAbhKpeX.mjs.map → main-resolver-DlaLOFBA.mjs.map} +1 -1
- package/dist/{main-resolver-Dxh444GO.mjs → main-resolver-IhZo4pI0.mjs} +1 -1
- package/dist/{neighborhood-DpaEM991.mjs → neighborhood-D9MJ1c8f.mjs} +1 -1
- package/dist/{neighborhood-DpaEM991.mjs.map → neighborhood-D9MJ1c8f.mjs.map} +1 -1
- package/dist/{note-context-DrcY4cWm.mjs → note-context-d1wT_-GA.mjs} +1 -1
- package/dist/{note-context-DrcY4cWm.mjs.map → note-context-d1wT_-GA.mjs.map} +1 -1
- package/dist/{postgres-BVme6qX0.mjs → postgres--BjPtLa0.mjs} +1 -1
- package/dist/{postgres-BVme6qX0.mjs.map → postgres--BjPtLa0.mjs.map} +1 -1
- package/dist/{program-C-fUghPv.mjs → program-CEIHn_Ma.mjs} +693 -101
- package/dist/program-CEIHn_Ma.mjs.map +1 -0
- package/dist/providers-sXcK5bDZ.mjs +3067 -0
- package/dist/providers-sXcK5bDZ.mjs.map +1 -0
- package/dist/{query-feedback-C1T6kS18.mjs → query-feedback-BUJxgw5B.mjs} +1 -1
- package/dist/{query-feedback-C1T6kS18.mjs.map → query-feedback-BUJxgw5B.mjs.map} +1 -1
- package/dist/query-feedback-DhyLOe5S.mjs +3 -0
- package/dist/router-1zi8jiNF.mjs +3 -0
- package/dist/{router-CsDm7HvK.mjs → router-DK_sLsUL.mjs} +1 -1
- package/dist/{router-CsDm7HvK.mjs.map → router-DK_sLsUL.mjs.map} +1 -1
- package/dist/skills/Worker/SKILL.md +41 -0
- package/dist/{sources-D8ZdNfvK.mjs → sources-Bi7--33T.mjs} +1 -1
- package/dist/{sources-D8ZdNfvK.mjs.map → sources-Bi7--33T.mjs.map} +1 -1
- package/dist/{sqlite-D1IaR8Am.mjs → sqlite-DtaL1glm.mjs} +1 -1
- package/dist/{sqlite-D1IaR8Am.mjs.map → sqlite-DtaL1glm.mjs.map} +1 -1
- package/dist/{state-qtmrBWCm.mjs → state-8Hm9E4tW.mjs} +1 -1
- package/dist/{state-WaXhLr6R.mjs → state-BY2L6-vX.mjs} +1 -1
- package/dist/{state-WaXhLr6R.mjs.map → state-BY2L6-vX.mjs.map} +1 -1
- package/dist/{themes-XPkj_bfP.mjs → themes-BN0a2duq.mjs} +1 -1
- package/dist/{themes-XPkj_bfP.mjs.map → themes-BN0a2duq.mjs.map} +1 -1
- package/dist/{tools-DEt6YPfc.mjs → tools-CGPqpU3A.mjs} +1 -1
- package/dist/{tools-ceiy7ANX.mjs → tools-y2bJpKom.mjs} +14 -14
- package/dist/{tools-ceiy7ANX.mjs.map → tools-y2bJpKom.mjs.map} +1 -1
- package/dist/{trace-DfyGmMG_.mjs → trace-h23JCcFD.mjs} +1 -1
- package/dist/{trace-DfyGmMG_.mjs.map → trace-h23JCcFD.mjs.map} +1 -1
- package/dist/{vault-indexer-CFvlPUMB.mjs → vault-indexer-DgsPjMgs.mjs} +1 -1
- package/dist/{vault-indexer-CFvlPUMB.mjs.map → vault-indexer-DgsPjMgs.mjs.map} +1 -1
- package/dist/{work-queue-worker-B8W8_3Rn.mjs → work-queue-worker-Dva_v_pI.mjs} +4 -4
- package/dist/{work-queue-worker-B8W8_3Rn.mjs.map → work-queue-worker-Dva_v_pI.mjs.map} +1 -1
- package/dist/{work-queue-worker-HN2Ufg-L.mjs → work-queue-worker-gsKd2LJa.mjs} +4 -4
- package/dist/{zettelkasten-CvjmMghT.mjs → zettelkasten-vo7psPdT.mjs} +3 -3
- package/dist/{zettelkasten-CvjmMghT.mjs.map → zettelkasten-vo7psPdT.mjs.map} +1 -1
- package/docs/commands/README.md +28 -0
- package/docs/commands/backup.md +1 -1
- package/docs/commands/clear-names.md +1 -1
- package/docs/commands/daemon.md +1 -1
- package/docs/commands/db.md +1 -1
- package/docs/commands/end.md +1 -1
- package/docs/commands/help.md +1 -1
- package/docs/commands/identity.md +1 -1
- package/docs/commands/kg.md +1 -1
- package/docs/commands/mcp.md +1 -1
- package/docs/commands/memory.md +1 -1
- package/docs/commands/notify.md +1 -1
- package/docs/commands/observation.md +1 -1
- package/docs/commands/obsidian.md +1 -1
- package/docs/commands/pause.md +1 -1
- package/docs/commands/project.md +1 -1
- package/docs/commands/projects.md +1 -1
- package/docs/commands/registry.md +1 -1
- package/docs/commands/restore.md +1 -1
- package/docs/commands/session.md +1 -1
- package/docs/commands/sessions.md +1 -1
- package/docs/commands/setup.md +1 -1
- package/docs/commands/shell-init.md +1 -1
- package/docs/commands/skill.md +1 -1
- package/docs/commands/task.md +1 -1
- package/docs/commands/topic.md +1 -1
- package/docs/commands/update.md +1 -1
- package/docs/commands/worker.md +361 -0
- package/docs/commands/zettel.md +1 -1
- package/docs/worker.md +303 -0
- package/package.json +1 -1
- package/scripts/build-hooks.mjs +46 -1
- package/src/hooks/pre-compact.sh +1 -0
- package/src/hooks/session-autosave.sh +1 -0
- package/src/hooks/session-stop.sh +1 -0
- package/src/hooks/ts/lib/context-fill.test.ts +104 -0
- package/src/hooks/ts/lib/context-fill.ts +73 -0
- package/src/hooks/ts/lib/handover-evidence.ts +227 -0
- package/src/hooks/ts/lib/worker-session.test.ts +24 -0
- package/src/hooks/ts/lib/worker-session.ts +23 -0
- package/src/hooks/ts/post-tool-use/sync-todo-to-md.ts +2 -0
- package/src/hooks/ts/post-tool-use/update-tab-on-action.ts +2 -0
- package/src/hooks/ts/pre-compact/context-compression-hook.ts +17 -0
- package/src/hooks/ts/pre-tool-use/route-agents-to-worker.ts +128 -0
- package/src/hooks/ts/session-start/initialize-session.ts +2 -0
- package/src/hooks/ts/session-start/load-core-context.ts +2 -0
- package/src/hooks/ts/session-start/load-project-context.ts +2 -0
- package/src/hooks/ts/session-start/post-compact-inject.ts +13 -0
- package/src/hooks/ts/stop/stop-hook.ts +2 -0
- package/src/hooks/ts/subagent-stop/subagent-stop-hook.ts +2 -0
- package/src/hooks/ts/user-prompt/update-tab-titles.ts +2 -0
- package/statusline-command.sh +17 -0
- package/dist/daemon-DRdoA489.mjs +0 -20
- package/dist/detector-DGAk1iBR.mjs +0 -5
- package/dist/factory-CrokPMk2.mjs +0 -3
- package/dist/program-C-fUghPv.mjs.map +0 -1
- package/dist/query-feedback-BBMBp96K.mjs +0 -3
- package/dist/router-BMkOb62X.mjs +0 -3
|
@@ -7,13 +7,14 @@ import { i as indexProject, n as indexAll, t as embedChunks } from "./sync-BWbe8
|
|
|
7
7
|
import { t as STOP_WORDS } from "./stop-words-Hfu8u22w.mjs";
|
|
8
8
|
import { a as searchMemory, i as populateSlugs } from "./search-Rpk1cSBC.mjs";
|
|
9
9
|
import { n as formatDetection, r as formatDetectionJson, t as detectProject } from "./detect-CdaA48EI.mjs";
|
|
10
|
-
import { _ as findMovedProjects, a as findLatestNote, c as extractAndStoreTriples, f as resolveHome, g as claudeProjectsDir, h as upsertSession, i as applyContinue, l as cmdScan, m as upsertProject, o as findNotesDir$1, p as saveScanConfig, r as appendCheckpointToNote, s as readBodyFile, t as readContextHandoverCache, u as loadScanConfig, v as scanTranscriptFolders, y as transcriptFiles } from "./context-handover-cache-
|
|
10
|
+
import { _ as findMovedProjects, a as findLatestNote, c as extractAndStoreTriples, f as resolveHome, g as claudeProjectsDir, h as upsertSession, i as applyContinue, l as cmdScan, m as upsertProject, o as findNotesDir$1, p as saveScanConfig, r as appendCheckpointToNote, s as readBodyFile, t as readContextHandoverCache, u as loadScanConfig, v as scanTranscriptFolders, y as transcriptFiles } from "./context-handover-cache-BpUojjsi.mjs";
|
|
11
11
|
import { a as schedulerLogPath, i as paiSocketPath, n as daemonLogPath, r as daemonPidPath } from "./runtime-paths-rni52zHX.mjs";
|
|
12
12
|
import { t as PaiClient } from "./ipc-client-BmypMNYk.mjs";
|
|
13
13
|
import { a as expandHome, c as UNROUTED, i as ensureConfigDir, n as CONFIG_FILE$2, o as loadConfig, s as OWNER_LABEL_PREFIX, t as CONFIG_DIR } from "./config-B64vFg14.mjs";
|
|
14
|
-
import { r as humanDuration, t as createStorageBackend } from "./factory-
|
|
15
|
-
import { s as
|
|
16
|
-
import {
|
|
14
|
+
import { r as humanDuration, t as createStorageBackend } from "./factory-DD2T33C9.mjs";
|
|
15
|
+
import { A as readJsonStrict, C as loadStatus, D as workersLogDir, E as ledgerPath, O as WorkersConfigError, S as openPaneForWorker, T as eventsPath, _ as stopProxy, a as setRole, b as checkPaneForWorker, c as useProvider, d as replayOutput, f as statusLineOutput, g as ensureProxyRunning, h as DEFAULT_PROXY_PORT, i as setProviderEnabled, j as writeJsonAtomic, k as readWorkersSection, l as followWorkers, m as testProvider, n as describeProviders, o as setWorkersEnabled, p as runWorker, r as removeProvider, s as unsetRole, t as addProvider, u as psOutput, v as sayToWorker, x as openFollowPane, y as describeMcp } from "./providers-sXcK5bDZ.mjs";
|
|
16
|
+
import { s as kgQuery } from "./kg-entity-r8duqhi9.mjs";
|
|
17
|
+
import { _ as scanSessions, a as renderDedupedSessions, c as probeResume, d as callAiBroker, f as fetchLiveSessions, g as resolveSessionByNameOrId, h as fmtAge, i as normalizeName$2, l as restoreTopLevel, m as sendToSession, o as hasConversation, p as revealItermSession, r as buildDeduped, s as launchInDir, t as cmdMain, u as printExitDir } from "./main-resolver-DlaLOFBA.mjs";
|
|
17
18
|
import { appendFileSync, chmodSync, copyFileSync, createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
18
19
|
import { homedir, platform, tmpdir } from "node:os";
|
|
19
20
|
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
@@ -1888,7 +1889,7 @@ function cmdUnregister(db, slug, opts = {}) {
|
|
|
1888
1889
|
//#region src/cli/commands/project/projects-index.ts
|
|
1889
1890
|
function registerProjectsCommands(projectsCmd, getDb) {
|
|
1890
1891
|
projectsCmd.command("list", { isDefault: true }).description("Show the unified deduped listing (same as `pai`).\nUse --all to include cold / zero-session / archived projects.").option("--all", "Show all entries including cold / zero-session / archived projects").option("-n, --n <count>", "Max rows to show", "20").action(async (opts) => {
|
|
1891
|
-
const { cmdMain } = await import("./main-resolver-
|
|
1892
|
+
const { cmdMain } = await import("./main-resolver-IhZo4pI0.mjs");
|
|
1892
1893
|
await cmdMain(getDb(), void 0, void 0, opts);
|
|
1893
1894
|
});
|
|
1894
1895
|
projectsCmd.command("list-raw").description("Raw registry listing with slug, path, status, last_active columns").option("--all", "Include archived projects (default: active only)").option("--status <status>", "Filter by status: active | archived").option("--tag <tag>", "Filter by tag").option("--type <type>", "Filter by type").action((opts) => {
|
|
@@ -2960,9 +2961,9 @@ function registerStatsCommands(memoryCmd, getDb) {
|
|
|
2960
2961
|
//#region src/cli/commands/memory/sources-cmd.ts
|
|
2961
2962
|
function registerSourcesCommand(memoryCmd) {
|
|
2962
2963
|
memoryCmd.command("sources").description("Show what the indexer has taken in: composition by source, which roots\ncontent enters from, the heaviest single files, and how many chunks are\nrewritten per day (which distinguishes a finite backlog from a treadmill).").option("--limit <n>", "Rows per section (default 8)", "8").action(async (opts) => {
|
|
2963
|
-
const { createStorageBackend } = await import("./factory-
|
|
2964
|
+
const { createStorageBackend } = await import("./factory-BXzqRYVZ.mjs");
|
|
2964
2965
|
const { loadConfig } = await import("./config-C_ErGddD.mjs");
|
|
2965
|
-
const { cmdMemorySources } = await import("./sources-
|
|
2966
|
+
const { cmdMemorySources } = await import("./sources-Bi7--33T.mjs");
|
|
2966
2967
|
let backend;
|
|
2967
2968
|
try {
|
|
2968
2969
|
backend = await createStorageBackend(loadConfig());
|
|
@@ -3649,7 +3650,7 @@ function cmdLogs(opts) {
|
|
|
3649
3650
|
}
|
|
3650
3651
|
function registerDaemonCommands(daemonCmd) {
|
|
3651
3652
|
daemonCmd.command("serve").description("Start the PAI daemon in the foreground").action(async () => {
|
|
3652
|
-
const { serve } = await import("./daemon-
|
|
3653
|
+
const { serve } = await import("./daemon-DsGGiIJM.mjs");
|
|
3653
3654
|
const { loadConfig: lc, ensureConfigDir } = await import("./config-C_ErGddD.mjs");
|
|
3654
3655
|
ensureConfigDir();
|
|
3655
3656
|
await serve(lc());
|
|
@@ -4017,86 +4018,6 @@ function registerRestoreCommands(program) {
|
|
|
4017
4018
|
});
|
|
4018
4019
|
}
|
|
4019
4020
|
|
|
4020
|
-
//#endregion
|
|
4021
|
-
//#region src/config/json-store.ts
|
|
4022
|
-
/**
|
|
4023
|
-
* json-store.ts — read/write JSON config files without destroying them
|
|
4024
|
-
*
|
|
4025
|
-
* The failure this exists to prevent:
|
|
4026
|
-
*
|
|
4027
|
-
* try { return JSON.parse(read(path)); } catch { return {}; }
|
|
4028
|
-
* ... later ...
|
|
4029
|
-
* write(path, JSON.stringify(ourData));
|
|
4030
|
-
*
|
|
4031
|
-
* An unreadable file becomes an empty object, and the next write makes that
|
|
4032
|
-
* permanent. Silently, exit code 0. This shape appeared three times in this
|
|
4033
|
-
* repo against three different files, and twice in AIBroker.
|
|
4034
|
-
*
|
|
4035
|
-
* The distinction that matters is between *missing* and *unreadable*:
|
|
4036
|
-
*
|
|
4037
|
-
* missing — legitimate first run. Start fresh; writing is safe.
|
|
4038
|
-
* unreadable — the file exists and we could not parse it. Those bytes are
|
|
4039
|
-
* the only copy of something. Never overwrite them.
|
|
4040
|
-
*
|
|
4041
|
-
* Collapsing the second into the first is the bug.
|
|
4042
|
-
*
|
|
4043
|
-
* NOT everything deserves this guard. For a transient buffer — an undelivered
|
|
4044
|
-
* message queue, a cache — starting fresh IS the correct recovery, and
|
|
4045
|
-
* refusing to write would disable the feature permanently. Use `writeJsonAtomic`
|
|
4046
|
-
* alone there: it still prevents a crash from truncating a good file, without
|
|
4047
|
-
* blocking recovery. Reserve `readJsonStrict` for data a user cannot rebuild.
|
|
4048
|
-
*/
|
|
4049
|
-
/**
|
|
4050
|
-
* Read a JSON file, distinguishing "absent" from "damaged".
|
|
4051
|
-
*
|
|
4052
|
-
* @param path file to read
|
|
4053
|
-
* @param label how to name it to the user, e.g. "~/.claude.json"
|
|
4054
|
-
* @throws if the file exists but cannot be read or parsed
|
|
4055
|
-
*/
|
|
4056
|
-
function readJsonStrict(path, label = path) {
|
|
4057
|
-
if (!existsSync(path)) return {};
|
|
4058
|
-
let raw;
|
|
4059
|
-
try {
|
|
4060
|
-
raw = readFileSync(path, "utf8");
|
|
4061
|
-
} catch (e) {
|
|
4062
|
-
throw new Error(`Could not read ${label}: ${e instanceof Error ? e.message : String(e)}\nRefusing to continue — writing now would replace its contents with ours alone.`);
|
|
4063
|
-
}
|
|
4064
|
-
try {
|
|
4065
|
-
return JSON.parse(raw);
|
|
4066
|
-
} catch (e) {
|
|
4067
|
-
throw new Error(`${label} exists but is not valid JSON: ${e instanceof Error ? e.message : String(e)}\nRefusing to continue — overwriting it would destroy whatever it holds.\nRepair the file, or move it aside and re-run this command.`);
|
|
4068
|
-
}
|
|
4069
|
-
}
|
|
4070
|
-
/**
|
|
4071
|
-
* Write JSON without risking the existing file.
|
|
4072
|
-
*
|
|
4073
|
-
* Keeps a `.bak-pai` copy of the previous contents, then writes to a temp file
|
|
4074
|
-
* and renames. Rename is atomic within a filesystem, so a crash mid-write
|
|
4075
|
-
* leaves the original intact rather than truncated — which is how these files
|
|
4076
|
-
* become corrupt in the first place.
|
|
4077
|
-
*/
|
|
4078
|
-
function writeJsonAtomic(path, data, opts = {}) {
|
|
4079
|
-
const { backup = true, label = path } = opts;
|
|
4080
|
-
const serialized = JSON.stringify(data, null, 2) + "\n";
|
|
4081
|
-
const dir = dirname(path);
|
|
4082
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
4083
|
-
if (backup && existsSync(path)) try {
|
|
4084
|
-
copyFileSync(path, `${path}.bak-pai`);
|
|
4085
|
-
} catch (e) {
|
|
4086
|
-
throw new Error(`Could not back up ${label}: ${e instanceof Error ? e.message : String(e)}\nRefusing to write without a backup.`);
|
|
4087
|
-
}
|
|
4088
|
-
const tmp = `${path}.tmp-pai-${process.pid}`;
|
|
4089
|
-
try {
|
|
4090
|
-
writeFileSync(tmp, serialized, "utf8");
|
|
4091
|
-
renameSync(tmp, path);
|
|
4092
|
-
} catch (e) {
|
|
4093
|
-
try {
|
|
4094
|
-
if (existsSync(tmp)) unlinkSync(tmp);
|
|
4095
|
-
} catch {}
|
|
4096
|
-
throw new Error(`Failed to write ${label}: ${e instanceof Error ? e.message : String(e)}\nThe original is unchanged.`);
|
|
4097
|
-
}
|
|
4098
|
-
}
|
|
4099
|
-
|
|
4100
4021
|
//#endregion
|
|
4101
4022
|
//#region src/cli/lib/exit.ts
|
|
4102
4023
|
/**
|
|
@@ -6152,6 +6073,209 @@ async function stepTaskBus(rl) {
|
|
|
6152
6073
|
} };
|
|
6153
6074
|
}
|
|
6154
6075
|
|
|
6076
|
+
//#endregion
|
|
6077
|
+
//#region src/workers/install.ts
|
|
6078
|
+
/**
|
|
6079
|
+
* install.ts — `pai worker install`: one command, whole migration.
|
|
6080
|
+
*
|
|
6081
|
+
* Three things:
|
|
6082
|
+
*
|
|
6083
|
+
* 1. ~/.claude/settings.json: point the PreToolUse "Agent" hook at
|
|
6084
|
+
* ${PAI_DIR}/Hooks/route-agents-to-worker.mjs, replacing the old
|
|
6085
|
+
* route-agents-to-glm.sh registration if present.
|
|
6086
|
+
* 2. ~/.local/bin compatibility shims (glm, glm-run, glm-ps, glm-log) that
|
|
6087
|
+
* exec the pai equivalents, so every existing habit, alias and script
|
|
6088
|
+
* keeps working. Anything already at those paths is moved aside to
|
|
6089
|
+
* <name>.pre-pai once — never deleted.
|
|
6090
|
+
* 3. Remove the old route-agents-to-glm.sh from ~/.claude/Hooks (the
|
|
6091
|
+
* settings.json entry is gone, so the file is dead).
|
|
6092
|
+
*
|
|
6093
|
+
* Idempotent: running it twice changes nothing the second time.
|
|
6094
|
+
*/
|
|
6095
|
+
const HOOKS_DIR = join(join(homedir(), ".claude"), "Hooks");
|
|
6096
|
+
const LOCAL_BIN = join(homedir(), ".local", "bin");
|
|
6097
|
+
const NEW_AGENT_HOOK = "${PAI_DIR}/Hooks/route-agents-to-worker.mjs";
|
|
6098
|
+
const OLD_AGENT_HOOK_STEM = "route-agents-to-glm";
|
|
6099
|
+
/** Absolute path of the pai executable to hard-wire into the shims. */
|
|
6100
|
+
function paiPath() {
|
|
6101
|
+
try {
|
|
6102
|
+
return execFileSync("/usr/bin/which", ["pai"], { encoding: "utf8" }).trim() || "pai";
|
|
6103
|
+
} catch {
|
|
6104
|
+
return "pai";
|
|
6105
|
+
}
|
|
6106
|
+
}
|
|
6107
|
+
function patchAgentHook(lines) {
|
|
6108
|
+
const settings = readSettingsJson();
|
|
6109
|
+
const hooks = typeof settings["hooks"] === "object" && settings["hooks"] !== null ? settings["hooks"] : {};
|
|
6110
|
+
const preToolUse = Array.isArray(hooks["PreToolUse"]) ? hooks["PreToolUse"] : [];
|
|
6111
|
+
let changed = false;
|
|
6112
|
+
let replaced = false;
|
|
6113
|
+
for (const rule of preToolUse) {
|
|
6114
|
+
if (rule["matcher"] !== "Agent") continue;
|
|
6115
|
+
const entries = Array.isArray(rule["hooks"]) ? rule["hooks"] : [];
|
|
6116
|
+
for (const entry of entries) {
|
|
6117
|
+
const cmd = typeof entry["command"] === "string" ? entry["command"] : "";
|
|
6118
|
+
if (cmd === NEW_AGENT_HOOK) replaced = true;
|
|
6119
|
+
if (cmd.includes(OLD_AGENT_HOOK_STEM)) {
|
|
6120
|
+
entry["command"] = NEW_AGENT_HOOK;
|
|
6121
|
+
lines.push(`settings.json: Agent hook migrated to ${NEW_AGENT_HOOK}`);
|
|
6122
|
+
changed = true;
|
|
6123
|
+
replaced = true;
|
|
6124
|
+
}
|
|
6125
|
+
}
|
|
6126
|
+
}
|
|
6127
|
+
if (!replaced) {
|
|
6128
|
+
preToolUse.push({
|
|
6129
|
+
matcher: "Agent",
|
|
6130
|
+
hooks: [{
|
|
6131
|
+
type: "command",
|
|
6132
|
+
command: NEW_AGENT_HOOK
|
|
6133
|
+
}]
|
|
6134
|
+
});
|
|
6135
|
+
lines.push(`settings.json: Agent hook added → ${NEW_AGENT_HOOK}`);
|
|
6136
|
+
changed = true;
|
|
6137
|
+
}
|
|
6138
|
+
if (changed) {
|
|
6139
|
+
hooks["PreToolUse"] = preToolUse;
|
|
6140
|
+
settings["hooks"] = hooks;
|
|
6141
|
+
writeSettingsJson(settings);
|
|
6142
|
+
} else lines.push(`settings.json: Agent hook already current`);
|
|
6143
|
+
return changed;
|
|
6144
|
+
}
|
|
6145
|
+
const GLM_PS_SHIM = (pai) => `# pai worker shim — replaces the python glm-ps of the same name
|
|
6146
|
+
case "\${1-}" in
|
|
6147
|
+
""|"ps") exec ${pai} worker ps ;;
|
|
6148
|
+
follow|watch|pane|log) exec ${pai} worker "$@" ;;
|
|
6149
|
+
--status) shift; exec ${pai} worker status-line "\${ITERM_SESSION_ID:-}" "\${PWD:-$HOME}" ;;
|
|
6150
|
+
-*) exec ${pai} worker ps "$@" ;;
|
|
6151
|
+
*) exec ${pai} worker replay "$@" ;;
|
|
6152
|
+
esac
|
|
6153
|
+
`;
|
|
6154
|
+
function shim(name, pai) {
|
|
6155
|
+
if (name === "glm-ps") return GLM_PS_SHIM(pai);
|
|
6156
|
+
if (name === "glm-log") return `#!/bin/sh\n# pai worker shim — replaces the shell glm-log of the same name\nexec ${pai} worker log "$@"\n`;
|
|
6157
|
+
if (name === "worker-say") return `#!/bin/sh\n# pai worker shim — say one line to a running worker\nexec ${pai} worker say "$@"\n`;
|
|
6158
|
+
return `#!/bin/sh\n# pai worker shim — replaces the previous ${name} of the same name\nexec ${pai} worker run "$@"\n`;
|
|
6159
|
+
}
|
|
6160
|
+
function installShims(lines) {
|
|
6161
|
+
const pai = paiPath();
|
|
6162
|
+
let changed = false;
|
|
6163
|
+
for (const name of [
|
|
6164
|
+
"glm",
|
|
6165
|
+
"glm-run",
|
|
6166
|
+
"glm-ps",
|
|
6167
|
+
"glm-log",
|
|
6168
|
+
"worker-say"
|
|
6169
|
+
]) {
|
|
6170
|
+
const path = join(LOCAL_BIN, name);
|
|
6171
|
+
const ours = `# pai worker shim`;
|
|
6172
|
+
let current = null;
|
|
6173
|
+
if (existsSync(path)) try {
|
|
6174
|
+
current = execFileSync("head", [
|
|
6175
|
+
"-c",
|
|
6176
|
+
"64",
|
|
6177
|
+
path
|
|
6178
|
+
], { encoding: "utf8" });
|
|
6179
|
+
} catch {
|
|
6180
|
+
current = null;
|
|
6181
|
+
}
|
|
6182
|
+
if (current !== null && !current.includes(ours)) {
|
|
6183
|
+
const aside = join(LOCAL_BIN, `${name}.pre-pai`);
|
|
6184
|
+
if (!existsSync(aside)) {
|
|
6185
|
+
renameSync(path, aside);
|
|
6186
|
+
lines.push(`${name}: previous version kept as ${name}.pre-pai`);
|
|
6187
|
+
} else {
|
|
6188
|
+
unlinkSync(path);
|
|
6189
|
+
lines.push(`${name}: replaced (${name}.pre-pai already exists)`);
|
|
6190
|
+
}
|
|
6191
|
+
changed = true;
|
|
6192
|
+
} else if (current === null) changed = true;
|
|
6193
|
+
else {
|
|
6194
|
+
lines.push(`${name}: shim already current`);
|
|
6195
|
+
continue;
|
|
6196
|
+
}
|
|
6197
|
+
writeFileSync(path, shim(name, pai), {
|
|
6198
|
+
encoding: "utf8",
|
|
6199
|
+
mode: 493
|
|
6200
|
+
});
|
|
6201
|
+
try {
|
|
6202
|
+
chmodSync(path, 493);
|
|
6203
|
+
} catch {}
|
|
6204
|
+
lines.push(`${name}: shim installed → ${pai} worker …`);
|
|
6205
|
+
}
|
|
6206
|
+
return changed;
|
|
6207
|
+
}
|
|
6208
|
+
function removeOldHook(lines) {
|
|
6209
|
+
const old = join(HOOKS_DIR, "route-agents-to-glm.sh");
|
|
6210
|
+
if (!existsSync(old)) return false;
|
|
6211
|
+
unlinkSync(old);
|
|
6212
|
+
lines.push(`removed ${OLD_AGENT_HOOK_STEM}.sh (registration migrated above)`);
|
|
6213
|
+
return true;
|
|
6214
|
+
}
|
|
6215
|
+
function installWorkers() {
|
|
6216
|
+
const lines = [];
|
|
6217
|
+
let changed = patchAgentHook(lines);
|
|
6218
|
+
if (installShims(lines)) changed = true;
|
|
6219
|
+
if (removeOldHook(lines)) changed = true;
|
|
6220
|
+
if (!existsSync(LOCAL_BIN)) lines.push(`note: ${LOCAL_BIN} did not exist; make sure it is on PATH`);
|
|
6221
|
+
return {
|
|
6222
|
+
lines,
|
|
6223
|
+
changed
|
|
6224
|
+
};
|
|
6225
|
+
}
|
|
6226
|
+
|
|
6227
|
+
//#endregion
|
|
6228
|
+
//#region src/cli/commands/setup/steps/17-workers.ts
|
|
6229
|
+
/** Step 17: Worker providers — subagents on configured providers. */
|
|
6230
|
+
async function stepWorkers(rl) {
|
|
6231
|
+
section("Step 17: Worker Providers (Optional)");
|
|
6232
|
+
const current = readConfigRaw().workers;
|
|
6233
|
+
if (current?.providers && Object.keys(current.providers).length > 0) {
|
|
6234
|
+
if (current.enabled !== false) {
|
|
6235
|
+
console.log(c.ok("Worker providers already configured. Running install to refresh shims…"));
|
|
6236
|
+
for (const l of installWorkers().lines) line$1(` ${l}`);
|
|
6237
|
+
}
|
|
6238
|
+
return { workers: current };
|
|
6239
|
+
}
|
|
6240
|
+
line$1();
|
|
6241
|
+
line$1(" PAI can run every subagent (worker) on a provider you configure —");
|
|
6242
|
+
line$1(" a non-Anthropic endpoint via its Anthropic-compatible API.");
|
|
6243
|
+
line$1();
|
|
6244
|
+
line$1(" `pai worker run` is the runner; ps / follow / replay watch the runs;");
|
|
6245
|
+
line$1(" the Agent tool is denied and rewritten to it once providers exist.");
|
|
6246
|
+
line$1();
|
|
6247
|
+
line$1(" PAI works fully without this — Agent subagents then just run as before.");
|
|
6248
|
+
line$1();
|
|
6249
|
+
if (!await promptYesNo(rl, "Configure a worker provider now?", false)) {
|
|
6250
|
+
line$1();
|
|
6251
|
+
console.log(c.ok("Skipping worker providers."));
|
|
6252
|
+
console.log(c.dim(" Add later: pai worker providers add <name> … — see docs/worker.md"));
|
|
6253
|
+
return { workers: {
|
|
6254
|
+
enabled: false,
|
|
6255
|
+
providers: {},
|
|
6256
|
+
roles: {}
|
|
6257
|
+
} };
|
|
6258
|
+
}
|
|
6259
|
+
line$1();
|
|
6260
|
+
line$1(" Add the provider after setup finishes (this step only prepares the");
|
|
6261
|
+
line$1(" wiring — shims, hook registration, log dir):");
|
|
6262
|
+
line$1();
|
|
6263
|
+
line$1(c.dim(" pai worker providers add glm \\"));
|
|
6264
|
+
line$1(c.dim(" --base-url https://api.example.com/api/anthropic \\"));
|
|
6265
|
+
line$1(c.dim(" --key-file ~/.config/example/api_key \\"));
|
|
6266
|
+
line$1(c.dim(" --model example-4.7 --fast-model example-4.7-flash"));
|
|
6267
|
+
line$1();
|
|
6268
|
+
line$1(" The first provider turns workers on and seeds the roles.");
|
|
6269
|
+
line$1();
|
|
6270
|
+
const r = installWorkers();
|
|
6271
|
+
for (const l of r.lines) line$1(` ${l}`);
|
|
6272
|
+
return { workers: {
|
|
6273
|
+
enabled: false,
|
|
6274
|
+
providers: {},
|
|
6275
|
+
roles: {}
|
|
6276
|
+
} };
|
|
6277
|
+
}
|
|
6278
|
+
|
|
6155
6279
|
//#endregion
|
|
6156
6280
|
//#region src/cli/commands/setup/index.ts
|
|
6157
6281
|
async function runSetup() {
|
|
@@ -6188,10 +6312,12 @@ async function runSetup() {
|
|
|
6188
6312
|
const mcpRegistered = await stepMcp(rl);
|
|
6189
6313
|
await stepDirectories(rl);
|
|
6190
6314
|
const taskConfig = await stepTaskBus(rl);
|
|
6315
|
+
const workersConfig = await stepWorkers(rl);
|
|
6191
6316
|
const allUpdates = {
|
|
6192
6317
|
...storageConfig,
|
|
6193
6318
|
...embeddingConfig,
|
|
6194
|
-
...taskConfig
|
|
6319
|
+
...taskConfig,
|
|
6320
|
+
...workersConfig
|
|
6195
6321
|
};
|
|
6196
6322
|
mergeConfig(allUpdates);
|
|
6197
6323
|
line$1();
|
|
@@ -7102,7 +7228,7 @@ async function cmdExplore(note, opts) {
|
|
|
7102
7228
|
const depth = parseInt(opts.depth ?? "3", 10);
|
|
7103
7229
|
const direction = opts.direction ?? "both";
|
|
7104
7230
|
const mode = opts.mode ?? "all";
|
|
7105
|
-
const { zettelExplore } = await import("./zettelkasten-
|
|
7231
|
+
const { zettelExplore } = await import("./zettelkasten-vo7psPdT.mjs");
|
|
7106
7232
|
const result = zettelExplore(getFedDb(), {
|
|
7107
7233
|
startNote: note,
|
|
7108
7234
|
depth,
|
|
@@ -7167,7 +7293,7 @@ async function cmdHealth(opts) {
|
|
|
7167
7293
|
const projectPath = opts.project;
|
|
7168
7294
|
const recentDays = parseInt(opts.days ?? "30", 10);
|
|
7169
7295
|
const includeTypes = opts.include ? opts.include.split(",").map((s) => s.trim()) : void 0;
|
|
7170
|
-
const { zettelHealth } = await import("./zettelkasten-
|
|
7296
|
+
const { zettelHealth } = await import("./zettelkasten-vo7psPdT.mjs");
|
|
7171
7297
|
const result = zettelHealth(getFedDb(), {
|
|
7172
7298
|
scope,
|
|
7173
7299
|
projectPath,
|
|
@@ -7238,7 +7364,7 @@ async function cmdSurprise(note, opts) {
|
|
|
7238
7364
|
const limit = parseInt(opts.limit ?? "10", 10);
|
|
7239
7365
|
const minSimilarity = parseFloat(opts.minSimilarity ?? "0.3");
|
|
7240
7366
|
const minGraphDistance = parseInt(opts.minDistance ?? "3", 10);
|
|
7241
|
-
const { zettelSurprise } = await import("./zettelkasten-
|
|
7367
|
+
const { zettelSurprise } = await import("./zettelkasten-vo7psPdT.mjs");
|
|
7242
7368
|
const db = getFedDb();
|
|
7243
7369
|
console.log();
|
|
7244
7370
|
console.log(header(" PAI Zettel Surprise"));
|
|
@@ -7293,7 +7419,7 @@ async function cmdSuggest(note, opts) {
|
|
|
7293
7419
|
const vaultProjectId = parseInt(opts.vaultProjectId, 10);
|
|
7294
7420
|
const limit = parseInt(opts.limit ?? "5", 10);
|
|
7295
7421
|
const excludeLinked = opts.excludeLinked !== false;
|
|
7296
|
-
const { zettelSuggest } = await import("./zettelkasten-
|
|
7422
|
+
const { zettelSuggest } = await import("./zettelkasten-vo7psPdT.mjs");
|
|
7297
7423
|
const db = getFedDb();
|
|
7298
7424
|
console.log();
|
|
7299
7425
|
console.log(header(" PAI Zettel Suggest"));
|
|
@@ -7346,7 +7472,7 @@ async function cmdConverse(question, opts) {
|
|
|
7346
7472
|
const vaultProjectId = parseInt(opts.vaultProjectId, 10);
|
|
7347
7473
|
const depth = parseInt(opts.depth ?? "2", 10);
|
|
7348
7474
|
const limit = parseInt(opts.limit ?? "15", 10);
|
|
7349
|
-
const { zettelConverse } = await import("./zettelkasten-
|
|
7475
|
+
const { zettelConverse } = await import("./zettelkasten-vo7psPdT.mjs");
|
|
7350
7476
|
const db = getFedDb();
|
|
7351
7477
|
console.log();
|
|
7352
7478
|
console.log(header(" PAI Zettel Converse"));
|
|
@@ -7409,7 +7535,7 @@ async function cmdThemes(opts) {
|
|
|
7409
7535
|
const minClusterSize = parseInt(opts.minSize ?? "3", 10);
|
|
7410
7536
|
const maxThemes = parseInt(opts.maxThemes ?? "10", 10);
|
|
7411
7537
|
const similarityThreshold = parseFloat(opts.threshold ?? "0.65");
|
|
7412
|
-
const { zettelThemes } = await import("./zettelkasten-
|
|
7538
|
+
const { zettelThemes } = await import("./zettelkasten-vo7psPdT.mjs");
|
|
7413
7539
|
const db = getFedDb();
|
|
7414
7540
|
console.log();
|
|
7415
7541
|
console.log(header(" PAI Zettel Themes"));
|
|
@@ -8984,7 +9110,7 @@ async function escalate(task, headline, detail, state, now, opts) {
|
|
|
8984
9110
|
state.alarmedAt[task.id] = now;
|
|
8985
9111
|
let delivered = false;
|
|
8986
9112
|
try {
|
|
8987
|
-
const { routeNotification } = await import("./router-
|
|
9113
|
+
const { routeNotification } = await import("./router-1zi8jiNF.mjs");
|
|
8988
9114
|
const { loadConfig } = await import("./config-C_ErGddD.mjs");
|
|
8989
9115
|
delivered = (await routeNotification({
|
|
8990
9116
|
event: "error",
|
|
@@ -11601,9 +11727,9 @@ function cmdActive(db, opts) {
|
|
|
11601
11727
|
], rows));
|
|
11602
11728
|
}
|
|
11603
11729
|
async function cmdAutoRoute(opts) {
|
|
11604
|
-
const { autoRoute, formatAutoRoute, formatAutoRouteJson } = await import("./auto-route-
|
|
11730
|
+
const { autoRoute, formatAutoRoute, formatAutoRouteJson } = await import("./auto-route-DM7GhJ8y.mjs");
|
|
11605
11731
|
const { openRegistry } = await import("./db-XEwJbuGO.mjs");
|
|
11606
|
-
const { createStorageBackend } = await import("./factory-
|
|
11732
|
+
const { createStorageBackend } = await import("./factory-BXzqRYVZ.mjs");
|
|
11607
11733
|
const { loadConfig } = await import("./config-C_ErGddD.mjs");
|
|
11608
11734
|
const config = loadConfig();
|
|
11609
11735
|
const registryDb = openRegistry();
|
|
@@ -12412,6 +12538,40 @@ function listProjectTranscripts(cwd, projectsDir) {
|
|
|
12412
12538
|
collect(join(projectDir, "sessions"));
|
|
12413
12539
|
return paths;
|
|
12414
12540
|
}
|
|
12541
|
+
/** Placeholder model id the platform writes on synthetic assistant turns. */
|
|
12542
|
+
const SYNTHETIC_MODEL = "<synthetic>";
|
|
12543
|
+
function modelFamily(model) {
|
|
12544
|
+
if (typeof model !== "string" || model === "" || model === SYNTHETIC_MODEL) return "unknown";
|
|
12545
|
+
return model.startsWith("claude-") ? "claude" : "foreign";
|
|
12546
|
+
}
|
|
12547
|
+
/**
|
|
12548
|
+
* The family of the LAST assistant model in a transcript — scanned from the
|
|
12549
|
+
* end so a long transcript costs one read and a few lines of parsing.
|
|
12550
|
+
* Unreadable or model-less transcripts are "unknown", never "foreign":
|
|
12551
|
+
* the callers that skip work on "foreign" must not skip it on doubt.
|
|
12552
|
+
*/
|
|
12553
|
+
function transcriptModelFamily(path) {
|
|
12554
|
+
let raw;
|
|
12555
|
+
try {
|
|
12556
|
+
raw = readFileSync(path, "utf-8");
|
|
12557
|
+
} catch {
|
|
12558
|
+
return "unknown";
|
|
12559
|
+
}
|
|
12560
|
+
const lines = raw.split("\n");
|
|
12561
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
12562
|
+
const line = lines[i];
|
|
12563
|
+
if (!line.includes("\"model\"")) continue;
|
|
12564
|
+
try {
|
|
12565
|
+
const entry = JSON.parse(line);
|
|
12566
|
+
if (entry.type !== "assistant") continue;
|
|
12567
|
+
const model = entry.message?.model;
|
|
12568
|
+
if (typeof model === "string" && model !== SYNTHETIC_MODEL) return modelFamily(model);
|
|
12569
|
+
} catch {
|
|
12570
|
+
continue;
|
|
12571
|
+
}
|
|
12572
|
+
}
|
|
12573
|
+
return "unknown";
|
|
12574
|
+
}
|
|
12415
12575
|
/**
|
|
12416
12576
|
* Every DISTINCT compact_boundary sample found across ALL of a project's
|
|
12417
12577
|
* transcripts (live + archived), newest first by the event's OWN timestamp
|
|
@@ -12428,6 +12588,8 @@ function readCompactBoundarySamples(cwd, projectsDir) {
|
|
|
12428
12588
|
} catch {
|
|
12429
12589
|
continue;
|
|
12430
12590
|
}
|
|
12591
|
+
let lastModel = null;
|
|
12592
|
+
let foreignDiscards = 0;
|
|
12431
12593
|
for (const line of raw.split("\n")) {
|
|
12432
12594
|
if (!line.trim()) continue;
|
|
12433
12595
|
let entry;
|
|
@@ -12436,11 +12598,20 @@ function readCompactBoundarySamples(cwd, projectsDir) {
|
|
|
12436
12598
|
} catch {
|
|
12437
12599
|
continue;
|
|
12438
12600
|
}
|
|
12601
|
+
if (entry.type === "assistant") {
|
|
12602
|
+
const model = entry.message?.model;
|
|
12603
|
+
if (typeof model === "string" && model !== SYNTHETIC_MODEL) lastModel = model;
|
|
12604
|
+
continue;
|
|
12605
|
+
}
|
|
12439
12606
|
if (entry.type !== "system" || entry.subtype !== "compact_boundary") continue;
|
|
12440
12607
|
const preTokens = entry.compactMetadata?.preTokens;
|
|
12441
12608
|
if (typeof preTokens !== "number" || !Number.isFinite(preTokens)) continue;
|
|
12442
12609
|
const timestampMs = entry.timestamp ? Date.parse(entry.timestamp) : NaN;
|
|
12443
12610
|
if (!Number.isFinite(timestampMs)) continue;
|
|
12611
|
+
if (modelFamily(lastModel) === "foreign") {
|
|
12612
|
+
foreignDiscards++;
|
|
12613
|
+
continue;
|
|
12614
|
+
}
|
|
12444
12615
|
const key = entry.uuid ?? `${timestampMs}:${preTokens}`;
|
|
12445
12616
|
if (!byKey.has(key)) byKey.set(key, {
|
|
12446
12617
|
preTokens,
|
|
@@ -12449,6 +12620,7 @@ function readCompactBoundarySamples(cwd, projectsDir) {
|
|
|
12449
12620
|
uuid: entry.uuid
|
|
12450
12621
|
});
|
|
12451
12622
|
}
|
|
12623
|
+
if (foreignDiscards > 0) console.error(`[context-fill] ignored ${foreignDiscards} compaction sample(s) from a non-Claude transcript (model=${lastModel}): ${path}`);
|
|
12452
12624
|
}
|
|
12453
12625
|
return [...byKey.values()].sort((a, b) => b.timestampMs - a.timestampMs);
|
|
12454
12626
|
}
|
|
@@ -12592,6 +12764,32 @@ function isImmediate(usedTokens, thresholds) {
|
|
|
12592
12764
|
return usedTokens >= thresholds.immediateTokens;
|
|
12593
12765
|
}
|
|
12594
12766
|
|
|
12767
|
+
//#endregion
|
|
12768
|
+
//#region src/hooks/ts/lib/worker-session.ts
|
|
12769
|
+
/**
|
|
12770
|
+
* Worker-session detection.
|
|
12771
|
+
*
|
|
12772
|
+
* A disposable headless worker (a `claude -p` run started by an orchestrating
|
|
12773
|
+
* session, possibly against a different model provider with a different
|
|
12774
|
+
* context window) shares the project directory and the hook configuration
|
|
12775
|
+
* with the real session that spawned it. Left alone, the hooks treat it as a
|
|
12776
|
+
* session in its own right: they inject project context into it, create and
|
|
12777
|
+
* rename a numbered session note for it, autosave it, and enqueue a
|
|
12778
|
+
* model-written handover for it. Its compactions also land in the project's
|
|
12779
|
+
* transcript folder, where they are indistinguishable from a real session's
|
|
12780
|
+
* and drag the measured compaction trigger down (observed 2026-09-17: two
|
|
12781
|
+
* workers compacting at ~151k tokens pulled a project's trigger from ~784k
|
|
12782
|
+
* to ~151k, and the real session's handover fired at ~50k tokens).
|
|
12783
|
+
*
|
|
12784
|
+
* The launcher marks such sessions with `PAI_WORKER=1`. Every hook that does
|
|
12785
|
+
* per-session bookkeeping returns immediately when this predicate is true.
|
|
12786
|
+
* Deliberately NOT guarded: the security validator (a worker's shell
|
|
12787
|
+
* commands must still be checked) and observability capture.
|
|
12788
|
+
*/
|
|
12789
|
+
function isWorkerSession(env = process.env) {
|
|
12790
|
+
return env.PAI_WORKER === "1";
|
|
12791
|
+
}
|
|
12792
|
+
|
|
12595
12793
|
//#endregion
|
|
12596
12794
|
//#region src/session/context-handover-trigger.ts
|
|
12597
12795
|
/**
|
|
@@ -12705,6 +12903,14 @@ const NOTHING = {
|
|
|
12705
12903
|
* either alone would have.
|
|
12706
12904
|
*/
|
|
12707
12905
|
async function checkAndEnqueueContextHandover(input, deps = defaultDeps) {
|
|
12906
|
+
if (isWorkerSession()) {
|
|
12907
|
+
console.error(`[context-handover-trigger] session ${input.sessionId}: worker session (PAI_WORKER=1) — no handover.`);
|
|
12908
|
+
return NOTHING;
|
|
12909
|
+
}
|
|
12910
|
+
if (input.transcriptPath && transcriptModelFamily(input.transcriptPath) === "foreign") {
|
|
12911
|
+
console.error(`[context-handover-trigger] session ${input.sessionId}: transcript written by a non-Claude model — no handover.`);
|
|
12912
|
+
return NOTHING;
|
|
12913
|
+
}
|
|
12708
12914
|
let state = deps.loadState(input.sessionId);
|
|
12709
12915
|
if (ALL_THRESHOLDS.every((t) => state.confirmed.includes(t))) return NOTHING;
|
|
12710
12916
|
let confirmedThisCheck = [];
|
|
@@ -13371,6 +13577,391 @@ function registerSessionCommands(sessionCmd, getDb) {
|
|
|
13371
13577
|
});
|
|
13372
13578
|
}
|
|
13373
13579
|
|
|
13580
|
+
//#endregion
|
|
13581
|
+
//#region src/cli/commands/worker/providers.ts
|
|
13582
|
+
function fail$1(e) {
|
|
13583
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
13584
|
+
console.error(err("pai worker: ") + msg);
|
|
13585
|
+
process.exitCode = 1;
|
|
13586
|
+
}
|
|
13587
|
+
/** `--env K=V` (repeatable) → record. Throws on a value without "=". */
|
|
13588
|
+
function collectEnv(pairs) {
|
|
13589
|
+
const out = {};
|
|
13590
|
+
for (const p of pairs ?? []) {
|
|
13591
|
+
const eq = p.indexOf("=");
|
|
13592
|
+
if (eq <= 0) throw new WorkersConfigError(`--env expects NAME=VALUE, got "${p}"`);
|
|
13593
|
+
out[p.slice(0, eq)] = p.slice(eq + 1);
|
|
13594
|
+
}
|
|
13595
|
+
return out;
|
|
13596
|
+
}
|
|
13597
|
+
function parseIntArg$1(v) {
|
|
13598
|
+
const n = parseInt(v, 10);
|
|
13599
|
+
if (Number.isNaN(n) || n <= 0) throw new WorkersConfigError(`expected a positive number, got "${v}"`);
|
|
13600
|
+
return n;
|
|
13601
|
+
}
|
|
13602
|
+
/** `glm/fast` | `{provider, mcp}` → one printable target line. */
|
|
13603
|
+
function roleTargetText(target) {
|
|
13604
|
+
if (typeof target === "string") return target;
|
|
13605
|
+
if (typeof target === "object" && target !== null) {
|
|
13606
|
+
const t = target;
|
|
13607
|
+
return `${t.provider ?? "?"}${t.mcp?.length ? ` +mcp(${t.mcp.join(",")})` : ""}`;
|
|
13608
|
+
}
|
|
13609
|
+
return String(target);
|
|
13610
|
+
}
|
|
13611
|
+
function registerWorkerProviderCommands(providersCmd) {
|
|
13612
|
+
providersCmd.description("Providers: list (default), add, remove, use, enable, disable, test").action(() => {
|
|
13613
|
+
const { workers } = readWorkersSection();
|
|
13614
|
+
console.log();
|
|
13615
|
+
for (const line of describeProviders(workers)) console.log(` ${line}`);
|
|
13616
|
+
console.log();
|
|
13617
|
+
console.log(dim$1(` workers are ${workers.enabled ? "on" : "off"} — pai worker ${workers.enabled ? "off" : "on"}`));
|
|
13618
|
+
console.log();
|
|
13619
|
+
});
|
|
13620
|
+
providersCmd.command("add <name>").description("Add a provider; the first one also turns workers on and seeds roles.\nExample: pai worker providers add glm --base-url https://…/anthropic \\\n --key-file ~/.config/zai/api_key --model glm-5.3 --fast-model glm-5.3-flash\nOpenAI-protocol: --protocol openai --upstream-url https://…/v1 (runs via the PAI proxy).\nCodex (ChatGPT plan): --engine codex — runs through the Codex CLI.").option("--base-url <url>", "Anthropic-compatible API base URL (required unless --protocol openai)").requiredOption("--model <model>", "Default model id for this provider").option("--key-file <path>", "File holding the API token (0600); omit for token \"local\"").option("--fast-model <model>", "Cheaper model for spotchecks and routing").option("--env <name=value>", "Extra env for runs (repeatable)", (v, acc) => [...acc, v], []).option("--note <text>", "Human note shown in `providers list`").option("--protocol <proto>", "anthropic (default) or openai — openai runs through the local PAI proxy").option("--upstream-url <url>", "Chat Completions base URL (required for --protocol openai)").option("--engine <engine>", "claude (default) or codex — codex runs `codex exec --json`").option("--context-window <tokens>", "Context window for the meter (default 200000; init event overrides)", parseIntArg$1).option("--quota-probe <url>", "URL whose JSON first number is the quota percent (0-100)").action((name, opts) => {
|
|
13621
|
+
try {
|
|
13622
|
+
const protocol = opts.protocol;
|
|
13623
|
+
const engine = opts.engine;
|
|
13624
|
+
if (protocol && protocol !== "anthropic" && protocol !== "openai") throw new WorkersConfigError(`--protocol must be anthropic or openai, got "${protocol}"`);
|
|
13625
|
+
if (engine && engine !== "claude" && engine !== "codex") throw new WorkersConfigError(`--engine must be claude or codex, got "${engine}"`);
|
|
13626
|
+
if (protocol !== "openai" && !opts.baseUrl) throw new WorkersConfigError("--base-url is required (only --protocol openai goes without it)");
|
|
13627
|
+
addProvider({
|
|
13628
|
+
name,
|
|
13629
|
+
baseUrl: opts.baseUrl ?? "",
|
|
13630
|
+
keyFile: opts.keyFile ?? null,
|
|
13631
|
+
model: opts.model,
|
|
13632
|
+
fastModel: opts.fastModel,
|
|
13633
|
+
env: collectEnv(opts.env),
|
|
13634
|
+
note: opts.note,
|
|
13635
|
+
...protocol ? { protocol } : {},
|
|
13636
|
+
...opts.upstreamUrl ? { upstreamUrl: opts.upstreamUrl } : {},
|
|
13637
|
+
...engine ? { engine } : {},
|
|
13638
|
+
...opts.contextWindow ? { contextWindow: opts.contextWindow } : {},
|
|
13639
|
+
quotaProbe: opts.quotaProbe
|
|
13640
|
+
});
|
|
13641
|
+
const { workers } = readWorkersSection();
|
|
13642
|
+
console.log(ok$1(`provider ${name} added`));
|
|
13643
|
+
if (workers.active === name) console.log(` active: ${name} — workers ${workers.enabled ? "on" : "off"}`);
|
|
13644
|
+
for (const line of describeProviders(workers)) console.log(dim$1(` ${line}`));
|
|
13645
|
+
} catch (e) {
|
|
13646
|
+
fail$1(e);
|
|
13647
|
+
}
|
|
13648
|
+
});
|
|
13649
|
+
providersCmd.command("remove <name>").description("Remove a provider and any roles pointing at it").action((name) => {
|
|
13650
|
+
try {
|
|
13651
|
+
removeProvider(name);
|
|
13652
|
+
console.log(ok$1(`provider ${name} removed`));
|
|
13653
|
+
} catch (e) {
|
|
13654
|
+
fail$1(e);
|
|
13655
|
+
}
|
|
13656
|
+
});
|
|
13657
|
+
providersCmd.command("use <name>").description("Make this provider the active one for runs without --provider/--role").action((name) => {
|
|
13658
|
+
try {
|
|
13659
|
+
useProvider(name);
|
|
13660
|
+
console.log(ok$1(`active provider: ${name}`));
|
|
13661
|
+
} catch (e) {
|
|
13662
|
+
fail$1(e);
|
|
13663
|
+
}
|
|
13664
|
+
});
|
|
13665
|
+
providersCmd.command("enable <name>").description("Enable a provider (also clears its cooldown)").action((name) => {
|
|
13666
|
+
try {
|
|
13667
|
+
setProviderEnabled(name, true);
|
|
13668
|
+
console.log(ok$1(`provider ${name} enabled`));
|
|
13669
|
+
} catch (e) {
|
|
13670
|
+
fail$1(e);
|
|
13671
|
+
}
|
|
13672
|
+
});
|
|
13673
|
+
providersCmd.command("disable <name>").description("Disable a provider (auto-routing skips it; --provider still works)").action((name) => {
|
|
13674
|
+
try {
|
|
13675
|
+
setProviderEnabled(name, false);
|
|
13676
|
+
console.log(ok$1(`provider ${name} disabled`));
|
|
13677
|
+
} catch (e) {
|
|
13678
|
+
fail$1(e);
|
|
13679
|
+
}
|
|
13680
|
+
});
|
|
13681
|
+
providersCmd.command("test [name]").description("One-word pong probe through a provider (default: the active one)").action(async (name) => {
|
|
13682
|
+
try {
|
|
13683
|
+
const { workers } = readWorkersSection();
|
|
13684
|
+
const pname = name ?? workers.active ?? "";
|
|
13685
|
+
if (!pname || !workers.providers[pname]) throw new WorkersConfigError(name ? `no provider named "${name}"` : "no active provider — name one explicitly");
|
|
13686
|
+
console.log(dim$1(` probing ${pname} (${workers.providers[pname].models.default}) …`));
|
|
13687
|
+
const r = await testProvider(pname, workers.providers[pname], workersLogDir(workers));
|
|
13688
|
+
const head = `${r.provider} ${r.model} ${(r.latencyMs / 1e3).toFixed(1)}s`;
|
|
13689
|
+
if (r.skipped) {
|
|
13690
|
+
console.log(dim$1(` ${head} ${r.skipped}`));
|
|
13691
|
+
console.log(` reply: ${r.result.slice(0, 200)}`);
|
|
13692
|
+
return;
|
|
13693
|
+
}
|
|
13694
|
+
console.log(r.ok ? ok$1(` ${head}`) : err(` ${head}`));
|
|
13695
|
+
console.log(` reply: ${r.result.slice(0, 200)}`);
|
|
13696
|
+
process.exitCode = r.ok ? 0 : 1;
|
|
13697
|
+
} catch (e) {
|
|
13698
|
+
fail$1(e);
|
|
13699
|
+
}
|
|
13700
|
+
});
|
|
13701
|
+
}
|
|
13702
|
+
/** `pai worker roles` — role → provider[/fast] assignment. */
|
|
13703
|
+
function registerWorkerRoleCommands(workerCmd) {
|
|
13704
|
+
const rolesCmd = workerCmd.command("roles").description("Roles: which provider serves implement / research / spotcheck").action(() => {
|
|
13705
|
+
const { workers } = readWorkersSection();
|
|
13706
|
+
const entries = Object.entries(workers.roles);
|
|
13707
|
+
if (!entries.length) {
|
|
13708
|
+
console.log(dim$1(" no roles set — runs use the active provider"));
|
|
13709
|
+
return;
|
|
13710
|
+
}
|
|
13711
|
+
for (const [role, target] of entries) console.log(` ${role.padEnd(12)} ${roleTargetText(target)}`);
|
|
13712
|
+
});
|
|
13713
|
+
rolesCmd.command("list").description("List roles and their providers (default action)").action(() => {
|
|
13714
|
+
const { workers } = readWorkersSection();
|
|
13715
|
+
const entries = Object.entries(workers.roles);
|
|
13716
|
+
if (!entries.length) {
|
|
13717
|
+
console.log(dim$1(" no roles set — runs use the active provider"));
|
|
13718
|
+
return;
|
|
13719
|
+
}
|
|
13720
|
+
for (const [role, target] of entries) console.log(` ${role.padEnd(12)} ${roleTargetText(target)}`);
|
|
13721
|
+
});
|
|
13722
|
+
rolesCmd.command("set <role> <provider[/alias]>").description("Point a role at a provider, optionally its fast model (e.g. glm/fast)").action((role, target) => {
|
|
13723
|
+
try {
|
|
13724
|
+
setRole(role, target);
|
|
13725
|
+
console.log(ok$1(`role ${role} → ${target}`));
|
|
13726
|
+
} catch (e) {
|
|
13727
|
+
fail$1(e);
|
|
13728
|
+
}
|
|
13729
|
+
});
|
|
13730
|
+
rolesCmd.command("unset <role>").description("Remove a role (runs then use the active provider)").action((role) => {
|
|
13731
|
+
try {
|
|
13732
|
+
unsetRole(role);
|
|
13733
|
+
console.log(ok$1(`role ${role} removed`));
|
|
13734
|
+
} catch (e) {
|
|
13735
|
+
fail$1(e);
|
|
13736
|
+
}
|
|
13737
|
+
});
|
|
13738
|
+
}
|
|
13739
|
+
|
|
13740
|
+
//#endregion
|
|
13741
|
+
//#region src/cli/commands/worker/index.ts
|
|
13742
|
+
function currentLogDir() {
|
|
13743
|
+
const { workers } = readWorkersSection();
|
|
13744
|
+
return workersLogDir(workers);
|
|
13745
|
+
}
|
|
13746
|
+
function fail(e) {
|
|
13747
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
13748
|
+
console.error(err("pai worker: ") + msg);
|
|
13749
|
+
process.exitCode = 1;
|
|
13750
|
+
}
|
|
13751
|
+
function registerWorkerCommands(workerCmd) {
|
|
13752
|
+
workerCmd.command("run").description("Run one claude-code worker through the configured provider.\nUnknown options are passed to claude verbatim (e.g. -p, --allowedTools);\n--output-format/--verbose are handled here.").allowUnknownOption(true).option("--provider <name>", "Provider to run on (default: active, else routing order)").option("--role <role>", "Use the provider of this role (implement, research, spotcheck, …)").option("--model <model>", "Override the provider's model for this run").option("--label <text>", "Short task label shown in ps / follow / status line").option("--mcp <names>", "MCP servers/sets this worker may use (comma-separated; see `pai worker mcp`)").option("--no-pane", "Do not open a follow pane for this worker").argument("[args...]", "claude arguments, e.g. -p '<task>' --allowedTools 'Read,Edit,Bash'").action(async (args, opts) => {
|
|
13753
|
+
try {
|
|
13754
|
+
const rc = await runWorker({
|
|
13755
|
+
providerFlag: opts.provider,
|
|
13756
|
+
role: opts.role,
|
|
13757
|
+
modelFlag: opts.model,
|
|
13758
|
+
label: opts.label,
|
|
13759
|
+
mcpFlag: opts.mcp,
|
|
13760
|
+
noPane: opts.pane === false,
|
|
13761
|
+
claudeArgs: args
|
|
13762
|
+
});
|
|
13763
|
+
process.exitCode = rc;
|
|
13764
|
+
} catch (e) {
|
|
13765
|
+
fail(e);
|
|
13766
|
+
}
|
|
13767
|
+
});
|
|
13768
|
+
workerCmd.command("ps").description("List workers of this session (RUNNING + FINISHED); --all for every worker").option("--all", "Show workers of all sessions, not just this terminal's").action((opts) => {
|
|
13769
|
+
try {
|
|
13770
|
+
console.log(psOutput(currentLogDir(), opts.all === true));
|
|
13771
|
+
} catch (e) {
|
|
13772
|
+
fail(e);
|
|
13773
|
+
}
|
|
13774
|
+
});
|
|
13775
|
+
workerCmd.command("follow [id]").description("Live transcript of one worker, or of this session's running workers").option("--all", "Follow workers of all sessions").option("--auto-exit [secs]", "Exit secs after the workers end (pane mode; default from config)").action(async (id, opts) => {
|
|
13776
|
+
try {
|
|
13777
|
+
const { workers } = readWorkersSection();
|
|
13778
|
+
const autoExit = opts.autoExit === void 0 || opts.autoExit === false ? 0 : opts.autoExit === true ? workers.pane.autoExitSecs : Number(opts.autoExit);
|
|
13779
|
+
await followWorkers(currentLogDir(), id ?? null, opts.all === true, autoExit);
|
|
13780
|
+
} catch (e) {
|
|
13781
|
+
fail(e);
|
|
13782
|
+
}
|
|
13783
|
+
});
|
|
13784
|
+
workerCmd.command("replay <id>").description("Print the transcript of one finished or running worker").option("--tail <n>", "Only the last n rendered lines", parseIntArg).action((id, opts) => {
|
|
13785
|
+
try {
|
|
13786
|
+
console.log(replayOutput(currentLogDir(), id, process.stdout.isTTY === true, opts.tail));
|
|
13787
|
+
} catch (e) {
|
|
13788
|
+
fail(e);
|
|
13789
|
+
}
|
|
13790
|
+
});
|
|
13791
|
+
workerCmd.command("watch").description("ps refreshed every 2 seconds (plain `watch`, colors kept)").action(() => {
|
|
13792
|
+
const proc = spawn("watch", [
|
|
13793
|
+
"-n",
|
|
13794
|
+
"2",
|
|
13795
|
+
"pai worker ps --all"
|
|
13796
|
+
], { stdio: "inherit" });
|
|
13797
|
+
proc.on("error", (e) => fail(/* @__PURE__ */ new Error(`cannot run watch: ${e.message}`)));
|
|
13798
|
+
proc.on("close", (code) => {
|
|
13799
|
+
process.exitCode = code ?? 1;
|
|
13800
|
+
});
|
|
13801
|
+
});
|
|
13802
|
+
workerCmd.command("pane [id]").description("Open the follow pane for a worker (or one shared pane for this session)").option("--check", "Only report whether the pane is open, plus the profile file's path and font").action(async (id, opts) => {
|
|
13803
|
+
try {
|
|
13804
|
+
const { workers } = readWorkersSection();
|
|
13805
|
+
const logDir = currentLogDir();
|
|
13806
|
+
const term = process.env.ITERM_SESSION_ID ?? "";
|
|
13807
|
+
if (id) {
|
|
13808
|
+
const msg = opts.check ? await checkPaneForWorker(id, workers.pane.fontSize) : await openPaneForWorker(logDir, workers, id, term);
|
|
13809
|
+
console.log(msg);
|
|
13810
|
+
} else console.log(await openFollowPane(logDir, workers, term, opts.check === true));
|
|
13811
|
+
} catch (e) {
|
|
13812
|
+
fail(e);
|
|
13813
|
+
}
|
|
13814
|
+
});
|
|
13815
|
+
workerCmd.command("log [what]").description("all = ledger, tail = last ledger lines, <id> = raw event stream, none = list").action((what) => {
|
|
13816
|
+
try {
|
|
13817
|
+
const logDir = currentLogDir();
|
|
13818
|
+
const ledger = ledgerPath(logDir);
|
|
13819
|
+
if (what === "all") {
|
|
13820
|
+
console.log(existsSync(ledger) ? readFileSync(ledger, "utf8") : dim$1("(no ledger yet)"));
|
|
13821
|
+
return;
|
|
13822
|
+
}
|
|
13823
|
+
if (what === "tail") {
|
|
13824
|
+
if (!existsSync(ledger)) {
|
|
13825
|
+
console.log(dim$1("(no ledger yet)"));
|
|
13826
|
+
return;
|
|
13827
|
+
}
|
|
13828
|
+
const lines = readFileSync(ledger, "utf8").trim().split("\n");
|
|
13829
|
+
console.log(lines.slice(-20).join("\n"));
|
|
13830
|
+
return;
|
|
13831
|
+
}
|
|
13832
|
+
if (what) {
|
|
13833
|
+
const path = eventsPath(logDir, what);
|
|
13834
|
+
if (!existsSync(path)) {
|
|
13835
|
+
fail(/* @__PURE__ */ new Error(`no event log for ${what}`));
|
|
13836
|
+
return;
|
|
13837
|
+
}
|
|
13838
|
+
const lines = readFileSync(path, "utf8").trim().split("\n");
|
|
13839
|
+
console.log(lines.slice(-30).join("\n"));
|
|
13840
|
+
return;
|
|
13841
|
+
}
|
|
13842
|
+
if (!existsSync(logDir)) {
|
|
13843
|
+
console.log(dim$1("(no workers yet)"));
|
|
13844
|
+
return;
|
|
13845
|
+
}
|
|
13846
|
+
for (const f of readdirSync(logDir).filter((x) => x.endsWith(".jsonl")).sort()) {
|
|
13847
|
+
const st = statSync(`${logDir}/${f}`);
|
|
13848
|
+
const mb = (st.size / 1024 / 1024).toFixed(1);
|
|
13849
|
+
console.log(` ${f.replace(".jsonl", "")} ${mb} MB ${st.mtime.toISOString().slice(0, 19).replace("T", " ")}`);
|
|
13850
|
+
}
|
|
13851
|
+
} catch (e) {
|
|
13852
|
+
fail(e);
|
|
13853
|
+
}
|
|
13854
|
+
});
|
|
13855
|
+
workerCmd.command("say <id> <text>").description("Send one message to a running worker (forwarded to its open stdin)").action(async (id, text) => {
|
|
13856
|
+
try {
|
|
13857
|
+
await sayToWorker(currentLogDir(), id, text);
|
|
13858
|
+
console.log("ok");
|
|
13859
|
+
} catch (e) {
|
|
13860
|
+
fail(e);
|
|
13861
|
+
}
|
|
13862
|
+
});
|
|
13863
|
+
workerCmd.command("resume <id> <text>").description("Continue a finished worker on the same provider: claude --resume <session>").option("--print-id", "Print the new worker id on its own line (pane follow handoff)").option("--no-pane", "Do not open a follow pane for the resumed worker").action(async (id, text, opts) => {
|
|
13864
|
+
try {
|
|
13865
|
+
const old = loadStatus(currentLogDir(), id);
|
|
13866
|
+
if (!old) {
|
|
13867
|
+
fail(/* @__PURE__ */ new Error(`no worker named "${id}"`));
|
|
13868
|
+
return;
|
|
13869
|
+
}
|
|
13870
|
+
if (!old.claudeSession) {
|
|
13871
|
+
fail(/* @__PURE__ */ new Error(`worker ${id} recorded no Claude session id — it predates resume support or ran through an engine that does not expose one`));
|
|
13872
|
+
return;
|
|
13873
|
+
}
|
|
13874
|
+
let newId = "";
|
|
13875
|
+
const rc = await runWorker({
|
|
13876
|
+
providerFlag: old.provider,
|
|
13877
|
+
modelFlag: old.model,
|
|
13878
|
+
label: `↩ ${old.label}`,
|
|
13879
|
+
noPane: opts.pane === false,
|
|
13880
|
+
claudeArgs: [
|
|
13881
|
+
"--resume",
|
|
13882
|
+
old.claudeSession,
|
|
13883
|
+
"-p",
|
|
13884
|
+
text
|
|
13885
|
+
],
|
|
13886
|
+
onWorkerStart: (wid) => {
|
|
13887
|
+
newId = wid;
|
|
13888
|
+
}
|
|
13889
|
+
});
|
|
13890
|
+
if (opts.printId === true && newId) console.log(newId);
|
|
13891
|
+
process.exitCode = rc;
|
|
13892
|
+
} catch (e) {
|
|
13893
|
+
fail(e);
|
|
13894
|
+
}
|
|
13895
|
+
});
|
|
13896
|
+
workerCmd.command("proxy [stop]").description("The local Anthropic↔OpenAI proxy (loopback only); started on demand by `run`,\n`proxy` starts/verifies it, `proxy stop` stops it again").option("--port <n>", `Port to listen on (default ${DEFAULT_PROXY_PORT})`, parseIntArg).action(async (stop, opts) => {
|
|
13897
|
+
try {
|
|
13898
|
+
const logDir = currentLogDir();
|
|
13899
|
+
if (stop === "stop") {
|
|
13900
|
+
console.log(stopProxy(logDir));
|
|
13901
|
+
return;
|
|
13902
|
+
}
|
|
13903
|
+
if (stop) {
|
|
13904
|
+
fail(/* @__PURE__ */ new Error(`unknown proxy argument "${stop}" (expected: stop)`));
|
|
13905
|
+
return;
|
|
13906
|
+
}
|
|
13907
|
+
const url = await ensureProxyRunning(opts.port ?? DEFAULT_PROXY_PORT, logDir);
|
|
13908
|
+
console.log(`proxy listening on ${url} — point openai providers at ${url}/<provider>`);
|
|
13909
|
+
} catch (e) {
|
|
13910
|
+
fail(e);
|
|
13911
|
+
}
|
|
13912
|
+
});
|
|
13913
|
+
workerCmd.command("mcp [list]").description("MCP servers workers may load via --mcp / roles, and the configured sets").action((what) => {
|
|
13914
|
+
if (what && what !== "list") {
|
|
13915
|
+
fail(/* @__PURE__ */ new Error(`unknown mcp argument "${what}" (expected: list)`));
|
|
13916
|
+
return;
|
|
13917
|
+
}
|
|
13918
|
+
try {
|
|
13919
|
+
const { workers } = readWorkersSection();
|
|
13920
|
+
console.log(describeMcp(workers));
|
|
13921
|
+
} catch (e) {
|
|
13922
|
+
fail(e);
|
|
13923
|
+
}
|
|
13924
|
+
});
|
|
13925
|
+
workerCmd.command("status-line [term] [cwd]").description("One-line worker summary for a status bar (empty when none in scope).\nCalled by statusline-command.sh with ITERM_SESSION_ID and the pane cwd.").action((term, cwd) => {
|
|
13926
|
+
try {
|
|
13927
|
+
const out = statusLineOutput(currentLogDir(), term ?? process.env.ITERM_SESSION_ID ?? "", cwd ?? process.cwd());
|
|
13928
|
+
if (out) console.log(out);
|
|
13929
|
+
} catch (e) {
|
|
13930
|
+
fail(e);
|
|
13931
|
+
}
|
|
13932
|
+
});
|
|
13933
|
+
workerCmd.command("on").description("Route Agent-tool subagents to workers (default when a provider exists)").action(() => {
|
|
13934
|
+
try {
|
|
13935
|
+
setWorkersEnabled(true);
|
|
13936
|
+
console.log("workers on — the Agent hook now denies and suggests `pai worker run`");
|
|
13937
|
+
} catch (e) {
|
|
13938
|
+
fail(e);
|
|
13939
|
+
}
|
|
13940
|
+
});
|
|
13941
|
+
workerCmd.command("off").description("Stop routing: Agent tool runs on Anthropic again").action(() => {
|
|
13942
|
+
try {
|
|
13943
|
+
setWorkersEnabled(false);
|
|
13944
|
+
console.log("workers off — Agent-tool subagents run on Anthropic");
|
|
13945
|
+
} catch (e) {
|
|
13946
|
+
fail(e);
|
|
13947
|
+
}
|
|
13948
|
+
});
|
|
13949
|
+
workerCmd.command("install").description("Migrate: Agent hook in settings.json, ~/.local/bin glm* shims, old script cleanup").action(() => {
|
|
13950
|
+
try {
|
|
13951
|
+
const r = installWorkers();
|
|
13952
|
+
for (const line of r.lines) console.log(` ${line}`);
|
|
13953
|
+
} catch (e) {
|
|
13954
|
+
fail(e);
|
|
13955
|
+
}
|
|
13956
|
+
});
|
|
13957
|
+
registerWorkerProviderCommands(workerCmd.command("providers").description("Providers: list, add, remove, use, enable, disable, test"));
|
|
13958
|
+
registerWorkerRoleCommands(workerCmd);
|
|
13959
|
+
}
|
|
13960
|
+
function parseIntArg(v) {
|
|
13961
|
+
const n = parseInt(v, 10);
|
|
13962
|
+
return Number.isNaN(n) ? 0 : n;
|
|
13963
|
+
}
|
|
13964
|
+
|
|
13374
13965
|
//#endregion
|
|
13375
13966
|
//#region src/cli/commands/session-cleanup/types.ts
|
|
13376
13967
|
const TEMPLATE_INDICATORS = [
|
|
@@ -13735,7 +14326,7 @@ async function countVectorDbPaths(oldPaths) {
|
|
|
13735
14326
|
if (oldPaths.length === 0) return 0;
|
|
13736
14327
|
try {
|
|
13737
14328
|
const { loadConfig } = await import("./config-C_ErGddD.mjs");
|
|
13738
|
-
const { PostgresBackend } = await import("./postgres
|
|
14329
|
+
const { PostgresBackend } = await import("./postgres--BjPtLa0.mjs");
|
|
13739
14330
|
const config = loadConfig();
|
|
13740
14331
|
if (config.storageBackend !== "postgres") return 0;
|
|
13741
14332
|
const pgBackend = new PostgresBackend(config.postgres ?? {});
|
|
@@ -13756,7 +14347,7 @@ async function updateVectorDbPaths(moves) {
|
|
|
13756
14347
|
if (moves.length === 0) return 0;
|
|
13757
14348
|
try {
|
|
13758
14349
|
const { loadConfig } = await import("./config-C_ErGddD.mjs");
|
|
13759
|
-
const { PostgresBackend } = await import("./postgres
|
|
14350
|
+
const { PostgresBackend } = await import("./postgres--BjPtLa0.mjs");
|
|
13760
14351
|
const config = loadConfig();
|
|
13761
14352
|
if (config.storageBackend !== "postgres") return 0;
|
|
13762
14353
|
const pgBackend = new PostgresBackend(config.postgres ?? {});
|
|
@@ -15099,6 +15690,7 @@ Examples:
|
|
|
15099
15690
|
const sessionCmd = program.command("session").description("Session management: list, info, checkpoint, handover, cleanup, slug, tag, route.\nShort forms: `pai pause`, `pai end`, `pai resume <name>`.");
|
|
15100
15691
|
registerSessionCommands(sessionCmd, getDb);
|
|
15101
15692
|
registerSessionCleanupCommand(sessionCmd, getDb);
|
|
15693
|
+
registerWorkerCommands(program.command("worker").description("Run subagents on configured worker providers: run, ps, follow, replay,\npane, log, status-line, providers, roles, on, off, install"));
|
|
15102
15694
|
program.command("sessions").description("Alias for `pai` — show the unified deduped listing.").option("--all", "Show all entries including cold / zero-session / archived projects").option("-n, --n <count>", "Max candidates for history search", "20").action(async (opts) => {
|
|
15103
15695
|
await cmdMain(getDb(), void 0, void 0, opts);
|
|
15104
15696
|
});
|
|
@@ -15280,4 +15872,4 @@ claude() {
|
|
|
15280
15872
|
|
|
15281
15873
|
//#endregion
|
|
15282
15874
|
export { drainStdio as n, buildProgram as t };
|
|
15283
|
-
//# sourceMappingURL=program-
|
|
15875
|
+
//# sourceMappingURL=program-CEIHn_Ma.mjs.map
|