@tekmidian/pai 0.48.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-Bgv1lFFA.mjs → program-BX67-4I2.mjs} +536 -260
- 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/audit.md +1 -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-Bgv1lFFA.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"));
|
|
@@ -10029,197 +10139,269 @@ function auditHooks(cwd) {
|
|
|
10029
10139
|
}
|
|
10030
10140
|
|
|
10031
10141
|
//#endregion
|
|
10032
|
-
//#region src/audit/
|
|
10142
|
+
//#region src/audit/first-turn.ts
|
|
10033
10143
|
/**
|
|
10034
|
-
*
|
|
10035
|
-
*
|
|
10144
|
+
* first-turn.ts — where a turn's context actually comes from, attributed
|
|
10145
|
+
* line by line from the transcript.
|
|
10036
10146
|
*
|
|
10037
|
-
*
|
|
10038
|
-
*
|
|
10039
|
-
*
|
|
10040
|
-
*
|
|
10147
|
+
* The API reports one number per assistant turn (cache_read + cache_creation
|
|
10148
|
+
* + input). The transcript holds the pieces Claude Code injected since the
|
|
10149
|
+
* previous turn: SessionStart / UserPromptSubmit hook output, the skill
|
|
10150
|
+
* listing, the deferred-tool name list, MCP server instructions, the human
|
|
10151
|
+
* prompt, any isMeta expansion (a skill body, a pasted slash-command result),
|
|
10152
|
+
* tool results, and the previous turn's own visible output (re-sent as
|
|
10153
|
+
* context on every later turn). Each of those is counted in cl100k here;
|
|
10154
|
+
* what is left over is the system prompt, the built-in tool schemas, agents
|
|
10155
|
+
* and memory files, which never appear in the transcript.
|
|
10041
10156
|
*
|
|
10042
|
-
*
|
|
10043
|
-
*
|
|
10044
|
-
*
|
|
10045
|
-
*
|
|
10046
|
-
|
|
10047
|
-
|
|
10048
|
-
|
|
10049
|
-
|
|
10050
|
-
|
|
10051
|
-
|
|
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.
|
|
10157
|
+
* Only the live lineage counts. Lines chain by parentUuid, and a prompt the
|
|
10158
|
+
* user abandoned before a reply (Escape, re-submit) leaves a dead branch
|
|
10159
|
+
* whose lines were never sent — run 9 measured a session whose first
|
|
10160
|
+
* /Name was abandoned and read 24k where the true baseline is 30k, because
|
|
10161
|
+
* the 3.5k-token skill listing sat on the dead branch.
|
|
10162
|
+
*
|
|
10163
|
+
* `type:"system"` lines with subtype local_command hold a slash command's
|
|
10164
|
+
* raw stdout, and the model receives it verbatim in addition to the isMeta
|
|
10165
|
+
* markdown copy — run 11 measured both present on the same turn, so both are
|
|
10166
|
+
* counted.
|
|
10083
10167
|
*/
|
|
10084
|
-
function
|
|
10085
|
-
|
|
10168
|
+
function parseLines(path) {
|
|
10169
|
+
const lines = [];
|
|
10170
|
+
for (const raw of readFileSync(path, "utf8").split("\n")) {
|
|
10171
|
+
if (!raw.trim()) continue;
|
|
10172
|
+
try {
|
|
10173
|
+
lines.push(JSON.parse(raw));
|
|
10174
|
+
} catch {}
|
|
10175
|
+
}
|
|
10176
|
+
return lines;
|
|
10086
10177
|
}
|
|
10087
|
-
function
|
|
10088
|
-
|
|
10089
|
-
|
|
10090
|
-
|
|
10091
|
-
|
|
10092
|
-
|
|
10093
|
-
|
|
10178
|
+
function classify(line) {
|
|
10179
|
+
if (line.type === "attachment") {
|
|
10180
|
+
const a = line.attachment ?? {};
|
|
10181
|
+
switch (a.type) {
|
|
10182
|
+
case "hook_success": {
|
|
10183
|
+
const name = a.hookName ?? "";
|
|
10184
|
+
return {
|
|
10185
|
+
kind: name.startsWith("SessionStart") ? "hook:SessionStart" : name.startsWith("UserPromptSubmit") ? "hook:UserPromptSubmit" : "hook:other",
|
|
10186
|
+
tokens: countTokens(a.stdout ?? a.content ?? ""),
|
|
10187
|
+
label: name
|
|
10188
|
+
};
|
|
10189
|
+
}
|
|
10190
|
+
case "skill_listing": return {
|
|
10191
|
+
kind: "skill_listing",
|
|
10192
|
+
tokens: countTokens(a.content ?? ""),
|
|
10193
|
+
label: "skill_listing"
|
|
10194
|
+
};
|
|
10195
|
+
case "deferred_tools_delta": return {
|
|
10196
|
+
kind: "deferred_tools",
|
|
10197
|
+
tokens: countTokens((a.addedNames ?? []).join("\n")),
|
|
10198
|
+
label: "deferred_tools_delta"
|
|
10199
|
+
};
|
|
10200
|
+
case "mcp_instructions_delta": return {
|
|
10201
|
+
kind: "mcp_instructions",
|
|
10202
|
+
tokens: countTokens(JSON.stringify(a.instructions ?? a.addedInstructions ?? a)),
|
|
10203
|
+
label: "mcp_instructions_delta"
|
|
10204
|
+
};
|
|
10205
|
+
default: return {
|
|
10206
|
+
kind: "attachment:other",
|
|
10207
|
+
tokens: countTokens(JSON.stringify(a)),
|
|
10208
|
+
label: a.type ?? "attachment"
|
|
10209
|
+
};
|
|
10210
|
+
}
|
|
10211
|
+
}
|
|
10212
|
+
if (line.type === "user") {
|
|
10213
|
+
const c = line.message?.content;
|
|
10214
|
+
const isToolResult = Array.isArray(c) && c.some((b) => b?.type === "tool_result");
|
|
10215
|
+
const text = typeof c === "string" ? c : JSON.stringify(c ?? "");
|
|
10216
|
+
return {
|
|
10217
|
+
kind: isToolResult ? "tool_result" : line.isMeta ? "prompt:meta" : "prompt",
|
|
10218
|
+
tokens: countTokens(text),
|
|
10219
|
+
label: text.slice(0, 40).replace(/\s+/g, " ")
|
|
10220
|
+
};
|
|
10221
|
+
}
|
|
10222
|
+
if (line.type === "system") return {
|
|
10223
|
+
kind: line.subtype === "local_command" ? "prompt:local_command" : "system:display-only",
|
|
10224
|
+
tokens: countTokens(line.content ?? ""),
|
|
10225
|
+
label: line.subtype ?? "system"
|
|
10094
10226
|
};
|
|
10227
|
+
return null;
|
|
10095
10228
|
}
|
|
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;
|
|
10229
|
+
function apiContextOf(usage) {
|
|
10107
10230
|
if (!usage) return null;
|
|
10108
|
-
|
|
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;
|
|
10231
|
+
return (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + (usage.input_tokens ?? 0);
|
|
10143
10232
|
}
|
|
10144
10233
|
/**
|
|
10145
|
-
*
|
|
10146
|
-
*
|
|
10147
|
-
*
|
|
10148
|
-
*
|
|
10234
|
+
* Attribute API call `turn`'s context growth to the transcript lines
|
|
10235
|
+
* injected since the previous call. Turn numbering matches
|
|
10236
|
+
* session-usage.ts's `turnIndex`: several assistant lines sharing one
|
|
10237
|
+
* message.id (one line per streamed content block) are one call. Returns
|
|
10238
|
+
* null if the transcript has fewer than `turn` calls.
|
|
10149
10239
|
*/
|
|
10150
|
-
function
|
|
10151
|
-
if (
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
const
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
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;
|
|
10240
|
+
function turnBreakdown(path, turn) {
|
|
10241
|
+
if (!existsSync(path)) return null;
|
|
10242
|
+
const lines = parseLines(path);
|
|
10243
|
+
const byUuid = /* @__PURE__ */ new Map();
|
|
10244
|
+
for (const l of lines) if (l.uuid) byUuid.set(l.uuid, l);
|
|
10245
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10246
|
+
let n = 0;
|
|
10247
|
+
let target = null;
|
|
10248
|
+
for (const l of lines) if (l.type === "assistant" && l.message?.usage) {
|
|
10249
|
+
const id = l.message.id ?? l.uuid ?? "";
|
|
10250
|
+
if (!seen.has(id)) {
|
|
10251
|
+
seen.add(id);
|
|
10252
|
+
n++;
|
|
10253
|
+
if (n === turn) {
|
|
10254
|
+
target = l;
|
|
10255
|
+
break;
|
|
10256
|
+
}
|
|
10202
10257
|
}
|
|
10203
|
-
|
|
10204
|
-
|
|
10205
|
-
|
|
10206
|
-
|
|
10258
|
+
}
|
|
10259
|
+
if (!target) return null;
|
|
10260
|
+
const rawChain = [];
|
|
10261
|
+
let prevLine = null;
|
|
10262
|
+
let cur = target.parentUuid ? byUuid.get(target.parentUuid) : void 0;
|
|
10263
|
+
while (cur) {
|
|
10264
|
+
if (cur.type === "assistant" && cur.message?.usage) {
|
|
10265
|
+
prevLine = cur;
|
|
10266
|
+
break;
|
|
10207
10267
|
}
|
|
10208
|
-
|
|
10209
|
-
|
|
10268
|
+
rawChain.unshift(cur);
|
|
10269
|
+
cur = cur.parentUuid ? byUuid.get(cur.parentUuid) : void 0;
|
|
10270
|
+
}
|
|
10271
|
+
const items = [];
|
|
10272
|
+
for (const l of rawChain) {
|
|
10273
|
+
const item = classify(l);
|
|
10274
|
+
if (item) items.push(item);
|
|
10275
|
+
}
|
|
10276
|
+
let prevApiContext = 0;
|
|
10277
|
+
let prevOutputTokens = 0;
|
|
10278
|
+
if (prevLine) {
|
|
10279
|
+
prevApiContext = apiContextOf(prevLine.message?.usage) ?? 0;
|
|
10280
|
+
prevOutputTokens = prevLine.message?.usage?.output_tokens ?? 0;
|
|
10281
|
+
const prevId = prevLine.message?.id ?? prevLine.uuid ?? "";
|
|
10282
|
+
const prevBlocks = [prevLine];
|
|
10283
|
+
let cur2 = prevLine.parentUuid ? byUuid.get(prevLine.parentUuid) : void 0;
|
|
10284
|
+
while (cur2 && cur2.type === "assistant" && (cur2.message?.id ?? cur2.uuid ?? "") === prevId) {
|
|
10285
|
+
prevBlocks.unshift(cur2);
|
|
10286
|
+
cur2 = cur2.parentUuid ? byUuid.get(cur2.parentUuid) : void 0;
|
|
10287
|
+
}
|
|
10288
|
+
let prevText = "";
|
|
10289
|
+
let thinkingCount = 0;
|
|
10290
|
+
for (const b of prevBlocks) {
|
|
10291
|
+
const content = b.message?.content;
|
|
10292
|
+
if (!Array.isArray(content)) continue;
|
|
10293
|
+
for (const block of content) if (block?.type === "text") prevText += block.text ?? "";
|
|
10294
|
+
else if (block?.type === "tool_use") prevText += JSON.stringify(block.input ?? {});
|
|
10295
|
+
else if (block?.type === "thinking") thinkingCount++;
|
|
10296
|
+
}
|
|
10297
|
+
const label = thinkingCount > 0 ? `previous call output (${thinkingCount} thinking block(s) redacted)` : "previous call output";
|
|
10298
|
+
items.push({
|
|
10299
|
+
kind: "assistant:prev",
|
|
10300
|
+
tokens: countTokens(prevText),
|
|
10301
|
+
label
|
|
10302
|
+
});
|
|
10303
|
+
}
|
|
10304
|
+
const byKind = {};
|
|
10305
|
+
let transcriptTokens = 0;
|
|
10306
|
+
let prevVisibleTokens = 0;
|
|
10307
|
+
for (const item of items) {
|
|
10308
|
+
if (item.kind === "system:display-only") continue;
|
|
10309
|
+
if (item.kind === "assistant:prev") {
|
|
10310
|
+
prevVisibleTokens = item.tokens;
|
|
10210
10311
|
continue;
|
|
10211
10312
|
}
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
contextSum += context;
|
|
10215
|
-
report.promptExposure += report.userPrompts;
|
|
10216
|
-
}
|
|
10313
|
+
transcriptTokens += item.tokens;
|
|
10314
|
+
byKind[item.kind] = (byKind[item.kind] ?? 0) + item.tokens;
|
|
10217
10315
|
}
|
|
10218
|
-
|
|
10219
|
-
|
|
10316
|
+
const apiContext = apiContextOf(target.message?.usage);
|
|
10317
|
+
const billedDelta = apiContext === null ? null : apiContext - prevApiContext;
|
|
10318
|
+
const residual = billedDelta === null ? null : billedDelta - transcriptTokens - prevOutputTokens;
|
|
10319
|
+
return {
|
|
10320
|
+
path,
|
|
10321
|
+
turn,
|
|
10322
|
+
apiContext,
|
|
10323
|
+
prevApiContext,
|
|
10324
|
+
billedDelta,
|
|
10325
|
+
prevOutputTokens,
|
|
10326
|
+
items,
|
|
10327
|
+
transcriptTokens,
|
|
10328
|
+
byKind,
|
|
10329
|
+
prevVisibleTokens,
|
|
10330
|
+
residual
|
|
10331
|
+
};
|
|
10220
10332
|
}
|
|
10221
|
-
|
|
10222
|
-
|
|
10333
|
+
/** Attribute the first assistant turn's context to the transcript lines on its live lineage. */
|
|
10334
|
+
function firstTurnBreakdown(path) {
|
|
10335
|
+
const empty = {
|
|
10336
|
+
path,
|
|
10337
|
+
apiContext: null,
|
|
10338
|
+
items: [],
|
|
10339
|
+
transcriptTokens: 0,
|
|
10340
|
+
byKind: {},
|
|
10341
|
+
remainder: null,
|
|
10342
|
+
deadBranchTokens: 0,
|
|
10343
|
+
deadBranchLines: 0
|
|
10344
|
+
};
|
|
10345
|
+
if (!existsSync(path)) return empty;
|
|
10346
|
+
const tb = turnBreakdown(path, 1);
|
|
10347
|
+
if (!tb) return empty;
|
|
10348
|
+
const lines = parseLines(path);
|
|
10349
|
+
const byUuid = /* @__PURE__ */ new Map();
|
|
10350
|
+
let firstIndex = -1;
|
|
10351
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10352
|
+
const l = lines[i];
|
|
10353
|
+
if (l.uuid) byUuid.set(l.uuid, l);
|
|
10354
|
+
if (l.type === "assistant" && l.message?.usage && firstIndex < 0) firstIndex = i;
|
|
10355
|
+
}
|
|
10356
|
+
if (firstIndex < 0) return empty;
|
|
10357
|
+
const first = lines[firstIndex];
|
|
10358
|
+
const live = /* @__PURE__ */ new Set();
|
|
10359
|
+
let cur = first.parentUuid ? byUuid.get(first.parentUuid) : void 0;
|
|
10360
|
+
while (cur && !live.has(cur)) {
|
|
10361
|
+
live.add(cur);
|
|
10362
|
+
cur = cur.parentUuid ? byUuid.get(cur.parentUuid) : void 0;
|
|
10363
|
+
}
|
|
10364
|
+
let deadBranchTokens = 0;
|
|
10365
|
+
let deadBranchLines = 0;
|
|
10366
|
+
for (let i = 0; i < firstIndex; i++) {
|
|
10367
|
+
const l = lines[i];
|
|
10368
|
+
if (!l.uuid || live.has(l)) continue;
|
|
10369
|
+
const item = classify(l);
|
|
10370
|
+
if (!item || item.kind === "system:display-only") continue;
|
|
10371
|
+
deadBranchLines++;
|
|
10372
|
+
deadBranchTokens += item.tokens;
|
|
10373
|
+
}
|
|
10374
|
+
return {
|
|
10375
|
+
path,
|
|
10376
|
+
apiContext: tb.apiContext,
|
|
10377
|
+
items: tb.items,
|
|
10378
|
+
transcriptTokens: tb.transcriptTokens,
|
|
10379
|
+
byKind: tb.byKind,
|
|
10380
|
+
remainder: tb.residual,
|
|
10381
|
+
deadBranchTokens,
|
|
10382
|
+
deadBranchLines
|
|
10383
|
+
};
|
|
10384
|
+
}
|
|
10385
|
+
/** One-line evidence string for the combined report's first-turn finding. */
|
|
10386
|
+
function firstTurnEvidence(b) {
|
|
10387
|
+
const parts = [];
|
|
10388
|
+
for (const k of [
|
|
10389
|
+
"skill_listing",
|
|
10390
|
+
"mcp_instructions",
|
|
10391
|
+
"deferred_tools",
|
|
10392
|
+
"hook:SessionStart",
|
|
10393
|
+
"hook:UserPromptSubmit",
|
|
10394
|
+
"hook:other",
|
|
10395
|
+
"prompt:meta",
|
|
10396
|
+
"prompt:local_command",
|
|
10397
|
+
"prompt",
|
|
10398
|
+
"attachment:other"
|
|
10399
|
+
]) {
|
|
10400
|
+
const v = b.byKind[k];
|
|
10401
|
+
if (v) parts.push(`${k} ${v}`);
|
|
10402
|
+
}
|
|
10403
|
+
const dead = b.deadBranchTokens > 0 ? `; dead-branch ${b.deadBranchTokens} not sent` : "";
|
|
10404
|
+
return `transcript-side ${b.transcriptTokens} (${parts.join(", ")}), remainder ${b.remainder ?? "n/a"}${dead}`;
|
|
10223
10405
|
}
|
|
10224
10406
|
|
|
10225
10407
|
//#endregion
|
|
@@ -10268,14 +10450,16 @@ function newestSessionLog() {
|
|
|
10268
10450
|
async function auditSession(path, threshold) {
|
|
10269
10451
|
const target = path ?? newestSessionLog();
|
|
10270
10452
|
if (!target) return null;
|
|
10271
|
-
const
|
|
10453
|
+
const keepaliveWord = loadConfig().sessions.cacheKeepalive.prompt;
|
|
10454
|
+
const report = await parseSessionUsage(target, threshold, keepaliveWord);
|
|
10272
10455
|
const total = totalUsageTokens(report.totals) || 1;
|
|
10273
10456
|
const percentages = {};
|
|
10274
10457
|
for (const [key, value] of Object.entries(report.totals)) percentages[key] = 100 * value / total;
|
|
10275
10458
|
return {
|
|
10276
10459
|
...report,
|
|
10277
10460
|
totalTokens: total,
|
|
10278
|
-
percentages
|
|
10461
|
+
percentages,
|
|
10462
|
+
firstTurn: firstTurnBreakdown(target)
|
|
10279
10463
|
};
|
|
10280
10464
|
}
|
|
10281
10465
|
/**
|
|
@@ -11143,10 +11327,11 @@ function buildFindings(input) {
|
|
|
11143
11327
|
});
|
|
11144
11328
|
if (input.session) {
|
|
11145
11329
|
const first = input.session.firstTurnContext ?? 0;
|
|
11330
|
+
const evidence = typeof input.session.firstTurn?.apiContext === "number" ? `${first} tokens: ${firstTurnEvidence(input.session.firstTurn)} (${input.session.path})` : `${first} tokens (${input.session.path})`;
|
|
11146
11331
|
findings.push({
|
|
11147
11332
|
finding: "first-turn context size",
|
|
11148
11333
|
severity: first > FIRST_TURN_CONTEXT_LIMIT ? "AMBER" : "GREEN",
|
|
11149
|
-
evidence
|
|
11334
|
+
evidence
|
|
11150
11335
|
});
|
|
11151
11336
|
}
|
|
11152
11337
|
const rate = input.daemon.kgParseFailureRate;
|
|
@@ -11789,6 +11974,23 @@ function printSession(report) {
|
|
|
11789
11974
|
"share"
|
|
11790
11975
|
], rows));
|
|
11791
11976
|
console.log(`context on first turn: ${report.firstTurnContext ?? "n/a"}`);
|
|
11977
|
+
if (report.firstTurn && report.firstTurn.apiContext !== null) {
|
|
11978
|
+
const ft = report.firstTurn;
|
|
11979
|
+
console.log(header("first-turn breakdown (live lineage, cl100k)"));
|
|
11980
|
+
const ftRows = ft.items.filter((i) => i.tokens > 0).map((i) => [
|
|
11981
|
+
i.kind,
|
|
11982
|
+
String(i.tokens),
|
|
11983
|
+
i.label
|
|
11984
|
+
]);
|
|
11985
|
+
console.log(renderTable([
|
|
11986
|
+
"kind",
|
|
11987
|
+
"tokens",
|
|
11988
|
+
"label"
|
|
11989
|
+
], ftRows));
|
|
11990
|
+
console.log(`transcript-side: ${ft.transcriptTokens}`);
|
|
11991
|
+
console.log(`remainder (system prompt + tool schemas + agents + memory): ${ft.remainder}`);
|
|
11992
|
+
console.log(`dead-branch: ${ft.deadBranchTokens} tokens on ${ft.deadBranchLines} lines, persisted but never sent`);
|
|
11993
|
+
}
|
|
11792
11994
|
console.log(`context on last turn: ${report.lastTurnContext ?? "n/a"}`);
|
|
11793
11995
|
console.log(`cache_creation split: ephemeral_5m=${report.cacheCreationSplit.ephemeral5m}, ephemeral_1h=${report.cacheCreationSplit.ephemeral1h}`);
|
|
11794
11996
|
console.log(dim$1(`models: ${JSON.stringify(report.models)}`));
|
|
@@ -11801,6 +12003,25 @@ function printSession(report) {
|
|
|
11801
12003
|
console.log(`safeguard fallbacks: ${report.fallbacks.length}`);
|
|
11802
12004
|
for (const f of report.fallbacks) console.log(dim$1(` ${f.from} -> ${f.to} before turn ${f.turnIndex}: ${f.category} (${f.scope})`));
|
|
11803
12005
|
}
|
|
12006
|
+
console.log(dim$1(`idle gaps > 60min: ${report.idleGapsOver60min}, keepalive beats: ${report.keepaliveBeats ?? "n/a"}`));
|
|
12007
|
+
}
|
|
12008
|
+
function printTurnBreakdown(tb) {
|
|
12009
|
+
console.log(header(`turn ${tb.turn} attribution (live lineage, cl100k)`));
|
|
12010
|
+
const rows = tb.items.filter((i) => i.tokens > 0).map((i) => [
|
|
12011
|
+
String(i.tokens),
|
|
12012
|
+
i.kind,
|
|
12013
|
+
i.label.replace(/\s+/g, " ").slice(0, 60)
|
|
12014
|
+
]);
|
|
12015
|
+
console.log(renderTable([
|
|
12016
|
+
"tokens",
|
|
12017
|
+
"kind",
|
|
12018
|
+
"label"
|
|
12019
|
+
], rows));
|
|
12020
|
+
console.log(`transcript-side ${tb.transcriptTokens}`);
|
|
12021
|
+
console.log(`billed delta ${tb.billedDelta ?? "n/a"} (context ${tb.apiContext ?? "n/a"} after ${tb.prevApiContext})`);
|
|
12022
|
+
console.log(`previous call output ${tb.prevOutputTokens} (includes thinking, re-sent on tool turns)`);
|
|
12023
|
+
console.log(`previous call visible output ${tb.prevVisibleTokens} (cl100k, listed as assistant:prev, not in transcript-side)`);
|
|
12024
|
+
console.log(`residual ${tb.residual ?? "n/a"}: positive = cl100k undercount vs the billing tokenizer (1.2-1.45x measured on tool output and markdown tables); negative = the previous call's thinking was not re-sent (only re-sent inside a tool loop)`);
|
|
11804
12025
|
}
|
|
11805
12026
|
function printSessionHistory(rows) {
|
|
11806
12027
|
console.log(header("Session history (newest first)"));
|
|
@@ -11829,6 +12050,23 @@ async function cmdSession(path, opts) {
|
|
|
11829
12050
|
printSessionHistory(rows);
|
|
11830
12051
|
return;
|
|
11831
12052
|
}
|
|
12053
|
+
if (opts.turn !== void 0) {
|
|
12054
|
+
const target = path ?? newestSessionLog();
|
|
12055
|
+
if (!target) {
|
|
12056
|
+
console.error(err(" No session transcript found."));
|
|
12057
|
+
process.exitCode = 1;
|
|
12058
|
+
return;
|
|
12059
|
+
}
|
|
12060
|
+
const tb = turnBreakdown(target, parseInt(opts.turn, 10));
|
|
12061
|
+
if (!tb) {
|
|
12062
|
+
console.error(err(` Turn ${opts.turn} not found in ${target}.`));
|
|
12063
|
+
process.exitCode = 1;
|
|
12064
|
+
return;
|
|
12065
|
+
}
|
|
12066
|
+
if (opts.json) return printJson(tb);
|
|
12067
|
+
printTurnBreakdown(tb);
|
|
12068
|
+
return;
|
|
12069
|
+
}
|
|
11832
12070
|
const report = await auditSession(path, opts.ctxThreshold ? parseInt(opts.ctxThreshold, 10) : void 0);
|
|
11833
12071
|
if (!report) {
|
|
11834
12072
|
console.error(err(" No session transcript found."));
|
|
@@ -12312,7 +12550,7 @@ function registerAuditCommands(auditCmd) {
|
|
|
12312
12550
|
await cmdFiles(paths, jsonOpt());
|
|
12313
12551
|
});
|
|
12314
12552
|
tokensCmd.command("hooks").description("Token cost of every SessionStart / UserPromptSubmit hook").action(() => cmdHooks(jsonOpt()));
|
|
12315
|
-
tokensCmd.command("session").description("Cache/input/output token split for a session transcript (default: newest)").argument("[path]", "Session JSONL path (default: newest under ~/.claude/projects)").option("--ctx-threshold <n>", "Per-turn context size above which a turn counts as 'above threshold' (default 200000)").option("--history [n]", "List first-turn context of the newest n sessions in this project's transcript directory (default 20)").action(async (path, opts) => {
|
|
12553
|
+
tokensCmd.command("session").description("Cache/input/output token split for a session transcript (default: newest)").argument("[path]", "Session JSONL path (default: newest under ~/.claude/projects)").option("--ctx-threshold <n>", "Per-turn context size above which a turn counts as 'above threshold' (default 200000)").option("--history [n]", "List first-turn context of the newest n sessions in this project's transcript directory (default 20)").option("--turn <n>", "Attribute API call n's context growth to the transcript lines injected since the previous call").action(async (path, opts) => {
|
|
12316
12554
|
await cmdSession(path, {
|
|
12317
12555
|
...jsonOpt(),
|
|
12318
12556
|
...opts
|
|
@@ -15091,8 +15329,8 @@ function cmdActive(db, opts) {
|
|
|
15091
15329
|
async function cmdAutoRoute(opts) {
|
|
15092
15330
|
const { autoRoute, formatAutoRoute, formatAutoRouteJson } = await import("./auto-route-CcD0OWWB.mjs");
|
|
15093
15331
|
const { openRegistry } = await import("./db-DbY2O8QO.mjs");
|
|
15094
|
-
const { createStorageBackend } = await import("./factory-
|
|
15095
|
-
const { loadConfig } = await import("./config-
|
|
15332
|
+
const { createStorageBackend } = await import("./factory-Ba8j9K7q.mjs");
|
|
15333
|
+
const { loadConfig } = await import("./config-RjeVGJ7K.mjs");
|
|
15096
15334
|
const config = loadConfig();
|
|
15097
15335
|
const registryDb = openRegistry();
|
|
15098
15336
|
const federation = await createStorageBackend(config);
|
|
@@ -17122,7 +17360,7 @@ function deriveLabel(prompt, klass) {
|
|
|
17122
17360
|
|
|
17123
17361
|
//#endregion
|
|
17124
17362
|
//#region src/cli/commands/worker/providers.ts
|
|
17125
|
-
function fail$
|
|
17363
|
+
function fail$4(e) {
|
|
17126
17364
|
const msg = e instanceof Error ? e.message : String(e);
|
|
17127
17365
|
console.error(err("pai worker: ") + msg);
|
|
17128
17366
|
process.exitCode = 1;
|
|
@@ -17185,12 +17423,12 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17185
17423
|
if (legacyNotice) console.log(dim$1(` ${legacyNotice}`));
|
|
17186
17424
|
console.log();
|
|
17187
17425
|
});
|
|
17188
|
-
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) => {
|
|
17189
17427
|
try {
|
|
17190
17428
|
const protocol = opts.protocol;
|
|
17191
17429
|
const engine = opts.engine;
|
|
17192
17430
|
if (protocol && protocol !== "anthropic" && protocol !== "openai") throw new WorkersConfigError(`--protocol must be anthropic or openai, got "${protocol}"`);
|
|
17193
|
-
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}"`);
|
|
17194
17432
|
const baseUrl = opts.baseUrl ?? opts.url;
|
|
17195
17433
|
if (protocol !== "openai" && !baseUrl) throw new WorkersConfigError("--base-url (or --url) is required (only --protocol openai goes without it)");
|
|
17196
17434
|
if (opts.key && opts.keyFile) throw new WorkersConfigError("--key and --key-file are alternatives — pass one, not both");
|
|
@@ -17217,7 +17455,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17217
17455
|
if (workers.active === name) console.log(` active: ${name} — workers ${workers.enabled ? "on" : "off"}`);
|
|
17218
17456
|
for (const line of describeProviders(workers)) console.log(dim$1(` ${line}`));
|
|
17219
17457
|
} catch (e) {
|
|
17220
|
-
fail$
|
|
17458
|
+
fail$4(e);
|
|
17221
17459
|
}
|
|
17222
17460
|
});
|
|
17223
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) => {
|
|
@@ -17232,7 +17470,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17232
17470
|
const { workers } = readWorkersSection();
|
|
17233
17471
|
for (const line of describeProviders(workers)) console.log(dim$1(` ${line}`));
|
|
17234
17472
|
} catch (e) {
|
|
17235
|
-
fail$
|
|
17473
|
+
fail$4(e);
|
|
17236
17474
|
}
|
|
17237
17475
|
});
|
|
17238
17476
|
providersCmd.command("remove <name>").description("Remove a provider and any classes pointing at it").action((name) => {
|
|
@@ -17240,7 +17478,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17240
17478
|
removeProvider(name);
|
|
17241
17479
|
console.log(ok$1(`provider ${name} removed`));
|
|
17242
17480
|
} catch (e) {
|
|
17243
|
-
fail$
|
|
17481
|
+
fail$4(e);
|
|
17244
17482
|
}
|
|
17245
17483
|
});
|
|
17246
17484
|
providersCmd.command("use <name>").description("Make this provider the active one for runs without --provider/--class").action((name) => {
|
|
@@ -17248,7 +17486,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17248
17486
|
useProvider(name);
|
|
17249
17487
|
console.log(ok$1(`active provider: ${name}`));
|
|
17250
17488
|
} catch (e) {
|
|
17251
|
-
fail$
|
|
17489
|
+
fail$4(e);
|
|
17252
17490
|
}
|
|
17253
17491
|
});
|
|
17254
17492
|
providersCmd.command("enable <name>").description("Enable a provider (also clears its cooldown)").action((name) => {
|
|
@@ -17256,7 +17494,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17256
17494
|
setProviderEnabled(name, true);
|
|
17257
17495
|
console.log(ok$1(`provider ${name} enabled`));
|
|
17258
17496
|
} catch (e) {
|
|
17259
|
-
fail$
|
|
17497
|
+
fail$4(e);
|
|
17260
17498
|
}
|
|
17261
17499
|
});
|
|
17262
17500
|
providersCmd.command("disable <name>").description("Disable a provider (auto-routing skips it; --provider still works)").action((name) => {
|
|
@@ -17264,7 +17502,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17264
17502
|
setProviderEnabled(name, false);
|
|
17265
17503
|
console.log(ok$1(`provider ${name} disabled`));
|
|
17266
17504
|
} catch (e) {
|
|
17267
|
-
fail$
|
|
17505
|
+
fail$4(e);
|
|
17268
17506
|
}
|
|
17269
17507
|
});
|
|
17270
17508
|
providersCmd.command("test [name]").description("One-word pong probe through a provider (default: the active one)").action(async (name) => {
|
|
@@ -17284,7 +17522,7 @@ function registerWorkerProviderCommands(providersCmd) {
|
|
|
17284
17522
|
console.log(` reply: ${r.result.slice(0, 200)}`);
|
|
17285
17523
|
process.exitCode = r.ok ? 0 : 1;
|
|
17286
17524
|
} catch (e) {
|
|
17287
|
-
fail$
|
|
17525
|
+
fail$4(e);
|
|
17288
17526
|
}
|
|
17289
17527
|
});
|
|
17290
17528
|
}
|
|
@@ -17305,7 +17543,7 @@ function registerClassSubcommands(classesCmd) {
|
|
|
17305
17543
|
setClass(cls, built);
|
|
17306
17544
|
console.log(ok$1(`class ${cls} → ${classTargetText(built)}`));
|
|
17307
17545
|
} catch (e) {
|
|
17308
|
-
fail$
|
|
17546
|
+
fail$4(e);
|
|
17309
17547
|
}
|
|
17310
17548
|
});
|
|
17311
17549
|
classesCmd.command("unset <class>").description("Remove a class (runs then use the active provider)").action((cls) => {
|
|
@@ -17313,7 +17551,7 @@ function registerClassSubcommands(classesCmd) {
|
|
|
17313
17551
|
unsetClass(cls);
|
|
17314
17552
|
console.log(ok$1(`class ${cls} removed`));
|
|
17315
17553
|
} catch (e) {
|
|
17316
|
-
fail$
|
|
17554
|
+
fail$4(e);
|
|
17317
17555
|
}
|
|
17318
17556
|
});
|
|
17319
17557
|
}
|
|
@@ -17325,13 +17563,13 @@ function registerWorkerClassCommands(workerCmd) {
|
|
|
17325
17563
|
|
|
17326
17564
|
//#endregion
|
|
17327
17565
|
//#region src/cli/commands/worker/model.ts
|
|
17328
|
-
function fail$
|
|
17566
|
+
function fail$3(e) {
|
|
17329
17567
|
const msg = e instanceof Error ? e.message : String(e);
|
|
17330
17568
|
console.error(err("pai worker: ") + msg);
|
|
17331
17569
|
process.exitCode = 1;
|
|
17332
17570
|
}
|
|
17333
17571
|
function registerWorkerModelCommand(workerCmd) {
|
|
17334
|
-
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) => {
|
|
17335
17573
|
try {
|
|
17336
17574
|
const { workers } = readWorkersSection();
|
|
17337
17575
|
if (what === void 0) {
|
|
@@ -17356,6 +17594,39 @@ function registerWorkerModelCommand(workerCmd) {
|
|
|
17356
17594
|
console.log(ok$1(`${name} ${capability} model: ${p.models[capability]}`));
|
|
17357
17595
|
console.log(dim$1(` ${modelPrefsText(p)}`));
|
|
17358
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]}`);
|
|
17359
17630
|
} catch (e) {
|
|
17360
17631
|
fail$2(e);
|
|
17361
17632
|
}
|
|
@@ -17553,7 +17824,7 @@ function fail(e) {
|
|
|
17553
17824
|
process.exitCode = 1;
|
|
17554
17825
|
}
|
|
17555
17826
|
function registerWorkerCommands(workerCmd) {
|
|
17556
|
-
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) => {
|
|
17557
17828
|
try {
|
|
17558
17829
|
const className = opts.class ?? opts.role;
|
|
17559
17830
|
if (opts.report !== void 0 && opts.report !== "json" && opts.report !== "ag2") throw new Error(`--report: expected "json" or "ag2", got "${opts.report}"`);
|
|
@@ -17624,6 +17895,10 @@ function registerWorkerCommands(workerCmd) {
|
|
|
17624
17895
|
printCmd: opts.printCmd,
|
|
17625
17896
|
reportFormatFlag,
|
|
17626
17897
|
noReportRetry: opts.reportRetry === false,
|
|
17898
|
+
capabilityFlag: opts.capability ?? opts.for,
|
|
17899
|
+
imageOut: opts.out,
|
|
17900
|
+
imageSize: opts.size,
|
|
17901
|
+
imageTimeoutMs: opts.timeoutMs,
|
|
17627
17902
|
claudeArgs
|
|
17628
17903
|
});
|
|
17629
17904
|
process.exitCode = rc;
|
|
@@ -17966,6 +18241,7 @@ function registerWorkerCommands(workerCmd) {
|
|
|
17966
18241
|
registerWorkerProviderCommands(workerCmd.command("providers").description("Providers: list, add, remove, use, enable, disable, test"));
|
|
17967
18242
|
registerWorkerClassCommands(workerCmd);
|
|
17968
18243
|
registerWorkerModelCommand(workerCmd);
|
|
18244
|
+
registerWorkerCapabilityCommand(workerCmd);
|
|
17969
18245
|
registerWorkerConfigCommands(workerCmd);
|
|
17970
18246
|
}
|
|
17971
18247
|
function parseIntArg(v) {
|
|
@@ -18628,7 +18904,7 @@ function analyzeProject(db, project) {
|
|
|
18628
18904
|
async function countVectorDbPaths(oldPaths) {
|
|
18629
18905
|
if (oldPaths.length === 0) return 0;
|
|
18630
18906
|
try {
|
|
18631
|
-
const { loadConfig } = await import("./config-
|
|
18907
|
+
const { loadConfig } = await import("./config-RjeVGJ7K.mjs");
|
|
18632
18908
|
const { PostgresBackend } = await import("./postgres-CUSXWgTG.mjs");
|
|
18633
18909
|
const config = loadConfig();
|
|
18634
18910
|
if (config.storageBackend !== "postgres") return 0;
|
|
@@ -18649,7 +18925,7 @@ async function countVectorDbPaths(oldPaths) {
|
|
|
18649
18925
|
async function updateVectorDbPaths(moves) {
|
|
18650
18926
|
if (moves.length === 0) return 0;
|
|
18651
18927
|
try {
|
|
18652
|
-
const { loadConfig } = await import("./config-
|
|
18928
|
+
const { loadConfig } = await import("./config-RjeVGJ7K.mjs");
|
|
18653
18929
|
const { PostgresBackend } = await import("./postgres-CUSXWgTG.mjs");
|
|
18654
18930
|
const config = loadConfig();
|
|
18655
18931
|
if (config.storageBackend !== "postgres") return 0;
|
|
@@ -20190,4 +20466,4 @@ claude() {
|
|
|
20190
20466
|
|
|
20191
20467
|
//#endregion
|
|
20192
20468
|
export { drainStdio as n, buildProgram as t };
|
|
20193
|
-
//# sourceMappingURL=program-
|
|
20469
|
+
//# sourceMappingURL=program-BX67-4I2.mjs.map
|