@tekmidian/pai 0.49.0 → 0.50.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/{aibroker-client-D5NT0S1N.mjs → aibroker-client-Bu72yPKj.mjs} +1 -1
- package/dist/{aibroker-client-Bn9SnGMa.mjs → aibroker-client-CDIXmq-e.mjs} +1 -1
- package/dist/{aibroker-client-Bn9SnGMa.mjs.map → aibroker-client-CDIXmq-e.mjs.map} +1 -1
- package/dist/{chain-CNSa0A3G.mjs → chain-BBbVbWVv.mjs} +4 -4
- package/dist/{chain-CNSa0A3G.mjs.map → chain-BBbVbWVv.mjs.map} +1 -1
- package/dist/cli/index.mjs +14 -14
- package/dist/cli/program.mjs +14 -14
- package/dist/config-Bn9jpblO.mjs +653 -0
- package/dist/config-Bn9jpblO.mjs.map +1 -0
- package/dist/{config-DxMZjpvY.mjs → config-DYXARgk8.mjs} +180 -151
- package/dist/config-DYXARgk8.mjs.map +1 -0
- package/dist/config-RjeVGJ7K.mjs +3 -0
- package/dist/{context-handover-cache-B9NKSjwR.mjs → context-handover-cache-yOuK9RIk.mjs} +5 -5
- package/dist/{context-handover-cache-B9NKSjwR.mjs.map → context-handover-cache-yOuK9RIk.mjs.map} +1 -1
- package/dist/daemon/index.mjs +13 -12
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/{daemon-DZ4Q0jrs.mjs → daemon-CBTQYtHF.mjs} +49 -30
- package/dist/daemon-CBTQYtHF.mjs.map +1 -0
- package/dist/daemon-Vf7pHnjR.mjs +25 -0
- package/dist/daemon-mcp/index.mjs +186 -23
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/factory-Ba8j9K7q.mjs +3 -0
- package/dist/{factory-DNYAqjH0.mjs → factory-Bt7TWQje.mjs} +3 -3
- package/dist/{factory-DNYAqjH0.mjs.map → factory-Bt7TWQje.mjs.map} +1 -1
- package/dist/{fallback-IeR2FrFn.mjs → fallback-CIGspCSi.mjs} +260 -10
- package/dist/fallback-CIGspCSi.mjs.map +1 -0
- package/dist/hooks/capture-all-events.mjs +80 -8
- package/dist/hooks/capture-all-events.mjs.map +4 -4
- package/dist/hooks/context-compression-hook.mjs +54 -39
- package/dist/hooks/context-compression-hook.mjs.map +4 -4
- package/dist/hooks/post-compact-inject.mjs +54 -39
- package/dist/hooks/post-compact-inject.mjs.map +4 -4
- package/dist/hooks/route-agents-to-worker.mjs +204 -109
- package/dist/hooks/route-agents-to-worker.mjs.map +4 -4
- package/dist/hooks/whisper-rules.mjs +7613 -9
- package/dist/hooks/whisper-rules.mjs.map +4 -4
- package/dist/hooks/worker-proxy.mjs +204 -109
- package/dist/hooks/worker-proxy.mjs.map +4 -4
- package/dist/hooks/worker-status-line.mjs +211 -116
- package/dist/hooks/worker-status-line.mjs.map +4 -4
- package/dist/hooks/worker-supervision.mjs +208 -113
- package/dist/hooks/worker-supervision.mjs.map +4 -4
- package/dist/{kg-entity-BSjM5xZh.mjs → kg-entity-BSW7ra8i.mjs} +1 -1
- package/dist/{kg-entity-BSjM5xZh.mjs.map → kg-entity-BSW7ra8i.mjs.map} +1 -1
- package/dist/main-resolver-BTastpp4.mjs +7 -0
- package/dist/{main-resolver-DvYu9jbl.mjs → main-resolver-BmZPCmgj.mjs} +3 -3
- package/dist/{main-resolver-DvYu9jbl.mjs.map → main-resolver-BmZPCmgj.mjs.map} +1 -1
- package/dist/{planner-Cn8HmT7l.mjs → planner-BiH9msqq.mjs} +6 -6
- package/dist/{planner-Cn8HmT7l.mjs.map → planner-BiH9msqq.mjs.map} +1 -1
- package/dist/{program-D9-wfmkZ.mjs → program-BX67-4I2.mjs} +233 -277
- package/dist/program-BX67-4I2.mjs.map +1 -0
- package/dist/query-feedback-Bm6XvCmg.mjs +3 -0
- package/dist/{query-feedback-CYyOGi3y.mjs → query-feedback-CTs_jx6i.mjs} +1 -1
- package/dist/{query-feedback-CYyOGi3y.mjs.map → query-feedback-CTs_jx6i.mjs.map} +1 -1
- package/dist/{run-Cls0YGgV.mjs → run-TChOnMO_.mjs} +1388 -1183
- package/dist/run-TChOnMO_.mjs.map +1 -0
- package/dist/{server-Ch99xt5K.mjs → server-R9UkCn3v.mjs} +42 -3
- package/dist/server-R9UkCn3v.mjs.map +1 -0
- package/dist/session-keepalive-BuG3wvik.mjs +579 -0
- package/dist/session-keepalive-BuG3wvik.mjs.map +1 -0
- package/dist/skills/VaultContext/SKILL.md +1 -1
- package/dist/{state-DnYGRmS7.mjs → state-Ca0ltLgI.mjs} +1 -1
- package/dist/{state-PJrgfDLW.mjs → state-DwrsOoMi.mjs} +1 -1
- package/dist/{state-PJrgfDLW.mjs.map → state-DwrsOoMi.mjs.map} +1 -1
- package/dist/{tools-B77WHeVZ.mjs → tools-BEqiJt_j.mjs} +13 -13
- package/dist/{tools-B77WHeVZ.mjs.map → tools-BEqiJt_j.mjs.map} +1 -1
- package/dist/{tools-NnR_n-Ja.mjs → tools-BwsOTmjh.mjs} +2 -2
- package/dist/{wakeup-UrhiW-KU.mjs → wakeup-Yk5lQ0jb.mjs} +1 -1
- package/dist/{wakeup-UrhiW-KU.mjs.map → wakeup-Yk5lQ0jb.mjs.map} +1 -1
- package/dist/{work-queue-worker-BA5IDBwQ.mjs → work-queue-worker-B9zVWImP.mjs} +3 -3
- package/dist/{work-queue-worker-BA5IDBwQ.mjs.map → work-queue-worker-B9zVWImP.mjs.map} +1 -1
- package/dist/work-queue-worker-D-DnQ7oh.mjs +14 -0
- package/dist/{zettelkasten-BBjA7GcP.mjs → zettelkasten-DezbXghl.mjs} +2 -2
- package/dist/{zettelkasten-BBjA7GcP.mjs.map → zettelkasten-DezbXghl.mjs.map} +1 -1
- package/docs/cache-keepalive.md +138 -0
- package/docs/commands/README.md +7 -0
- package/docs/commands/config.md +84 -0
- package/docs/commands/daemon.md +6 -0
- package/docs/commands/worker.md +37 -2
- package/docs/config.md +171 -0
- package/docs/model-catalogue.md +502 -0
- package/docs/provider-independence-details.md +1 -1
- package/docs/task-bus.md +1 -1
- package/docs/worker.md +33 -5
- package/docs/workers-config.md +105 -12
- package/package.json +1 -1
- package/src/hooks/ts/capture-all-events.test.ts +71 -0
- package/src/hooks/ts/capture-all-events.ts +10 -3
- package/src/hooks/ts/user-prompt/whisper-rules.test.ts +56 -1
- package/src/hooks/ts/user-prompt/whisper-rules.ts +24 -0
- package/statusline-command.sh +81 -6
- package/dist/config-DCu8THKi.mjs +0 -228
- package/dist/config-DCu8THKi.mjs.map +0 -1
- package/dist/config-DeqFqa1N.mjs +0 -3
- package/dist/config-DxMZjpvY.mjs.map +0 -1
- package/dist/daemon-CYpVy_Dw.mjs +0 -24
- package/dist/daemon-DZ4Q0jrs.mjs.map +0 -1
- package/dist/factory-47Qf_df7.mjs +0 -3
- package/dist/fallback-IeR2FrFn.mjs.map +0 -1
- package/dist/indexer-C5Le7XE7.mjs +0 -1
- package/dist/main-resolver-CnfnWa8C.mjs +0 -7
- package/dist/program-D9-wfmkZ.mjs.map +0 -1
- package/dist/query-feedback-C7rSfiXU.mjs +0 -3
- package/dist/run-Cls0YGgV.mjs.map +0 -1
- package/dist/server-Ch99xt5K.mjs.map +0 -1
- package/dist/work-queue-worker-DLM2wlqM.mjs +0 -14
|
@@ -8,21 +8,22 @@ import { i as indexProject, n as indexAll, t as embedChunks } from "./sync-Btcb6
|
|
|
8
8
|
import { t as STOP_WORDS } from "./stop-words-BaMEGVeY.mjs";
|
|
9
9
|
import { a as searchMemory, i as populateSlugs } from "./search-B69cQwDB.mjs";
|
|
10
10
|
import { n as formatDetection, r as formatDetectionJson, t as detectProject } from "./detect-Bf2z-oKB.mjs";
|
|
11
|
-
import { B as claudeProjectsDir, C as findLatestNote, H as scanTranscriptFolders, I as resolveHome, L as saveScanConfig, M as cmdScan, N as loadScanConfig, P as migrateScanConfig, R as upsertProject, S as applyContinue, T as readBodyFile, U as transcriptFiles, V as findMovedProjects, d as migrateWorkQueue, g as migrateSummaryCooldowns, t as readContextHandoverCache, v as extractAndStoreTriples, w as findNotesDir$1, x as appendCheckpointToNote, z as upsertSession } from "./context-handover-cache-
|
|
12
|
-
import {
|
|
11
|
+
import { B as claudeProjectsDir, C as findLatestNote, H as scanTranscriptFolders, I as resolveHome, L as saveScanConfig, M as cmdScan, N as loadScanConfig, P as migrateScanConfig, R as upsertProject, S as applyContinue, T as readBodyFile, U as transcriptFiles, V as findMovedProjects, d as migrateWorkQueue, g as migrateSummaryCooldowns, t as readContextHandoverCache, v as extractAndStoreTriples, w as findNotesDir$1, x as appendCheckpointToNote, z as upsertSession } from "./context-handover-cache-yOuK9RIk.mjs";
|
|
12
|
+
import { b as OWNER_LABEL_PREFIX, c as migrateConfigFile, d as readMainConfigRaw, f as writeMainConfigRaw, h as resolvedMainConfigPath, l as paiConfigFilePath, m as migrateMainConfigToYaml, n as CONFIG_FILE$2, o as expandHome, p as MainConfigError, s as loadConfig, t as CONFIG_DIR, u as paiConfigYamlFilePath, v as readJsonStrict, x as UNROUTED, y as writeJsonAtomic } from "./config-Bn9jpblO.mjs";
|
|
13
13
|
import { a as schedulerLogPath, i as paiSocketPath, n as daemonLogPath, r as daemonPidPath } from "./runtime-paths-B0P1TvUr.mjs";
|
|
14
|
-
import { A as
|
|
15
|
-
import {
|
|
14
|
+
import { A as migrateWorkersToYaml, C as readWorkersSection, F as workersYamlPath, I as DEFAULT_CONTEXT_WINDOW, L as contextWindowFromModelId, M as parseWorkersYamlDocument, N as relocateWorkersYaml, O as initWorkersYaml, P as workersYamlLegacyNotice, R as stripModelVariant, _ as isModelCapability, b as nativeAnthropicProvider, c as MODEL_CAPABILITIES, d as WorkersConfigError, g as getProviderOrNative, h as expandHome$1, j as needsWorkersYamlRelocation, k as inlineWorkersYamlKeys, l as PROVIDER_TAGS, t as ANTHROPIC_NATIVE, y as maskKey } from "./config-DYXARgk8.mjs";
|
|
15
|
+
import { B as setWorkerLabel, C as mergeWorker, D as UNLABELED, F as loadStatuses, G as describeMcp, H as longInlinePromptHint, K as expandMcpNames, M as isChatPane, N as isLive, P as loadStatus, R as ownsPid, S as discardWorker, T as appendLedger, U as parseRunnerArgs, V as waitForTerminalStatus, a as projectLaunchConfig, c as handoffFromInside, f as checkPaneForWorker, i as usageContextTokens, m as openPaneForWorker, n as runWorker, o as parseWorkerReport, p as openFollowPane, r as testProvider, u as sayToWorker, z as saveStatus } from "./run-TChOnMO_.mjs";
|
|
16
|
+
import { a as totalUsageTokens, i as parseSessionUsage, r as sessionKeepaliveLedgerSummary, t as loadSessionKeepaliveState } from "./session-keepalive-BuG3wvik.mjs";
|
|
16
17
|
import { t as PaiClient } from "./ipc-client-C6NryMMb.mjs";
|
|
17
|
-
import { r as humanDuration, t as createStorageBackend } from "./factory-
|
|
18
|
-
import { n as
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import { i as
|
|
24
|
-
import { t as
|
|
25
|
-
import {
|
|
18
|
+
import { r as humanDuration, t as createStorageBackend } from "./factory-Bt7TWQje.mjs";
|
|
19
|
+
import { A as getConfigValueOp, C as psOutput, D as resolveSpecPath, E as readSpecPrompt, M as setConfigValueOp, N as unsetConfigValueOp, O as MainConfigOpsError, S as followWorkers, T as statusLineOutput, _ as setWorkersEnabled, a as addProvider, b as updateProvider, c as describeModels, d as removeProvider, f as resolveProviderName, g as setProviderModel, h as setProviderEnabled, i as fallbackStatusText, j as listConfigOp, k as formatConfigGetOutput, l as describeProviders, m as setClass, n as fallbackOn, o as classTargetText, p as setCapabilityPreference, r as fallbackStatus, s as describeCapabilities, t as fallbackOff, u as modelPrefsText, v as unsetCapabilityPreference, w as replayOutput, x as useProvider, y as unsetClass } from "./fallback-CIGspCSi.mjs";
|
|
20
|
+
import { n as migrateIdentityFile } from "./wakeup-Yk5lQ0jb.mjs";
|
|
21
|
+
import { a as renderDedupedSessions, c as probeResume, d as fmtAge, f as migrateSessionScanCache, h as scanSessions, i as normalizeName$2, l as restoreTopLevel, m as scanCacheFilePath, o as hasConversation, p as resolveSessionByNameOrId, r as buildDeduped, s as launchInDir, t as cmdMain, u as printExitDir } from "./main-resolver-BmZPCmgj.mjs";
|
|
22
|
+
import { n as queriesDirPath, t as migrateQueriesDir } from "./query-feedback-CTs_jx6i.mjs";
|
|
23
|
+
import { s as kgQuery } from "./kg-entity-BSW7ra8i.mjs";
|
|
24
|
+
import { g as ledgerPath, h as eventsPath, i as buildRunEnv, m as ensureNoMcpConfig, n as ensureProxyRunning, r as stopProxy, t as DEFAULT_PROXY_PORT, y as workersLogDir } from "./server-R9UkCn3v.mjs";
|
|
25
|
+
import { i as sendToSession, n as fetchLiveSessions, r as revealItermSession, t as callAiBroker } from "./aibroker-client-CDIXmq-e.mjs";
|
|
26
|
+
import { t as runChain } from "./chain-BBbVbWVv.mjs";
|
|
26
27
|
import { appendFileSync, chmodSync, copyFileSync, cpSync, createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
27
28
|
import { homedir, platform, tmpdir } from "node:os";
|
|
28
29
|
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
@@ -1974,7 +1975,7 @@ function cmdUnregister(db, slug, opts = {}) {
|
|
|
1974
1975
|
//#region src/cli/commands/project/projects-index.ts
|
|
1975
1976
|
function registerProjectsCommands(projectsCmd, getDb) {
|
|
1976
1977
|
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) => {
|
|
1977
|
-
const { cmdMain } = await import("./main-resolver-
|
|
1978
|
+
const { cmdMain } = await import("./main-resolver-BTastpp4.mjs");
|
|
1978
1979
|
await cmdMain(getDb(), void 0, void 0, opts);
|
|
1979
1980
|
});
|
|
1980
1981
|
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) => {
|
|
@@ -2976,7 +2977,7 @@ function registerStatsCommands(memoryCmd, getDb) {
|
|
|
2976
2977
|
console.log(` ${bold$1("defaultLimit:")} ${search.defaultLimit}`);
|
|
2977
2978
|
console.log(` ${bold$1("snippetLength:")} ${search.snippetLength}`);
|
|
2978
2979
|
console.log();
|
|
2979
|
-
console.log(dim$1(` Config file: ${
|
|
2980
|
+
console.log(dim$1(` Config file: ${paiConfigFilePath()}`));
|
|
2980
2981
|
console.log(dim$1(` Edit directly or use: pai memory settings <key> <value>`));
|
|
2981
2982
|
console.log();
|
|
2982
2983
|
return;
|
|
@@ -3005,11 +3006,11 @@ function registerStatsCommands(memoryCmd, getDb) {
|
|
|
3005
3006
|
process.exitCode = 1;
|
|
3006
3007
|
return;
|
|
3007
3008
|
}
|
|
3008
|
-
let fileConfig
|
|
3009
|
-
|
|
3010
|
-
fileConfig =
|
|
3011
|
-
} catch {
|
|
3012
|
-
console.error(err(`Could not
|
|
3009
|
+
let fileConfig;
|
|
3010
|
+
try {
|
|
3011
|
+
fileConfig = readMainConfigRaw();
|
|
3012
|
+
} catch (e) {
|
|
3013
|
+
console.error(err(`Could not read config: ${e instanceof Error ? e.message : String(e)}`));
|
|
3013
3014
|
process.exitCode = 1;
|
|
3014
3015
|
return;
|
|
3015
3016
|
}
|
|
@@ -3037,8 +3038,7 @@ function registerStatsCommands(memoryCmd, getDb) {
|
|
|
3037
3038
|
}
|
|
3038
3039
|
fileConfig.search[key] = parsed;
|
|
3039
3040
|
try {
|
|
3040
|
-
|
|
3041
|
-
writeFileSync(CONFIG_FILE$2, JSON.stringify(fileConfig, null, 2) + "\n", "utf-8");
|
|
3041
|
+
writeMainConfigRaw(fileConfig);
|
|
3042
3042
|
console.log(ok$1(`Set search.${key} = ${parsed}`));
|
|
3043
3043
|
console.log(dim$1(` Restart daemon to apply: pai daemon restart`));
|
|
3044
3044
|
} catch (e) {
|
|
@@ -3052,8 +3052,8 @@ function registerStatsCommands(memoryCmd, getDb) {
|
|
|
3052
3052
|
//#region src/cli/commands/memory/sources-cmd.ts
|
|
3053
3053
|
function registerSourcesCommand(memoryCmd) {
|
|
3054
3054
|
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) => {
|
|
3055
|
-
const { createStorageBackend } = await import("./factory-
|
|
3056
|
-
const { loadConfig } = await import("./config-
|
|
3055
|
+
const { createStorageBackend } = await import("./factory-Ba8j9K7q.mjs");
|
|
3056
|
+
const { loadConfig } = await import("./config-RjeVGJ7K.mjs");
|
|
3057
3057
|
const { cmdMemorySources } = await import("./sources-KBGEA_E6.mjs");
|
|
3058
3058
|
let backend;
|
|
3059
3059
|
try {
|
|
@@ -3153,23 +3153,17 @@ function checkDeliveryReachability(deliverTo, sendingAccount, sendingAccountAlia
|
|
|
3153
3153
|
//#endregion
|
|
3154
3154
|
//#region src/cli/commands/identity.ts
|
|
3155
3155
|
function readConfig() {
|
|
3156
|
-
if (!existsSync(CONFIG_FILE$2)) return {};
|
|
3157
3156
|
try {
|
|
3158
|
-
return
|
|
3157
|
+
return readMainConfigRaw();
|
|
3159
3158
|
} catch (e) {
|
|
3160
|
-
console.error(err("pai identity: ") + `Could not
|
|
3159
|
+
console.error(err("pai identity: ") + `Could not read ${paiConfigFilePath()}: ${String(e)}`);
|
|
3161
3160
|
process.exit(1);
|
|
3162
3161
|
}
|
|
3163
3162
|
}
|
|
3164
|
-
/**
|
|
3165
|
-
*
|
|
3166
|
-
* config truncated. The daemon reads this file; a half-written one takes the
|
|
3167
|
-
* whole system down rather than just this command.
|
|
3168
|
-
*/
|
|
3163
|
+
/** Writes through writeMainConfigRaw — comment-preserving into config.yaml
|
|
3164
|
+
* when it exists, else atomic into config.json. */
|
|
3169
3165
|
function writeConfig(config) {
|
|
3170
|
-
|
|
3171
|
-
writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
3172
|
-
renameSync(tmp, CONFIG_FILE$2);
|
|
3166
|
+
writeMainConfigRaw(config);
|
|
3173
3167
|
}
|
|
3174
3168
|
function identityOf(config) {
|
|
3175
3169
|
return config.identity ?? { selfEmails: [] };
|
|
@@ -3263,6 +3257,33 @@ function registerIdentityCommands(identityCmd) {
|
|
|
3263
3257
|
});
|
|
3264
3258
|
}
|
|
3265
3259
|
|
|
3260
|
+
//#endregion
|
|
3261
|
+
//#region src/config/voices-config.ts
|
|
3262
|
+
/**
|
|
3263
|
+
* voices-config.ts — voices.json / voices.yaml, the same load-either /
|
|
3264
|
+
* migrate pattern as the main config (see main-config.ts) and workers.yaml.
|
|
3265
|
+
*
|
|
3266
|
+
* Nothing in src/ currently reads voices.json (see the note on
|
|
3267
|
+
* migrateVoicesJson in pai-files.ts) — this exists so the one place that
|
|
3268
|
+
* would ever load it has the YAML-preferring behaviour already in place,
|
|
3269
|
+
* and so `pai config yaml` has one JSON→YAML step to run for it, same as
|
|
3270
|
+
* every other PAI config file.
|
|
3271
|
+
*/
|
|
3272
|
+
function voicesJsonPath() {
|
|
3273
|
+
return paiHomePath("voices.json");
|
|
3274
|
+
}
|
|
3275
|
+
function voicesYamlPath() {
|
|
3276
|
+
return paiHomePath("voices.yaml");
|
|
3277
|
+
}
|
|
3278
|
+
/** `pai config yaml`'s voices.json → voices.yaml step. */
|
|
3279
|
+
function migrateVoicesToYaml(opts = {}) {
|
|
3280
|
+
return migrateMainConfigToYaml(voicesJsonPath(), {
|
|
3281
|
+
...opts,
|
|
3282
|
+
yamlPath: voicesYamlPath(),
|
|
3283
|
+
sectionComments: {}
|
|
3284
|
+
});
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3266
3287
|
//#endregion
|
|
3267
3288
|
//#region src/tasks/dispatch.ts
|
|
3268
3289
|
/**
|
|
@@ -3732,7 +3753,7 @@ async function escalate(task, headline, detail, state, now, opts) {
|
|
|
3732
3753
|
let delivered = false;
|
|
3733
3754
|
try {
|
|
3734
3755
|
const { routeNotification } = await import("./router-CremHKTI.mjs");
|
|
3735
|
-
const { loadConfig } = await import("./config-
|
|
3756
|
+
const { loadConfig } = await import("./config-RjeVGJ7K.mjs");
|
|
3736
3757
|
delivered = (await routeNotification({
|
|
3737
3758
|
event: "error",
|
|
3738
3759
|
title: `PAI: ${headline}`,
|
|
@@ -5153,42 +5174,38 @@ function checkHealth(vaultPath, db) {
|
|
|
5153
5174
|
//#endregion
|
|
5154
5175
|
//#region src/cli/commands/obsidian.ts
|
|
5155
5176
|
/**
|
|
5156
|
-
* Read obsidianVaultPath from
|
|
5157
|
-
* Falls back to defaultVaultPath() if not set.
|
|
5177
|
+
* Read obsidianVaultPath from the main config (config.yaml if it exists,
|
|
5178
|
+
* else config.json). Falls back to defaultVaultPath() if not set.
|
|
5158
5179
|
*/
|
|
5159
5180
|
function getVaultPath(override) {
|
|
5160
5181
|
if (override) return expandHome(override);
|
|
5161
5182
|
try {
|
|
5162
|
-
const
|
|
5163
|
-
const cfg = JSON.parse(raw);
|
|
5183
|
+
const cfg = readMainConfigRaw();
|
|
5164
5184
|
if (typeof cfg.obsidianVaultPath === "string" && cfg.obsidianVaultPath) return expandHome(cfg.obsidianVaultPath);
|
|
5165
5185
|
} catch {}
|
|
5166
5186
|
return defaultVaultPath();
|
|
5167
5187
|
}
|
|
5168
5188
|
/**
|
|
5169
|
-
* Read obsidianVaultPath from
|
|
5189
|
+
* Read obsidianVaultPath from the main config without falling back to
|
|
5170
5190
|
* defaultVaultPath() — undefined means "not set", distinct from getVaultPath's
|
|
5171
5191
|
* "give me something usable". Used by `pai config migrate` to decide whether
|
|
5172
5192
|
* the stored path still points at the pre-2026-09-19 default.
|
|
5173
5193
|
*/
|
|
5174
5194
|
function getConfigObsidianVaultPathRaw() {
|
|
5175
5195
|
try {
|
|
5176
|
-
const
|
|
5177
|
-
const cfg = JSON.parse(raw);
|
|
5196
|
+
const cfg = readMainConfigRaw();
|
|
5178
5197
|
if (typeof cfg.obsidianVaultPath === "string" && cfg.obsidianVaultPath) return expandHome(cfg.obsidianVaultPath);
|
|
5179
5198
|
} catch {}
|
|
5180
5199
|
}
|
|
5181
|
-
/**
|
|
5182
|
-
*
|
|
5183
|
-
*/
|
|
5200
|
+
/** Persist obsidianVaultPath into the main config so future syncs use the
|
|
5201
|
+
* same path (config.yaml if it exists, else config.json). */
|
|
5184
5202
|
function saveVaultPath(vaultPath) {
|
|
5185
5203
|
let cfg = {};
|
|
5186
5204
|
try {
|
|
5187
|
-
cfg =
|
|
5205
|
+
cfg = readMainConfigRaw();
|
|
5188
5206
|
} catch {}
|
|
5189
5207
|
cfg.obsidianVaultPath = vaultPath;
|
|
5190
|
-
|
|
5191
|
-
writeFileSync(CONFIG_FILE$2, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
|
|
5208
|
+
writeMainConfigRaw(cfg);
|
|
5192
5209
|
}
|
|
5193
5210
|
function cmdSync(db, opts) {
|
|
5194
5211
|
const vaultPath = getVaultPath(opts.vault);
|
|
@@ -5956,15 +5973,17 @@ function migrateObsidianVaultPathConfig(dryRun) {
|
|
|
5956
5973
|
function registerConfigCommands(configCmd) {
|
|
5957
5974
|
configCmd.command("path").description("Print the PAI_HOME namespace dir and each resolved per-user file").action(() => {
|
|
5958
5975
|
console.log(bold$1("PAI_HOME: ") + paiHomeDir());
|
|
5959
|
-
console.log(` config.json:
|
|
5976
|
+
console.log(` config.json / config.yaml: ${paiConfigFilePath()} / ${paiConfigYamlFilePath()}`);
|
|
5960
5977
|
console.log(` workers.yaml: ${workersYamlPath()}`);
|
|
5961
5978
|
console.log(` whisper-rules.md: ${whisperRulesPath()}`);
|
|
5962
5979
|
console.log(` advisor-mode.json: ${advisorModePath()}`);
|
|
5963
5980
|
console.log(` session-state/: ${sessionStateDir()}`);
|
|
5964
5981
|
console.log(` session-scan-cache.json: ${scanCacheFilePath()}`);
|
|
5965
5982
|
console.log(` queries/: ${queriesDirPath()}`);
|
|
5966
|
-
console.log(` summary-cooldowns.json, work-queue.json, kg-backfill-state.json
|
|
5967
|
-
console.log(`
|
|
5983
|
+
console.log(` summary-cooldowns.json, work-queue.json, kg-backfill-state.json:`);
|
|
5984
|
+
console.log(` all under PAI_HOME`);
|
|
5985
|
+
console.log(` voices.json / voices.yaml: ${voicesJsonPath()} / ${voicesYamlPath()}`);
|
|
5986
|
+
console.log(` federation.db (orphan): under PAI_HOME`);
|
|
5968
5987
|
console.log(` logs/workers/: ${paiHomePath("logs", "workers")} (pass --logs to move)`);
|
|
5969
5988
|
console.log(` History/: ${paiHomePath("History")} (pass --history to move)`);
|
|
5970
5989
|
console.log(` agent-sessions.json, session-routing.json, History/security/security-events.jsonl:`);
|
|
@@ -5979,6 +5998,65 @@ function registerConfigCommands(configCmd) {
|
|
|
5979
5998
|
console.log(` agents/: ${paiHomePath("agents")} (~/.claude/Agents symlinks here after migrate)`);
|
|
5980
5999
|
console.log(` commands/: ${paiHomePath("commands")} (~/.claude/Commands symlinks here after migrate)`);
|
|
5981
6000
|
});
|
|
6001
|
+
configCmd.command("yaml").description("Convert config.json → config.yaml (and voices.json → voices.yaml), with a short\n # comment above every top-level section and any \"_comment\"/\"_...Note\" JSON-\n workaround key re-attached as a real comment. Verifies the generated YAML\n re-parses to the exact same data before writing anything; the JSON is then\n renamed to <name>.migrated-<YYYY-MM-DD> (never deleted). Every writer keeps\n working unmodified afterwards — see docs/config.md.").option("--dry-run", "Print the plan without writing anything").option("--force", "Regenerate config.yaml (or voices.yaml) even if it already exists").action((opts) => {
|
|
6002
|
+
let hadError = false;
|
|
6003
|
+
const run = (label, fn) => {
|
|
6004
|
+
try {
|
|
6005
|
+
const r = fn();
|
|
6006
|
+
if (r.dryRun) console.log(dim$1(` ${label}: would write ${r.yamlPath}`));
|
|
6007
|
+
else console.log(ok$1(` ${label}: `) + `${r.backupPath} → ${r.yamlPath}`);
|
|
6008
|
+
} catch (e) {
|
|
6009
|
+
hadError = true;
|
|
6010
|
+
console.error(err(` ${label}: `) + (e instanceof MainConfigError ? e.message : String(e instanceof Error ? e.message : e)));
|
|
6011
|
+
}
|
|
6012
|
+
};
|
|
6013
|
+
run("config.yaml", () => migrateMainConfigToYaml(paiConfigFilePath(), {
|
|
6014
|
+
dryRun: opts.dryRun,
|
|
6015
|
+
force: opts.force
|
|
6016
|
+
}));
|
|
6017
|
+
run("voices.yaml", () => migrateVoicesToYaml({
|
|
6018
|
+
dryRun: opts.dryRun,
|
|
6019
|
+
force: opts.force
|
|
6020
|
+
}));
|
|
6021
|
+
if (hadError) process.exitCode = 1;
|
|
6022
|
+
});
|
|
6023
|
+
configCmd.command("list").description("Print the main PAI config as YAML (secrets masked). Defaults to what the file explicitly sets.").option("--all", "Include every default value, not just what the file sets").option("--json", "Print JSON instead of YAML").action((opts) => {
|
|
6024
|
+
const r = listConfigOp({ all: opts.all });
|
|
6025
|
+
console.log(opts.json ? JSON.stringify(r.data, null, 2) : r.yaml.trimEnd());
|
|
6026
|
+
});
|
|
6027
|
+
configCmd.command("get <path>").description("Print one config value (dotted path, e.g. search.recencyBoostDays), masked if it looks like a secret. An object/array subtree prints as YAML; pass --json for JSON.").option("--json", "Print an object/array value as JSON instead of YAML").action((path, opts) => {
|
|
6028
|
+
try {
|
|
6029
|
+
const r = getConfigValueOp(path);
|
|
6030
|
+
if (!r.found) {
|
|
6031
|
+
console.error(err(`Not set: ${path}`));
|
|
6032
|
+
process.exitCode = 1;
|
|
6033
|
+
return;
|
|
6034
|
+
}
|
|
6035
|
+
console.log(formatConfigGetOutput(r.value, { json: opts.json }));
|
|
6036
|
+
} catch (e) {
|
|
6037
|
+
console.error(err(e instanceof MainConfigOpsError ? e.message : String(e instanceof Error ? e.message : e)));
|
|
6038
|
+
process.exitCode = 1;
|
|
6039
|
+
}
|
|
6040
|
+
});
|
|
6041
|
+
configCmd.command("set <path> <value>").description("Set one config value (dotted path). Value parsing: true/false, null, numbers,\n [...] / {...} as JSON, else a string. Creates config.yaml (from config.json,\n if any) on first use, then writes comment-preserving.").option("--force", "Set an unknown top-level key, or a value of a different type than the default").action((path, value, opts) => {
|
|
6042
|
+
try {
|
|
6043
|
+
const r = setConfigValueOp(path, value, { force: opts.force });
|
|
6044
|
+
if (r.yamlCreated) console.log(dim$1(` created ${r.yamlPath}`));
|
|
6045
|
+
console.log(ok$1(`Set ${path} = `) + (typeof r.value === "object" ? JSON.stringify(r.value) : String(r.value)));
|
|
6046
|
+
} catch (e) {
|
|
6047
|
+
console.error(err(e instanceof MainConfigOpsError || e instanceof MainConfigError ? e.message : String(e instanceof Error ? e.message : e)));
|
|
6048
|
+
process.exitCode = 1;
|
|
6049
|
+
}
|
|
6050
|
+
});
|
|
6051
|
+
configCmd.command("unset <path>").description("Remove one config value (dotted path), reverting it to the built-in default").action((path) => {
|
|
6052
|
+
try {
|
|
6053
|
+
const r = unsetConfigValueOp(path);
|
|
6054
|
+
console.log(r.existed ? ok$1(`Unset ${path}`) : dim$1(` ${path} was not set`));
|
|
6055
|
+
} catch (e) {
|
|
6056
|
+
console.error(err(e instanceof MainConfigOpsError ? e.message : String(e instanceof Error ? e.message : e)));
|
|
6057
|
+
process.exitCode = 1;
|
|
6058
|
+
}
|
|
6059
|
+
});
|
|
5982
6060
|
configCmd.command("migrate").description("Move config.json, workers.yaml, whisper-rules.md, advisor-mode.json, session-state/,\nsession-scan-cache.json, queries/, summary-cooldowns.json, work-queue.json,\nkg-backfill-state.json, voices.json, the orphaned federation.db,\n~/.pai/registry.db, ~/.pai/federation.db (live), ~/.pai/backups/,\n~/.pai/obsidian-vault/, ~/.pai/scheduler-state.json, and ~/.claude/Agents/,\n~/.claude/Commands/ into PAI_HOME.\nregistry.db and federation.db each get an extra WAL checkpoint and PRAGMA\nintegrity_check (sqlite3 CLI, if present) on top of the byte-identical copy.\nbackups/ is copied and sha256-verified file-by-file into\nbackups/legacy-pai-backups/ rather than merged in place. Agents/ and Commands/ each\nget a symlink left at the old ~/.claude path once their content has moved, so\nClaude Code still finds them at its fixed harness paths.\nEach file is copied, verified byte-identical, then the old file is renamed to\n<name>.migrated-<YYYYMMDD> (never deleted); directories move their contents, then\nthe emptied old directory is renamed the same way. Idempotent — entries already at\nthe new location, or with nothing old to migrate, are skipped.").option("--dry-run", "Print the plan without writing anything").option("--logs", "Also move ~/.claude/logs/workers into PAI_HOME: an atomic rename plus a\n symlink left at the old path, so any worker still writing there\n keeps landing in the new directory (falls back to copy+verify\n across volumes, refusing while a worker is RUNNING — see\n `pai worker ps` — only in that fallback case)").option("--history", "Also move ~/.claude/History/, agent-sessions.json, session-routing.json\n and history/security/security-events.jsonl into PAI_HOME. These are\n written by hooks on EVERY session's every turn, not just spawned\n workers — refuses while any worker other than the interactive\n session is RUNNING (see `pai worker ps`), but that guard cannot see\n OTHER interactive `claude` sessions on this machine. Confirm none\n are active (`ps aux | grep claude`, `aibroker sessions list`) before\n passing this flag.").action((opts) => {
|
|
5983
6061
|
const dryRun = !!opts.dryRun;
|
|
5984
6062
|
let hadError = false;
|
|
@@ -6578,6 +6656,35 @@ async function cmdMigrate(connectionString) {
|
|
|
6578
6656
|
process.exitCode = result.status ?? 1;
|
|
6579
6657
|
}
|
|
6580
6658
|
}
|
|
6659
|
+
function cmdKeepalive() {
|
|
6660
|
+
const config = loadConfig().sessions.cacheKeepalive;
|
|
6661
|
+
console.log();
|
|
6662
|
+
console.log(bold$1(" Session cache keepalive"));
|
|
6663
|
+
console.log();
|
|
6664
|
+
console.log(config.enabled ? ok$1(` enabled`) : warn$1(` disabled`));
|
|
6665
|
+
console.log(dim$1(` idleMinutes: ${config.idleMinutes} maxBeats: ${config.maxBeats} activeHours: ${config.activeHours} minContextTokens: ${config.minContextTokens} prompt: "${config.prompt}"`));
|
|
6666
|
+
if (!config.enabled) {
|
|
6667
|
+
console.log();
|
|
6668
|
+
console.log(dim$1(` Enable it in ${resolvedMainConfigPath(paiConfigFilePath())}: sessions.cacheKeepalive.enabled: true`));
|
|
6669
|
+
console.log(dim$1(` pai config set sessions.cacheKeepalive.enabled true`));
|
|
6670
|
+
}
|
|
6671
|
+
const state = loadSessionKeepaliveState();
|
|
6672
|
+
const ids = Object.keys(state);
|
|
6673
|
+
console.log();
|
|
6674
|
+
if (ids.length === 0) console.log(dim$1(" No per-session beat counters recorded yet."));
|
|
6675
|
+
else {
|
|
6676
|
+
console.log(bold$1(" Per-session beat counters"));
|
|
6677
|
+
for (const id of ids) {
|
|
6678
|
+
const e = state[id];
|
|
6679
|
+
console.log(dim$1(` ${id}: beats=${e.beats} lastBeatAt=${e.lastBeatAt ?? "never"}`));
|
|
6680
|
+
}
|
|
6681
|
+
}
|
|
6682
|
+
const summary = sessionKeepaliveLedgerSummary();
|
|
6683
|
+
console.log();
|
|
6684
|
+
console.log(bold$1(` Ledger: ${summary.sent} sent, ${summary.skipped} skipped`));
|
|
6685
|
+
for (const line of summary.lastLines) console.log(dim$1(` ${line}`));
|
|
6686
|
+
console.log();
|
|
6687
|
+
}
|
|
6581
6688
|
function cmdLogs(opts) {
|
|
6582
6689
|
const lines = opts.lines ?? "50";
|
|
6583
6690
|
if (!existsSync(DAEMON_LOG)) {
|
|
@@ -6598,8 +6705,8 @@ function cmdLogs(opts) {
|
|
|
6598
6705
|
}
|
|
6599
6706
|
function registerDaemonCommands(daemonCmd) {
|
|
6600
6707
|
daemonCmd.command("serve").description("Start the PAI daemon in the foreground").action(async () => {
|
|
6601
|
-
const { serve } = await import("./daemon-
|
|
6602
|
-
const { loadConfig: lc, ensureConfigDir } = await import("./config-
|
|
6708
|
+
const { serve } = await import("./daemon-Vf7pHnjR.mjs");
|
|
6709
|
+
const { loadConfig: lc, ensureConfigDir } = await import("./config-RjeVGJ7K.mjs");
|
|
6603
6710
|
ensureConfigDir();
|
|
6604
6711
|
await serve(lc());
|
|
6605
6712
|
});
|
|
@@ -6618,6 +6725,9 @@ function registerDaemonCommands(daemonCmd) {
|
|
|
6618
6725
|
daemonCmd.command("uninstall").description("Remove the launchd service and revert to direct MCP").action(() => {
|
|
6619
6726
|
cmdUninstall();
|
|
6620
6727
|
});
|
|
6728
|
+
daemonCmd.command("keepalive").description("Show interactive-session cache keepalive config, per-session beat counters, and recent ledger lines").action(() => {
|
|
6729
|
+
cmdKeepalive();
|
|
6730
|
+
});
|
|
6621
6731
|
daemonCmd.command("logs").description(`Tail the daemon log (${DAEMON_LOG})`).option("-n, --lines <n>", "Number of lines to show", "50").option("-f, --follow", "Follow log output (like tail -f)").action((opts) => {
|
|
6622
6732
|
cmdLogs(opts);
|
|
6623
6733
|
});
|
|
@@ -6959,10 +7069,10 @@ async function promptYesNo(rl, question, defaultYes = true) {
|
|
|
6959
7069
|
* current command happened to be setting.
|
|
6960
7070
|
*/
|
|
6961
7071
|
function readConfigRaw() {
|
|
6962
|
-
return
|
|
7072
|
+
return readMainConfigRaw();
|
|
6963
7073
|
}
|
|
6964
7074
|
function writeConfigRaw(data) {
|
|
6965
|
-
|
|
7075
|
+
writeMainConfigRaw(data);
|
|
6966
7076
|
}
|
|
6967
7077
|
function mergeConfig(updates) {
|
|
6968
7078
|
const current = readConfigRaw();
|
|
@@ -9202,7 +9312,7 @@ async function cmdExplore(note, opts) {
|
|
|
9202
9312
|
const depth = parseInt(opts.depth ?? "3", 10);
|
|
9203
9313
|
const direction = opts.direction ?? "both";
|
|
9204
9314
|
const mode = opts.mode ?? "all";
|
|
9205
|
-
const { zettelExplore } = await import("./zettelkasten-
|
|
9315
|
+
const { zettelExplore } = await import("./zettelkasten-DezbXghl.mjs");
|
|
9206
9316
|
const result = zettelExplore(getFedDb(), {
|
|
9207
9317
|
startNote: note,
|
|
9208
9318
|
depth,
|
|
@@ -9267,7 +9377,7 @@ async function cmdHealth(opts) {
|
|
|
9267
9377
|
const projectPath = opts.project;
|
|
9268
9378
|
const recentDays = parseInt(opts.days ?? "30", 10);
|
|
9269
9379
|
const includeTypes = opts.include ? opts.include.split(",").map((s) => s.trim()) : void 0;
|
|
9270
|
-
const { zettelHealth } = await import("./zettelkasten-
|
|
9380
|
+
const { zettelHealth } = await import("./zettelkasten-DezbXghl.mjs");
|
|
9271
9381
|
const result = zettelHealth(getFedDb(), {
|
|
9272
9382
|
scope,
|
|
9273
9383
|
projectPath,
|
|
@@ -9338,7 +9448,7 @@ async function cmdSurprise(note, opts) {
|
|
|
9338
9448
|
const limit = parseInt(opts.limit ?? "10", 10);
|
|
9339
9449
|
const minSimilarity = parseFloat(opts.minSimilarity ?? "0.3");
|
|
9340
9450
|
const minGraphDistance = parseInt(opts.minDistance ?? "3", 10);
|
|
9341
|
-
const { zettelSurprise } = await import("./zettelkasten-
|
|
9451
|
+
const { zettelSurprise } = await import("./zettelkasten-DezbXghl.mjs");
|
|
9342
9452
|
const db = getFedDb();
|
|
9343
9453
|
console.log();
|
|
9344
9454
|
console.log(header(" PAI Zettel Surprise"));
|
|
@@ -9393,7 +9503,7 @@ async function cmdSuggest(note, opts) {
|
|
|
9393
9503
|
const vaultProjectId = parseInt(opts.vaultProjectId, 10);
|
|
9394
9504
|
const limit = parseInt(opts.limit ?? "5", 10);
|
|
9395
9505
|
const excludeLinked = opts.excludeLinked !== false;
|
|
9396
|
-
const { zettelSuggest } = await import("./zettelkasten-
|
|
9506
|
+
const { zettelSuggest } = await import("./zettelkasten-DezbXghl.mjs");
|
|
9397
9507
|
const db = getFedDb();
|
|
9398
9508
|
console.log();
|
|
9399
9509
|
console.log(header(" PAI Zettel Suggest"));
|
|
@@ -9446,7 +9556,7 @@ async function cmdConverse(question, opts) {
|
|
|
9446
9556
|
const vaultProjectId = parseInt(opts.vaultProjectId, 10);
|
|
9447
9557
|
const depth = parseInt(opts.depth ?? "2", 10);
|
|
9448
9558
|
const limit = parseInt(opts.limit ?? "15", 10);
|
|
9449
|
-
const { zettelConverse } = await import("./zettelkasten-
|
|
9559
|
+
const { zettelConverse } = await import("./zettelkasten-DezbXghl.mjs");
|
|
9450
9560
|
const db = getFedDb();
|
|
9451
9561
|
console.log();
|
|
9452
9562
|
console.log(header(" PAI Zettel Converse"));
|
|
@@ -9509,7 +9619,7 @@ async function cmdThemes(opts) {
|
|
|
9509
9619
|
const minClusterSize = parseInt(opts.minSize ?? "3", 10);
|
|
9510
9620
|
const maxThemes = parseInt(opts.maxThemes ?? "10", 10);
|
|
9511
9621
|
const similarityThreshold = parseFloat(opts.threshold ?? "0.65");
|
|
9512
|
-
const { zettelThemes } = await import("./zettelkasten-
|
|
9622
|
+
const { zettelThemes } = await import("./zettelkasten-DezbXghl.mjs");
|
|
9513
9623
|
const db = getFedDb();
|
|
9514
9624
|
console.log();
|
|
9515
9625
|
console.log(header(" PAI Zettel Themes"));
|
|
@@ -10028,200 +10138,6 @@ function auditHooks(cwd) {
|
|
|
10028
10138
|
};
|
|
10029
10139
|
}
|
|
10030
10140
|
|
|
10031
|
-
//#endregion
|
|
10032
|
-
//#region src/audit/session-usage.ts
|
|
10033
|
-
/**
|
|
10034
|
-
* session-usage.ts — parse the usage numbers out of a Claude Code session
|
|
10035
|
-
* (or subagent, or pai-worker event-mirror) JSONL transcript.
|
|
10036
|
-
*
|
|
10037
|
-
* All three log shapes share the same assistant-message envelope
|
|
10038
|
-
* ({ type: "assistant", message: { id, model, usage } }), so one parser
|
|
10039
|
-
* covers `pai audit tokens session` and the per-log readings feeding
|
|
10040
|
-
* `pai audit tokens spawn`.
|
|
10041
|
-
*
|
|
10042
|
-
* Streaming writes one JSONL line per content block of the same logical
|
|
10043
|
-
* turn, repeating message.id and usage each time — summing every line would
|
|
10044
|
-
* multiply usage by the block count. Keeping only the first line seen per
|
|
10045
|
-
* message.id is what the reference script (and this parser) count instead.
|
|
10046
|
-
*/
|
|
10047
|
-
const USAGE_KEYS = [
|
|
10048
|
-
"cache_read_input_tokens",
|
|
10049
|
-
"cache_creation_input_tokens",
|
|
10050
|
-
"input_tokens",
|
|
10051
|
-
"output_tokens"
|
|
10052
|
-
];
|
|
10053
|
-
function emptyTotals() {
|
|
10054
|
-
return {
|
|
10055
|
-
cache_read_input_tokens: 0,
|
|
10056
|
-
cache_creation_input_tokens: 0,
|
|
10057
|
-
input_tokens: 0,
|
|
10058
|
-
output_tokens: 0
|
|
10059
|
-
};
|
|
10060
|
-
}
|
|
10061
|
-
/**
|
|
10062
|
-
* `type:"system"` `subtype:"compact_boundary"` lines mark a compaction.
|
|
10063
|
-
* turnIndex is the count of assistant turns already folded when it fired,
|
|
10064
|
-
* so it lines up with the turn numbering the text/JSON report prints.
|
|
10065
|
-
*/
|
|
10066
|
-
function isCompactBoundary(line) {
|
|
10067
|
-
return line.type === "system" && line.subtype === "compact_boundary";
|
|
10068
|
-
}
|
|
10069
|
-
function parseCompactionEvent(line, turnIndex) {
|
|
10070
|
-
const meta = line.compactMetadata;
|
|
10071
|
-
if (!meta || typeof meta.preTokens !== "number") return null;
|
|
10072
|
-
return {
|
|
10073
|
-
trigger: meta.trigger ?? "unknown",
|
|
10074
|
-
preTokens: meta.preTokens,
|
|
10075
|
-
turnIndex
|
|
10076
|
-
};
|
|
10077
|
-
}
|
|
10078
|
-
/**
|
|
10079
|
-
* `type:"system"` `subtype:"model_refusal_fallback"` lines mark an
|
|
10080
|
-
* automatic model switch fired by an API safety refusal (e.g. "cyber"
|
|
10081
|
-
* category), not a user or config choice — worth flagging separately since
|
|
10082
|
-
* it can rebuild the whole prompt cache mid-session.
|
|
10083
|
-
*/
|
|
10084
|
-
function isModelFallback(line) {
|
|
10085
|
-
return line.type === "system" && line.subtype === "model_refusal_fallback";
|
|
10086
|
-
}
|
|
10087
|
-
function parseFallbackEvent(line, turnIndex) {
|
|
10088
|
-
return {
|
|
10089
|
-
turnIndex,
|
|
10090
|
-
from: line.originalModel ?? "unknown",
|
|
10091
|
-
to: line.fallbackModel ?? "unknown",
|
|
10092
|
-
category: line.apiRefusalCategory ?? "unknown",
|
|
10093
|
-
scope: line.scope ?? "unknown"
|
|
10094
|
-
};
|
|
10095
|
-
}
|
|
10096
|
-
/**
|
|
10097
|
-
* Fold one already-parsed JSONL line into a report being accumulated.
|
|
10098
|
-
* Exported separately so both the streaming file reader below and tests
|
|
10099
|
-
* (which build fixtures as arrays of objects, not files) share one path.
|
|
10100
|
-
* Returns the turn's context value when a new turn was counted, else null
|
|
10101
|
-
* (non-assistant line, no usage, or a duplicate message.id already seen).
|
|
10102
|
-
*/
|
|
10103
|
-
function foldAssistantLine(report, seenIds, line, threshold) {
|
|
10104
|
-
if (line.type !== "assistant") return null;
|
|
10105
|
-
const message = line.message;
|
|
10106
|
-
const usage = message?.usage;
|
|
10107
|
-
if (!usage) return null;
|
|
10108
|
-
const id = message?.id ?? line.uuid;
|
|
10109
|
-
if (id) {
|
|
10110
|
-
if (seenIds.has(id)) return null;
|
|
10111
|
-
seenIds.add(id);
|
|
10112
|
-
}
|
|
10113
|
-
report.turns++;
|
|
10114
|
-
for (const key of USAGE_KEYS) {
|
|
10115
|
-
const v = usage[key];
|
|
10116
|
-
if (typeof v === "number") report.totals[key] += v;
|
|
10117
|
-
}
|
|
10118
|
-
const context = (Number(usage.cache_read_input_tokens) || 0) + (Number(usage.cache_creation_input_tokens) || 0) + (Number(usage.input_tokens) || 0);
|
|
10119
|
-
if (report.firstTurnContext === null) {
|
|
10120
|
-
report.firstTurnContext = context;
|
|
10121
|
-
report.firstTurnAt = line.timestamp ?? null;
|
|
10122
|
-
}
|
|
10123
|
-
report.lastTurnContext = context;
|
|
10124
|
-
report.maxContext = report.maxContext === null ? context : Math.max(report.maxContext, context);
|
|
10125
|
-
if (context > threshold) report.turnsAboveThreshold++;
|
|
10126
|
-
if ((Number(usage.cache_creation_input_tokens) || 0) > 2e4) report.cacheRebuildTurns++;
|
|
10127
|
-
const model = message?.model ?? "unknown";
|
|
10128
|
-
report.models[model] = (report.models[model] ?? 0) + 1;
|
|
10129
|
-
if (report.lastModel !== null && report.lastModel !== model) report.modelSwitches.push({
|
|
10130
|
-
turnIndex: report.turns,
|
|
10131
|
-
from: report.lastModel,
|
|
10132
|
-
to: model,
|
|
10133
|
-
cacheRead: Number(usage.cache_read_input_tokens) || 0,
|
|
10134
|
-
cacheCreation: Number(usage.cache_creation_input_tokens) || 0
|
|
10135
|
-
});
|
|
10136
|
-
report.lastModel = model;
|
|
10137
|
-
const split = usage.cache_creation;
|
|
10138
|
-
if (split) {
|
|
10139
|
-
report.cacheCreationSplit.ephemeral5m += Number(split.ephemeral_5m_input_tokens) || 0;
|
|
10140
|
-
report.cacheCreationSplit.ephemeral1h += Number(split.ephemeral_1h_input_tokens) || 0;
|
|
10141
|
-
}
|
|
10142
|
-
return context;
|
|
10143
|
-
}
|
|
10144
|
-
/**
|
|
10145
|
-
* True for a real human-authored `type:"user"` prompt line: content is a
|
|
10146
|
-
* plain string, or a content array with no `tool_result` block. Claude Code
|
|
10147
|
-
* encodes tool results as `type:"user"` messages whose content array is
|
|
10148
|
-
* entirely (or partly) tool_result blocks — those must not count as prompts.
|
|
10149
|
-
*/
|
|
10150
|
-
function isRealUserPrompt(line) {
|
|
10151
|
-
if (line.type !== "user") return false;
|
|
10152
|
-
if (line.isMeta) return false;
|
|
10153
|
-
if (line.origin?.kind && line.origin.kind !== "human") return false;
|
|
10154
|
-
const content = line.message?.content;
|
|
10155
|
-
if (typeof content === "string") return true;
|
|
10156
|
-
if (Array.isArray(content)) return !content.some((block) => block?.type === "tool_result");
|
|
10157
|
-
return false;
|
|
10158
|
-
}
|
|
10159
|
-
/** Parse a session/subagent/worker-event JSONL file into a usage report. */
|
|
10160
|
-
async function parseSessionUsage(path, threshold = 2e5) {
|
|
10161
|
-
const report = {
|
|
10162
|
-
path,
|
|
10163
|
-
sizeBytes: existsSync(path) ? readFileSync(path).byteLength : 0,
|
|
10164
|
-
turns: 0,
|
|
10165
|
-
totals: emptyTotals(),
|
|
10166
|
-
models: {},
|
|
10167
|
-
firstTurnContext: null,
|
|
10168
|
-
lastTurnContext: null,
|
|
10169
|
-
cacheCreationSplit: {
|
|
10170
|
-
ephemeral5m: 0,
|
|
10171
|
-
ephemeral1h: 0
|
|
10172
|
-
},
|
|
10173
|
-
avgContext: null,
|
|
10174
|
-
maxContext: null,
|
|
10175
|
-
turnsAboveThreshold: 0,
|
|
10176
|
-
cacheRebuildTurns: 0,
|
|
10177
|
-
userPrompts: 0,
|
|
10178
|
-
promptExposure: 0,
|
|
10179
|
-
compactions: [],
|
|
10180
|
-
firstTurnAt: null,
|
|
10181
|
-
lastModel: null,
|
|
10182
|
-
modelSwitches: [],
|
|
10183
|
-
fallbacks: []
|
|
10184
|
-
};
|
|
10185
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
10186
|
-
let contextSum = 0;
|
|
10187
|
-
const rl = createInterface({
|
|
10188
|
-
input: createReadStream(path, "utf8"),
|
|
10189
|
-
crlfDelay: Infinity
|
|
10190
|
-
});
|
|
10191
|
-
for await (const raw of rl) {
|
|
10192
|
-
if (!raw.trim()) continue;
|
|
10193
|
-
let obj;
|
|
10194
|
-
try {
|
|
10195
|
-
obj = JSON.parse(raw);
|
|
10196
|
-
} catch {
|
|
10197
|
-
continue;
|
|
10198
|
-
}
|
|
10199
|
-
if (obj.type === "user") {
|
|
10200
|
-
if (isRealUserPrompt(obj)) report.userPrompts++;
|
|
10201
|
-
continue;
|
|
10202
|
-
}
|
|
10203
|
-
if (isCompactBoundary(obj)) {
|
|
10204
|
-
const event = parseCompactionEvent(obj, report.turns);
|
|
10205
|
-
if (event) report.compactions.push(event);
|
|
10206
|
-
continue;
|
|
10207
|
-
}
|
|
10208
|
-
if (isModelFallback(obj)) {
|
|
10209
|
-
report.fallbacks.push(parseFallbackEvent(obj, report.turns));
|
|
10210
|
-
continue;
|
|
10211
|
-
}
|
|
10212
|
-
const context = foldAssistantLine(report, seenIds, obj, threshold);
|
|
10213
|
-
if (context !== null) {
|
|
10214
|
-
contextSum += context;
|
|
10215
|
-
report.promptExposure += report.userPrompts;
|
|
10216
|
-
}
|
|
10217
|
-
}
|
|
10218
|
-
report.avgContext = report.turns > 0 ? Math.round(contextSum / report.turns) : null;
|
|
10219
|
-
return report;
|
|
10220
|
-
}
|
|
10221
|
-
function totalUsageTokens(totals) {
|
|
10222
|
-
return totals.cache_read_input_tokens + totals.cache_creation_input_tokens + totals.input_tokens + totals.output_tokens;
|
|
10223
|
-
}
|
|
10224
|
-
|
|
10225
10141
|
//#endregion
|
|
10226
10142
|
//#region src/audit/first-turn.ts
|
|
10227
10143
|
/**
|
|
@@ -10534,7 +10450,8 @@ function newestSessionLog() {
|
|
|
10534
10450
|
async function auditSession(path, threshold) {
|
|
10535
10451
|
const target = path ?? newestSessionLog();
|
|
10536
10452
|
if (!target) return null;
|
|
10537
|
-
const
|
|
10453
|
+
const keepaliveWord = loadConfig().sessions.cacheKeepalive.prompt;
|
|
10454
|
+
const report = await parseSessionUsage(target, threshold, keepaliveWord);
|
|
10538
10455
|
const total = totalUsageTokens(report.totals) || 1;
|
|
10539
10456
|
const percentages = {};
|
|
10540
10457
|
for (const [key, value] of Object.entries(report.totals)) percentages[key] = 100 * value / total;
|
|
@@ -12086,6 +12003,7 @@ function printSession(report) {
|
|
|
12086
12003
|
console.log(`safeguard fallbacks: ${report.fallbacks.length}`);
|
|
12087
12004
|
for (const f of report.fallbacks) console.log(dim$1(` ${f.from} -> ${f.to} before turn ${f.turnIndex}: ${f.category} (${f.scope})`));
|
|
12088
12005
|
}
|
|
12006
|
+
console.log(dim$1(`idle gaps > 60min: ${report.idleGapsOver60min}, keepalive beats: ${report.keepaliveBeats ?? "n/a"}`));
|
|
12089
12007
|
}
|
|
12090
12008
|
function printTurnBreakdown(tb) {
|
|
12091
12009
|
console.log(header(`turn ${tb.turn} attribution (live lineage, cl100k)`));
|
|
@@ -15411,8 +15329,8 @@ function cmdActive(db, opts) {
|
|
|
15411
15329
|
async function cmdAutoRoute(opts) {
|
|
15412
15330
|
const { autoRoute, formatAutoRoute, formatAutoRouteJson } = await import("./auto-route-CcD0OWWB.mjs");
|
|
15413
15331
|
const { openRegistry } = await import("./db-DbY2O8QO.mjs");
|
|
15414
|
-
const { createStorageBackend } = await import("./factory-
|
|
15415
|
-
const { loadConfig } = await import("./config-
|
|
15332
|
+
const { createStorageBackend } = await import("./factory-Ba8j9K7q.mjs");
|
|
15333
|
+
const { loadConfig } = await import("./config-RjeVGJ7K.mjs");
|
|
15416
15334
|
const config = loadConfig();
|
|
15417
15335
|
const registryDb = openRegistry();
|
|
15418
15336
|
const federation = await createStorageBackend(config);
|
|
@@ -17442,7 +17360,7 @@ function deriveLabel(prompt, klass) {
|
|
|
17442
17360
|
|
|
17443
17361
|
//#endregion
|
|
17444
17362
|
//#region src/cli/commands/worker/providers.ts
|
|
17445
|
-
function fail$
|
|
17363
|
+
function fail$4(e) {
|
|
17446
17364
|
const msg = e instanceof Error ? e.message : String(e);
|
|
17447
17365
|
console.error(err("pai worker: ") + msg);
|
|
17448
17366
|
process.exitCode = 1;
|
|
@@ -17505,12 +17423,12 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17505
17423
|
if (legacyNotice) console.log(dim$1(` ${legacyNotice}`));
|
|
17506
17424
|
console.log();
|
|
17507
17425
|
});
|
|
17508
|
-
providersCmd.command("add <name>").description("Add a provider; the first one also turns workers on and seeds classes.\nExample: pai worker providers add glm --url https://…/anthropic \\\n --key sk-… --model glm-5.3 --fast-model glm-5.3-flash\n--key writes the token inline as `key:` in workers.yaml (quoted, file kept 0600);\n--key-file writes only a path to a 0600 file holding it — use one or the other.\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)").option("--url <url>", "Alias of --base-url").requiredOption("--model <model>", "Default model id for this provider").option("--key <token>", "API token, written inline as `key:` (quoted); use instead of --key-file").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
|
|
17426
|
+
providersCmd.command("add <name>").description("Add a provider; the first one also turns workers on and seeds classes.\nExample: pai worker providers add glm --url https://…/anthropic \\\n --key sk-… --model glm-5.3 --fast-model glm-5.3-flash\n--key writes the token inline as `key:` in workers.yaml (quoted, file kept 0600);\n--key-file writes only a path to a 0600 file holding it — use one or the other.\nOpenAI-protocol: --protocol openai --upstream-url https://…/v1 (runs via the PAI proxy).\nCodex (ChatGPT plan): --engine codex — runs through the Codex CLI.\nImage generation: --engine image — `pai worker run --capability image` then POSTs\nstraight to {url}/images/generations instead of spawning claude.").option("--base-url <url>", "Anthropic-compatible API base URL (required unless --protocol openai)").option("--url <url>", "Alias of --base-url").requiredOption("--model <model>", "Default model id for this provider").option("--key <token>", "API token, written inline as `key:` (quoted); use instead of --key-file").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), codex, or image — codex runs `codex exec --json`, image POSTs {url}/images/generations").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)").option("--cost-tier <1-5>", "Cost tier 1 (cheapest) … 5 (most expensive; default 3)", parseCostTier).option("--tags <tags>", "Capability tags, comma-separated (from: " + PROVIDER_TAGS.join(", ") + ")", (v, acc) => [...acc, v], []).action((name, opts) => {
|
|
17509
17427
|
try {
|
|
17510
17428
|
const protocol = opts.protocol;
|
|
17511
17429
|
const engine = opts.engine;
|
|
17512
17430
|
if (protocol && protocol !== "anthropic" && protocol !== "openai") throw new WorkersConfigError(`--protocol must be anthropic or openai, got "${protocol}"`);
|
|
17513
|
-
if (engine && engine !== "claude" && engine !== "codex") throw new WorkersConfigError(`--engine must be claude or
|
|
17431
|
+
if (engine && engine !== "claude" && engine !== "codex" && engine !== "image") throw new WorkersConfigError(`--engine must be claude, codex, or image, got "${engine}"`);
|
|
17514
17432
|
const baseUrl = opts.baseUrl ?? opts.url;
|
|
17515
17433
|
if (protocol !== "openai" && !baseUrl) throw new WorkersConfigError("--base-url (or --url) is required (only --protocol openai goes without it)");
|
|
17516
17434
|
if (opts.key && opts.keyFile) throw new WorkersConfigError("--key and --key-file are alternatives — pass one, not both");
|
|
@@ -17537,7 +17455,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17537
17455
|
if (workers.active === name) console.log(` active: ${name} — workers ${workers.enabled ? "on" : "off"}`);
|
|
17538
17456
|
for (const line of describeProviders(workers)) console.log(dim$1(` ${line}`));
|
|
17539
17457
|
} catch (e) {
|
|
17540
|
-
fail$
|
|
17458
|
+
fail$4(e);
|
|
17541
17459
|
}
|
|
17542
17460
|
});
|
|
17543
17461
|
providersCmd.command("update <name>").description("Change cost tier and tags of a provider (routing constraints use these)").option("--cost-tier <1-5>", "Cost tier 1 (cheapest) … 5 (most expensive)", parseCostTier).option("--tags <tags>", "Capability tags, comma-separated (from: " + PROVIDER_TAGS.join(", ") + "); --tags '' clears", (v, acc) => [...acc, v], []).action((name, opts) => {
|
|
@@ -17552,7 +17470,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17552
17470
|
const { workers } = readWorkersSection();
|
|
17553
17471
|
for (const line of describeProviders(workers)) console.log(dim$1(` ${line}`));
|
|
17554
17472
|
} catch (e) {
|
|
17555
|
-
fail$
|
|
17473
|
+
fail$4(e);
|
|
17556
17474
|
}
|
|
17557
17475
|
});
|
|
17558
17476
|
providersCmd.command("remove <name>").description("Remove a provider and any classes pointing at it").action((name) => {
|
|
@@ -17560,7 +17478,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17560
17478
|
removeProvider(name);
|
|
17561
17479
|
console.log(ok$1(`provider ${name} removed`));
|
|
17562
17480
|
} catch (e) {
|
|
17563
|
-
fail$
|
|
17481
|
+
fail$4(e);
|
|
17564
17482
|
}
|
|
17565
17483
|
});
|
|
17566
17484
|
providersCmd.command("use <name>").description("Make this provider the active one for runs without --provider/--class").action((name) => {
|
|
@@ -17568,7 +17486,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17568
17486
|
useProvider(name);
|
|
17569
17487
|
console.log(ok$1(`active provider: ${name}`));
|
|
17570
17488
|
} catch (e) {
|
|
17571
|
-
fail$
|
|
17489
|
+
fail$4(e);
|
|
17572
17490
|
}
|
|
17573
17491
|
});
|
|
17574
17492
|
providersCmd.command("enable <name>").description("Enable a provider (also clears its cooldown)").action((name) => {
|
|
@@ -17576,7 +17494,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17576
17494
|
setProviderEnabled(name, true);
|
|
17577
17495
|
console.log(ok$1(`provider ${name} enabled`));
|
|
17578
17496
|
} catch (e) {
|
|
17579
|
-
fail$
|
|
17497
|
+
fail$4(e);
|
|
17580
17498
|
}
|
|
17581
17499
|
});
|
|
17582
17500
|
providersCmd.command("disable <name>").description("Disable a provider (auto-routing skips it; --provider still works)").action((name) => {
|
|
@@ -17584,7 +17502,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17584
17502
|
setProviderEnabled(name, false);
|
|
17585
17503
|
console.log(ok$1(`provider ${name} disabled`));
|
|
17586
17504
|
} catch (e) {
|
|
17587
|
-
fail$
|
|
17505
|
+
fail$4(e);
|
|
17588
17506
|
}
|
|
17589
17507
|
});
|
|
17590
17508
|
providersCmd.command("test [name]").description("One-word pong probe through a provider (default: the active one)").action(async (name) => {
|
|
@@ -17604,7 +17522,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17604
17522
|
console.log(` reply: ${r.result.slice(0, 200)}`);
|
|
17605
17523
|
process.exitCode = r.ok ? 0 : 1;
|
|
17606
17524
|
} catch (e) {
|
|
17607
|
-
fail$
|
|
17525
|
+
fail$4(e);
|
|
17608
17526
|
}
|
|
17609
17527
|
});
|
|
17610
17528
|
}
|
|
@@ -17625,7 +17543,7 @@ function registerClassSubcommands(classesCmd) {
|
|
|
17625
17543
|
setClass(cls, built);
|
|
17626
17544
|
console.log(ok$1(`class ${cls} → ${classTargetText(built)}`));
|
|
17627
17545
|
} catch (e) {
|
|
17628
|
-
fail$
|
|
17546
|
+
fail$4(e);
|
|
17629
17547
|
}
|
|
17630
17548
|
});
|
|
17631
17549
|
classesCmd.command("unset <class>").description("Remove a class (runs then use the active provider)").action((cls) => {
|
|
@@ -17633,7 +17551,7 @@ function registerClassSubcommands(classesCmd) {
|
|
|
17633
17551
|
unsetClass(cls);
|
|
17634
17552
|
console.log(ok$1(`class ${cls} removed`));
|
|
17635
17553
|
} catch (e) {
|
|
17636
|
-
fail$
|
|
17554
|
+
fail$4(e);
|
|
17637
17555
|
}
|
|
17638
17556
|
});
|
|
17639
17557
|
}
|
|
@@ -17645,13 +17563,13 @@ function registerWorkerClassCommands(workerCmd) {
|
|
|
17645
17563
|
|
|
17646
17564
|
//#endregion
|
|
17647
17565
|
//#region src/cli/commands/worker/model.ts
|
|
17648
|
-
function fail$
|
|
17566
|
+
function fail$3(e) {
|
|
17649
17567
|
const msg = e instanceof Error ? e.message : String(e);
|
|
17650
17568
|
console.error(err("pai worker: ") + msg);
|
|
17651
17569
|
process.exitCode = 1;
|
|
17652
17570
|
}
|
|
17653
17571
|
function registerWorkerModelCommand(workerCmd) {
|
|
17654
|
-
workerCmd.command("model [what] [model]").description("Model ids per provider: no args lists them,\n`model <model-id>` sets the active provider's default model (back-compat),\n`model <capability>` shows one capability, `model <capability> <model-id>` sets it.\
|
|
17572
|
+
workerCmd.command("model [what] [model]").description("Model ids per provider: no args lists them,\n`model <model-id>` sets the active provider's default model (back-compat),\n`model <capability>` shows one capability, `model <capability> <model-id>` sets it.\nCapability names are open (default, fast, image, … — any ^[a-z][a-z0-9-]*$ name);\n`pai worker capability` picks which provider serves one across the whole config.\n--provider targets another provider.").option("--provider <name>", "Provider to read or change (default: the active one)").action((what, model, opts) => {
|
|
17655
17573
|
try {
|
|
17656
17574
|
const { workers } = readWorkersSection();
|
|
17657
17575
|
if (what === void 0) {
|
|
@@ -17676,6 +17594,39 @@ function registerWorkerModelCommand(workerCmd) {
|
|
|
17676
17594
|
console.log(ok$1(`${name} ${capability} model: ${p.models[capability]}`));
|
|
17677
17595
|
console.log(dim$1(` ${modelPrefsText(p)}`));
|
|
17678
17596
|
}
|
|
17597
|
+
} catch (e) {
|
|
17598
|
+
fail$3(e);
|
|
17599
|
+
}
|
|
17600
|
+
});
|
|
17601
|
+
}
|
|
17602
|
+
|
|
17603
|
+
//#endregion
|
|
17604
|
+
//#region src/cli/commands/worker/capability.ts
|
|
17605
|
+
function fail$2(e) {
|
|
17606
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
17607
|
+
console.error(err("pai worker: ") + msg);
|
|
17608
|
+
process.exitCode = 1;
|
|
17609
|
+
}
|
|
17610
|
+
function registerWorkerCapabilityCommand(workerCmd) {
|
|
17611
|
+
workerCmd.command("capability [name] [providers]").description("Which provider(s) serve a capability (e.g. image), independent of any one\nprovider's own model table: no args lists every preference and what it\nresolves to; `capability <name> <provider>[,<provider>…]` sets the\npreference list (first usable one wins); `capability <name> --unset`\nremoves it. An `engine: image` provider preferred for \"image\" runs\n`pai worker run --capability image` directly against its images API.").option("--unset", "Remove the preference for <name>").option("--json", "Machine-readable output").action((name, providers, opts) => {
|
|
17612
|
+
try {
|
|
17613
|
+
if (name === void 0) {
|
|
17614
|
+
const { workers } = readWorkersSection();
|
|
17615
|
+
const lines = describeCapabilities(workers);
|
|
17616
|
+
if (opts.json) console.log(JSON.stringify(lines));
|
|
17617
|
+
else for (const line of lines) console.log(` ${line}`);
|
|
17618
|
+
return;
|
|
17619
|
+
}
|
|
17620
|
+
if (opts.unset) {
|
|
17621
|
+
unsetCapabilityPreference(name);
|
|
17622
|
+
console.log(ok$1(`removed the capability preference for "${name}"`));
|
|
17623
|
+
return;
|
|
17624
|
+
}
|
|
17625
|
+
if (providers === void 0) throw new Error(`usage: pai worker capability ${name} <provider>[,<provider>…] (or --unset to remove it)`);
|
|
17626
|
+
const workers = setCapabilityPreference(name, providers.split(",").map((p) => p.trim()));
|
|
17627
|
+
const line = describeCapabilities(workers).find((l) => l.startsWith(`${name}:`));
|
|
17628
|
+
console.log(ok$1(`${name}: [${workers.capabilities[name]?.join(", ")}]`));
|
|
17629
|
+
if (line) console.log(` -> ${line.split(" -> ")[1]}`);
|
|
17679
17630
|
} catch (e) {
|
|
17680
17631
|
fail$2(e);
|
|
17681
17632
|
}
|
|
@@ -17873,7 +17824,7 @@ function fail(e) {
|
|
|
17873
17824
|
process.exitCode = 1;
|
|
17874
17825
|
}
|
|
17875
17826
|
function registerWorkerCommands(workerCmd) {
|
|
17876
|
-
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.\n--label \"<goal>\" is optional — it is the row shown in ps / follow /\nthe status line; when absent it is derived from the prompt's first line\n(--chain/--agent derive their own instead).\n--spec <file> (or --spec -) reads the prompt from a file/stdin instead of\nan inline -p '<prompt>', which breaks on shell quoting; mutually\nexclusive with -p.\nGrant MCP tools by naming mcp__server__tool in --allowedTools (the server loads automatically);\n--chain draft,implement[,review] runs a spec-first pipeline;\n--agent <name> runs an agent definition from ~/.claude/agents.").allowUnknownOption(true).option("--provider <name>", "Provider to run on (default: active, else routing order)").option("--class <name>", "Use the provider of this class (draft, implement, review, research, spotcheck, simple, complex, image)").option("--role <name>", "Alias of --class (roles were renamed to classes)").option("--chain <stages>", "Comma-separated stage classes, e.g. draft,implement or draft,implement,review").option("--agent <name>", "Run the agent definition ~/.claude/agents/<name>.md on a worker").option("--model <model>", "Override the model for this run. Headless (-p) workers default to the --class model; an interactive launch (no -p) with no --model uses the harness default model from settings.json.").option("--label <text>", "Short task label shown in ps / follow / status line (default: first line of the prompt)").option("--spec <path>", "Read the prompt from this file (or - for stdin) instead of -p; mutually exclusive with -p").option("--cwd <dir>", "Directory the worker runs in (default: this process's cwd)").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").option("--worktree", "Run in a git worktree on branch worker/<id> (default for implement/complex/plan in a git repo)").option("--no-worktree", "Run in place, no worktree").option("--print-cmd", "Print the assembled claude argv as JSON and exit, without spawning (audit tool)").option("--report <format>", "Final-report contract/parser: json or ag2 (default: ag2, or PAI_WORKER_REPORT)").option("--no-report-retry", "Skip the one bounded re-ask when the final AG2 message fails validation").argument("[args...]", "claude arguments, e.g. -p '<task>' --allowedTools 'Read,Edit,Bash'").action(async (args, opts) => {
|
|
17827
|
+
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.\n--label \"<goal>\" is optional — it is the row shown in ps / follow /\nthe status line; when absent it is derived from the prompt's first line\n(--chain/--agent derive their own instead).\n--spec <file> (or --spec -) reads the prompt from a file/stdin instead of\nan inline -p '<prompt>', which breaks on shell quoting; mutually\nexclusive with -p.\nGrant MCP tools by naming mcp__server__tool in --allowedTools (the server loads automatically);\n--chain draft,implement[,review] runs a spec-first pipeline;\n--agent <name> runs an agent definition from ~/.claude/agents.").allowUnknownOption(true).option("--provider <name>", "Provider to run on (default: active, else routing order)").option("--class <name>", "Use the provider of this class (draft, implement, review, research, spotcheck, simple, complex, image)").option("--role <name>", "Alias of --class (roles were renamed to classes)").option("--capability <name>", "Run on whichever configured provider serves this capability (see `pai worker capability`), not the active provider or --class's own one — e.g. --capability image runs an `engine: image` provider directly, POSTing a prompt and writing the PNG instead of spawning claude").option("--for <name>", "Alias of --capability").option("--out <file>", "Where an image-capability run writes its PNG (default: <logDir>/<worker-id>.png)").option("--size <WxH>", "Image size for an image-capability run, e.g. 1024x1024 (default: 1024x1024)").option("--timeout-ms <n>", "Timeout for an image-capability run's HTTP request (default: 120000)", parseIntArg).option("--chain <stages>", "Comma-separated stage classes, e.g. draft,implement or draft,implement,review").option("--agent <name>", "Run the agent definition ~/.claude/agents/<name>.md on a worker").option("--model <model>", "Override the model for this run. Headless (-p) workers default to the --class model; an interactive launch (no -p) with no --model uses the harness default model from settings.json.").option("--label <text>", "Short task label shown in ps / follow / status line (default: first line of the prompt)").option("--spec <path>", "Read the prompt from this file (or - for stdin) instead of -p; mutually exclusive with -p").option("--cwd <dir>", "Directory the worker runs in (default: this process's cwd)").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").option("--worktree", "Run in a git worktree on branch worker/<id> (default for implement/complex/plan in a git repo)").option("--no-worktree", "Run in place, no worktree").option("--print-cmd", "Print the assembled claude argv as JSON and exit, without spawning (audit tool)").option("--report <format>", "Final-report contract/parser: json or ag2 (default: ag2, or PAI_WORKER_REPORT)").option("--no-report-retry", "Skip the one bounded re-ask when the final AG2 message fails validation").argument("[args...]", "claude arguments, e.g. -p '<task>' --allowedTools 'Read,Edit,Bash'").action(async (args, opts) => {
|
|
17877
17828
|
try {
|
|
17878
17829
|
const className = opts.class ?? opts.role;
|
|
17879
17830
|
if (opts.report !== void 0 && opts.report !== "json" && opts.report !== "ag2") throw new Error(`--report: expected "json" or "ag2", got "${opts.report}"`);
|
|
@@ -17944,6 +17895,10 @@ function registerWorkerCommands(workerCmd) {
|
|
|
17944
17895
|
printCmd: opts.printCmd,
|
|
17945
17896
|
reportFormatFlag,
|
|
17946
17897
|
noReportRetry: opts.reportRetry === false,
|
|
17898
|
+
capabilityFlag: opts.capability ?? opts.for,
|
|
17899
|
+
imageOut: opts.out,
|
|
17900
|
+
imageSize: opts.size,
|
|
17901
|
+
imageTimeoutMs: opts.timeoutMs,
|
|
17947
17902
|
claudeArgs
|
|
17948
17903
|
});
|
|
17949
17904
|
process.exitCode = rc;
|
|
@@ -18286,6 +18241,7 @@ function registerWorkerCommands(workerCmd) {
|
|
|
18286
18241
|
registerWorkerProviderCommands(workerCmd.command("providers").description("Providers: list, add, remove, use, enable, disable, test"));
|
|
18287
18242
|
registerWorkerClassCommands(workerCmd);
|
|
18288
18243
|
registerWorkerModelCommand(workerCmd);
|
|
18244
|
+
registerWorkerCapabilityCommand(workerCmd);
|
|
18289
18245
|
registerWorkerConfigCommands(workerCmd);
|
|
18290
18246
|
}
|
|
18291
18247
|
function parseIntArg(v) {
|
|
@@ -18948,7 +18904,7 @@ function analyzeProject(db, project) {
|
|
|
18948
18904
|
async function countVectorDbPaths(oldPaths) {
|
|
18949
18905
|
if (oldPaths.length === 0) return 0;
|
|
18950
18906
|
try {
|
|
18951
|
-
const { loadConfig } = await import("./config-
|
|
18907
|
+
const { loadConfig } = await import("./config-RjeVGJ7K.mjs");
|
|
18952
18908
|
const { PostgresBackend } = await import("./postgres-CUSXWgTG.mjs");
|
|
18953
18909
|
const config = loadConfig();
|
|
18954
18910
|
if (config.storageBackend !== "postgres") return 0;
|
|
@@ -18969,7 +18925,7 @@ async function countVectorDbPaths(oldPaths) {
|
|
|
18969
18925
|
async function updateVectorDbPaths(moves) {
|
|
18970
18926
|
if (moves.length === 0) return 0;
|
|
18971
18927
|
try {
|
|
18972
|
-
const { loadConfig } = await import("./config-
|
|
18928
|
+
const { loadConfig } = await import("./config-RjeVGJ7K.mjs");
|
|
18973
18929
|
const { PostgresBackend } = await import("./postgres-CUSXWgTG.mjs");
|
|
18974
18930
|
const config = loadConfig();
|
|
18975
18931
|
if (config.storageBackend !== "postgres") return 0;
|
|
@@ -20510,4 +20466,4 @@ claude() {
|
|
|
20510
20466
|
|
|
20511
20467
|
//#endregion
|
|
20512
20468
|
export { drainStdio as n, buildProgram as t };
|
|
20513
|
-
//# sourceMappingURL=program-
|
|
20469
|
+
//# sourceMappingURL=program-BX67-4I2.mjs.map
|