@tekmidian/pai 0.59.0 → 0.60.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.
Files changed (129) hide show
  1. package/.claude-plugin/plugin.json +8 -33
  2. package/PLUGIN-ARCHITECTURE.md +4 -6
  3. package/dist/{aibroker-client-B8Wtasez.mjs → aibroker-client-BYG-GtAg.mjs} +1 -1
  4. package/dist/{chain-Cltx0NjY.mjs → chain-DFlzAfDE.mjs} +4 -4
  5. package/dist/{chain-Cltx0NjY.mjs.map → chain-DFlzAfDE.mjs.map} +1 -1
  6. package/dist/cli/index.mjs +12 -12
  7. package/dist/cli/program.mjs +12 -12
  8. package/dist/{config-DCYWFaEi.mjs → config-GceCMUxa.mjs} +1 -1
  9. package/dist/{config-D1omG_UU.mjs → config-msER8xj_.mjs} +16 -4
  10. package/dist/config-msER8xj_.mjs.map +1 -0
  11. package/dist/{context-handover-cache-Dt4HW1Ro.mjs → context-handover-cache-DF0xWVB0.mjs} +4 -4
  12. package/dist/{context-handover-cache-Dt4HW1Ro.mjs.map → context-handover-cache-DF0xWVB0.mjs.map} +1 -1
  13. package/dist/daemon/index.mjs +12 -12
  14. package/dist/daemon-BppwJtrH.mjs +19 -0
  15. package/dist/{daemon-CmqJfeb9.mjs → daemon-DArRKBlk.mjs} +97 -23
  16. package/dist/daemon-DArRKBlk.mjs.map +1 -0
  17. package/dist/daemon-mcp/index.mjs +9 -9
  18. package/dist/daemon-mcp/index.mjs.map +1 -1
  19. package/dist/{factory-DiuntvwG.mjs → factory-ChfAvCzO.mjs} +7 -7
  20. package/dist/{factory-DiuntvwG.mjs.map → factory-ChfAvCzO.mjs.map} +1 -1
  21. package/dist/{factory-C9wGUe30.mjs → factory-D6I2jmjQ.mjs} +1 -1
  22. package/dist/{fallback-B_2ozQHw.mjs → fallback-B7VEMMRn.mjs} +80 -9
  23. package/dist/fallback-B7VEMMRn.mjs.map +1 -0
  24. package/dist/hooks/block-sleep-poll.mjs +7590 -0
  25. package/dist/hooks/block-sleep-poll.mjs.map +7 -0
  26. package/dist/hooks/capture-all-events.mjs +8 -4
  27. package/dist/hooks/capture-all-events.mjs.map +2 -2
  28. package/dist/hooks/capture-session-summary.mjs.map +1 -1
  29. package/dist/hooks/capture-tool-output.mjs.map +1 -1
  30. package/dist/hooks/cleanup-session-files.mjs.map +1 -1
  31. package/dist/hooks/context-compression-hook.mjs +6 -6
  32. package/dist/hooks/context-compression-hook.mjs.map +3 -3
  33. package/dist/hooks/initialize-session.mjs.map +1 -1
  34. package/dist/hooks/load-core-context.mjs.map +1 -1
  35. package/dist/hooks/load-project-context.mjs.map +2 -2
  36. package/dist/hooks/post-compact-inject.mjs +1 -1
  37. package/dist/hooks/post-compact-inject.mjs.map +2 -2
  38. package/dist/hooks/route-agents-to-worker.mjs +4 -3
  39. package/dist/hooks/route-agents-to-worker.mjs.map +3 -3
  40. package/dist/hooks/security-validator.mjs +4 -3
  41. package/dist/hooks/security-validator.mjs.map +3 -3
  42. package/dist/hooks/stop-hook.mjs.map +1 -1
  43. package/dist/hooks/sync-todo-to-md.mjs.map +1 -1
  44. package/dist/hooks/whisper-rules.mjs +4 -3
  45. package/dist/hooks/whisper-rules.mjs.map +3 -3
  46. package/dist/hooks/worker-proxy.mjs +4 -3
  47. package/dist/hooks/worker-proxy.mjs.map +3 -3
  48. package/dist/hooks/worker-status-line.mjs +9 -6
  49. package/dist/hooks/worker-status-line.mjs.map +3 -3
  50. package/dist/hooks/worker-supervision.mjs +4 -3
  51. package/dist/hooks/worker-supervision.mjs.map +3 -3
  52. package/dist/{main-resolver-DYNxxNwJ.mjs → main-resolver-BqTjt-GL.mjs} +100 -21
  53. package/dist/main-resolver-BqTjt-GL.mjs.map +1 -0
  54. package/dist/main-resolver-CCojcYr2.mjs +7 -0
  55. package/dist/{planner-DsZZEZCA.mjs → planner-DbpsMZqY.mjs} +83 -30
  56. package/dist/planner-DbpsMZqY.mjs.map +1 -0
  57. package/dist/{program-B5miXqQa.mjs → program-QSuci3RH.mjs} +278 -92
  58. package/dist/program-QSuci3RH.mjs.map +1 -0
  59. package/dist/router-C5hPk9YP.mjs +3 -0
  60. package/dist/{router-BdxdsAvs.mjs → router-DjFZIoDl.mjs} +2 -1
  61. package/dist/router-DjFZIoDl.mjs.map +1 -0
  62. package/dist/{run-XaA-UPdW.mjs → run-BFxuNnIJ.mjs} +100 -17
  63. package/dist/run-BFxuNnIJ.mjs.map +1 -0
  64. package/dist/{run-env-DyTWgZC9.mjs → run-env-kbWVwUvU.mjs} +2 -2
  65. package/dist/{run-env-DyTWgZC9.mjs.map → run-env-kbWVwUvU.mjs.map} +1 -1
  66. package/dist/{server-CzqHGbKt.mjs → server-B0vBmfJt.mjs} +2 -2
  67. package/dist/{server-CzqHGbKt.mjs.map → server-B0vBmfJt.mjs.map} +1 -1
  68. package/dist/{session-keepalive-DDsEo81Q.mjs → session-keepalive-CUcFa8ld.mjs} +5 -5
  69. package/dist/{session-keepalive-DDsEo81Q.mjs.map → session-keepalive-CUcFa8ld.mjs.map} +1 -1
  70. package/dist/{tools-Diro2s8J.mjs → tools-CaTZnIau.mjs} +2 -2
  71. package/dist/{tools-Diro2s8J.mjs.map → tools-CaTZnIau.mjs.map} +1 -1
  72. package/dist/{tools-KgL1rkPD.mjs → tools-_7MF8Ebb.mjs} +3 -3
  73. package/dist/{wakeup-D0uvqLsE.mjs → wakeup-CTX3rlz-.mjs} +2 -2
  74. package/dist/wakeup-CTX3rlz-.mjs.map +1 -0
  75. package/dist/{work-queue-worker-DL9lu8sN.mjs → work-queue-worker-B4eZGy5n.mjs} +2 -2
  76. package/dist/{work-queue-worker-DL9lu8sN.mjs.map → work-queue-worker-B4eZGy5n.mjs.map} +1 -1
  77. package/dist/work-queue-worker-DuhZmfDB.mjs +11 -0
  78. package/docker/init.sql +1 -1
  79. package/docs/commands/README.md +4 -4
  80. package/docs/commands/daemon.md +6 -6
  81. package/docs/commands/session.md +2 -2
  82. package/docs/commands/worker.md +5 -2
  83. package/docs/workers-config.md +5 -1
  84. package/package.json +1 -1
  85. package/plugins/context-preservation/hooks/hooks.json +38 -31
  86. package/plugins/context-preservation/plugin.json +1 -4
  87. package/plugins/core/hooks/hooks.json +51 -46
  88. package/plugins/core/plugin.json +1 -4
  89. package/plugins/creative/plugin.json +1 -5
  90. package/plugins/observability/hooks/hooks.json +87 -72
  91. package/plugins/observability/plugin.json +1 -5
  92. package/plugins/productivity/hooks/hooks.json +22 -19
  93. package/plugins/productivity/plugin.json +2 -5
  94. package/plugins/semantic-search/plugin.json +1 -16
  95. package/plugins/ui/hooks/hooks.json +17 -14
  96. package/plugins/ui/plugin.json +1 -5
  97. package/plugins/zettelkasten/plugin.json +1 -14
  98. package/src/hooks/session-autosave.sh +4 -2
  99. package/src/hooks/session-stop.sh +2 -2
  100. package/src/hooks/ts/capture-all-events.ts +3 -3
  101. package/src/hooks/ts/lib/metadata-extraction.ts +3 -2
  102. package/src/hooks/ts/lib/pai-paths.test.ts +1 -1
  103. package/src/hooks/ts/lib/sleep-poll-gate.test.ts +62 -0
  104. package/src/hooks/ts/lib/sleep-poll-gate.ts +77 -0
  105. package/src/hooks/ts/post-tool-use/capture-tool-output.ts +1 -1
  106. package/src/hooks/ts/post-tool-use/sync-todo-to-md.ts +1 -1
  107. package/src/hooks/ts/pre-compact/context-compression-hook.ts +1 -1
  108. package/src/hooks/ts/pre-tool-use/block-sleep-poll.test.ts +44 -0
  109. package/src/hooks/ts/pre-tool-use/block-sleep-poll.ts +50 -0
  110. package/src/hooks/ts/session-end/capture-session-summary.ts +1 -1
  111. package/src/hooks/ts/session-start/initialize-session.ts +2 -2
  112. package/src/hooks/ts/session-start/load-core-context.ts +2 -2
  113. package/src/hooks/ts/session-start/load-project-context.ts +1 -1
  114. package/src/hooks/ts/stop/stop-hook.ts +1 -1
  115. package/src/hooks/ts/user-prompt/cleanup-session-files.ts +1 -1
  116. package/statusline-command.sh +26 -11
  117. package/dist/config-D1omG_UU.mjs.map +0 -1
  118. package/dist/daemon-B6hx1Ysz.mjs +0 -19
  119. package/dist/daemon-CmqJfeb9.mjs.map +0 -1
  120. package/dist/fallback-B_2ozQHw.mjs.map +0 -1
  121. package/dist/main-resolver-DYNxxNwJ.mjs.map +0 -1
  122. package/dist/main-resolver-Df65bPAi.mjs +0 -7
  123. package/dist/planner-DsZZEZCA.mjs.map +0 -1
  124. package/dist/program-B5miXqQa.mjs.map +0 -1
  125. package/dist/router-BXOf_e9e.mjs +0 -3
  126. package/dist/router-BdxdsAvs.mjs.map +0 -1
  127. package/dist/run-XaA-UPdW.mjs.map +0 -1
  128. package/dist/wakeup-D0uvqLsE.mjs.map +0 -1
  129. package/dist/work-queue-worker-BY7xn4pw.mjs +0 -11
@@ -3,28 +3,28 @@ import { a as slugify$1, i as parseSessionFilename } from "./migrate-BD7D8EEh.mj
3
3
  import { n as ensurePaiMarker } from "./pai-marker-D1MMswkz.mjs";
4
4
  import { t as STOP_WORDS } from "./stop-words-ClCwO8mZ.mjs";
5
5
  import { t as applyRecencyBoost } from "./search-BaTbyWjK.mjs";
6
- import { a as getStorageBackend, i as getRegistryBackend, r as createStorageBackend, s as humanDuration } from "./factory-DiuntvwG.mjs";
7
- import { a as formatDetectionJson, i as formatDetection, n as migrateIdentityFile, r as detectProject } from "./wakeup-D0uvqLsE.mjs";
8
- 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-Dt4HW1Ro.mjs";
6
+ import { a as getStorageBackend, i as getRegistryBackend, r as createStorageBackend, s as humanDuration } from "./factory-ChfAvCzO.mjs";
7
+ import { a as formatDetectionJson, i as formatDetection, n as migrateIdentityFile, r as detectProject } from "./wakeup-CTX3rlz-.mjs";
8
+ 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-DF0xWVB0.mjs";
9
9
  import { a as paiHomePath, i as paiHomeDir, n as migratePaiDir, o as resolvePaiFile, r as migratePaiFile, t as PaiFileMigrationError } from "./pai-home-CeOIYltV.mjs";
10
- 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-D1omG_UU.mjs";
10
+ 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-msER8xj_.mjs";
11
11
  import { a as schedulerLogPath, i as paiSocketPath, n as daemonLogPath, r as daemonPidPath } from "./runtime-paths-BRwDeBEg.mjs";
12
- import { A as initWorkersYaml, B as stripModelVariant, F as relocateWorkersYaml, I as workersYamlLegacyNotice, L as workersYamlPath, M as migrateWorkersToYaml, N as needsWorkersYamlRelocation, P as parseWorkersYamlDocument, R as DEFAULT_CONTEXT_WINDOW, S as nativeAnthropicProvider, T as readWorkersSection, _ as expandHome$1, d as PROVIDER_TAGS, j as inlineWorkersYamlKeys, p as WorkersConfigError, r as ANTHROPIC_NATIVE, t as buildRunEnv, u as MODEL_CAPABILITIES, v as getProviderOrNative, x as maskKey, y as isModelCapability, z as contextWindowFromModelId } from "./run-env-DyTWgZC9.mjs";
13
- import { B as saveStatus, C as mergeWorker, D as UNLABELED, F as loadStatus, H as waitForTerminalStatus, I as loadStatuses, K as describeMcp, N as isChatPane, P as isLive, S as discardWorker, T as appendLedger, U as longInlinePromptHint, V as setWorkerLabel, W as parseRunnerArgs, a as projectLaunchConfig, c as handoffFromInside, f as checkPaneForWorker, i as usageContextTokens, m as openPaneForWorker, n as runWorker, o as parseWorkerReport, p as openFollowPane, q as expandMcpNames, r as testProvider, u as sayToWorker, z as ownsPid } from "./run-XaA-UPdW.mjs";
12
+ import { A as initWorkersYaml, B as stripModelVariant, F as relocateWorkersYaml, I as workersYamlLegacyNotice, L as workersYamlPath, M as migrateWorkersToYaml, N as needsWorkersYamlRelocation, P as parseWorkersYamlDocument, R as DEFAULT_CONTEXT_WINDOW, S as nativeAnthropicProvider, T as readWorkersSection, _ as expandHome$1, d as PROVIDER_TAGS, j as inlineWorkersYamlKeys, p as WorkersConfigError, r as ANTHROPIC_NATIVE, t as buildRunEnv, u as MODEL_CAPABILITIES, v as getProviderOrNative, x as maskKey, y as isModelCapability, z as contextWindowFromModelId } from "./run-env-kbWVwUvU.mjs";
13
+ import { A as UNLABELED, E as mergeWorker, G as waitForTerminalStatus, H as ownsPid, I as isChatPane, K as longInlinePromptHint, L as isLive, O as appendLedger, R as loadStatus, U as saveStatus, W as setWorkerLabel, X as expandMcpNames, Y as describeMcp, a as projectLaunchConfig, c as handoffFromInside, f as checkPaneForWorker, g as markControlsHeld, h as clickrControlsArgv, i as usageContextTokens, m as openPaneForWorker, n as runWorker, o as parseWorkerReport, p as openFollowPane, q as parseRunnerArgs, r as testProvider, u as sayToWorker, w as discardWorker, z as loadStatuses } from "./run-BFxuNnIJ.mjs";
14
14
  import { t as MergeError } from "./merge-xoyfZQ4W.mjs";
15
15
  import { n as oldFederationPath, t as federationDbPath } from "./federation-db-w8e-sKjp.mjs";
16
16
  import { r as registryDbPath, t as oldRegistryPath } from "./registry-db-Bkpbp7Gf.mjs";
17
17
  import { i as indexProjectWithBackend } from "./async-CLvuY7b-.mjs";
18
18
  import { t as PaiClient } from "./ipc-client-Dasm515c.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-B_2ozQHw.mjs";
20
- import { _ as scanCacheFilePath, a as buildDeduped, c as hasConversation, d as restoreTopLevel, f as resumeArgvText, g as resolveSessionByNameOrId, h as migrateSessionScanCache, l as launchInDir, m as fmtAge, o as normalizeName$2, p as printExitDir, s as renderDedupedSessions, t as cmdMain, u as providerResumePlan, v as scanSessions } from "./main-resolver-DYNxxNwJ.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-B7VEMMRn.mjs";
20
+ import { _ as resolveSessionByNameOrId, a as buildDeduped, c as hasConversation, d as providerResumePlan, f as restoreTopLevel, g as migrateSessionScanCache, h as fmtAge, l as launchInDir, m as printExitDir, o as normalizeName$2, p as resumeArgvText, s as renderDedupedSessions, t as cmdMain, u as launchPrompt, v as scanCacheFilePath, y as scanSessions } from "./main-resolver-BqTjt-GL.mjs";
21
21
  import { n as queriesDirPath, t as migrateQueriesDir } from "./query-feedback-xz5axTj3.mjs";
22
- import { h as ledgerPath, m as eventsPath, n as ensureProxyRunning, p as ensureNoMcpConfig, r as stopProxy, t as DEFAULT_PROXY_PORT, v as workersLogDir } from "./server-CzqHGbKt.mjs";
22
+ import { h as ledgerPath, m as eventsPath, n as ensureProxyRunning, p as ensureNoMcpConfig, r as stopProxy, t as DEFAULT_PROXY_PORT, v as workersLogDir } from "./server-B0vBmfJt.mjs";
23
23
  import { i as sendToSession, n as fetchLiveSessions, r as revealItermSession, t as callAiBroker } from "./aibroker-client-BK1h--W_.mjs";
24
- import { a as totalUsageTokens, i as parseSessionUsage, r as sessionKeepaliveLedgerSummary, t as loadSessionKeepaliveState } from "./session-keepalive-DDsEo81Q.mjs";
25
- import { t as runChain } from "./chain-Cltx0NjY.mjs";
24
+ import { a as totalUsageTokens, i as parseSessionUsage, r as sessionKeepaliveLedgerSummary, t as loadSessionKeepaliveState } from "./session-keepalive-CUcFa8ld.mjs";
25
+ import { t as runChain } from "./chain-DFlzAfDE.mjs";
26
26
  import { appendFileSync, chmodSync, copyFileSync, cpSync, createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
27
- import { homedir, platform, tmpdir } from "node:os";
27
+ import { homedir, platform, tmpdir, userInfo } from "node:os";
28
28
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
29
29
  import chalk from "chalk";
30
30
  import { createHash, randomUUID } from "node:crypto";
@@ -161,7 +161,7 @@ async function cmdPromote(opts) {
161
161
  console.error(dim$1(` Pick a durable location for the new project.`));
162
162
  process.exit(1);
163
163
  }
164
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
164
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
165
165
  const registryBackend = await getRegistryBackend();
166
166
  const encodedDir = encodeDir(targetPath);
167
167
  if (await registryBackend.getProjectBySlug(slug) ?? await registryBackend.getProjectByRootPath(targetPath) ?? await registryBackend.getProjectByEncodedDir(encodedDir)) {
@@ -2035,7 +2035,7 @@ async function cmdUnregister(slug, opts = {}) {
2035
2035
  //#region src/cli/commands/project/projects-index.ts
2036
2036
  function registerProjectsCommands(projectsCmd) {
2037
2037
  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) => {
2038
- const { cmdMain } = await import("./main-resolver-Df65bPAi.mjs");
2038
+ const { cmdMain } = await import("./main-resolver-CCojcYr2.mjs");
2039
2039
  await cmdMain(void 0, void 0, opts);
2040
2040
  });
2041
2041
  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(async (opts) => {
@@ -3247,8 +3247,8 @@ function registerStatsCommands(memoryCmd) {
3247
3247
  //#region src/cli/commands/memory/sources-cmd.ts
3248
3248
  function registerSourcesCommand(memoryCmd) {
3249
3249
  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) => {
3250
- const { createStorageBackend } = await import("./factory-C9wGUe30.mjs");
3251
- const { loadConfig } = await import("./config-DCYWFaEi.mjs");
3250
+ const { createStorageBackend } = await import("./factory-D6I2jmjQ.mjs");
3251
+ const { loadConfig } = await import("./config-GceCMUxa.mjs");
3252
3252
  const { cmdMemorySources } = await import("./sources-BBe3AZ-W.mjs");
3253
3253
  let backend;
3254
3254
  try {
@@ -3949,8 +3949,8 @@ async function escalate(task, headline, detail, state, now, opts) {
3949
3949
  state.alarmedAt[task.id] = now;
3950
3950
  let delivered = false;
3951
3951
  try {
3952
- const { routeNotification } = await import("./router-BXOf_e9e.mjs");
3953
- const { loadConfig } = await import("./config-DCYWFaEi.mjs");
3952
+ const { routeNotification } = await import("./router-C5hPk9YP.mjs");
3953
+ const { loadConfig } = await import("./config-GceCMUxa.mjs");
3954
3954
  delivered = (await routeNotification({
3955
3955
  event: "error",
3956
3956
  title: `PAI: ${headline}`,
@@ -3963,6 +3963,35 @@ async function escalate(task, headline, detail, state, now, opts) {
3963
3963
  } catch {}
3964
3964
  }
3965
3965
  /**
3966
+ * One best-effort PAILot alert at the moment a task transitions into parked.
3967
+ *
3968
+ * Parking is silent from the phone's point of view: the tracker note and the
3969
+ * scheduler log both say PARKED, but the only person who reads either is the
3970
+ * one who already knows. On 2026-09-27 both morning sweeps were parked on a
3971
+ * FALSE unreachable (the hub misread a statusline) and nothing surfaced it
3972
+ * until the day's run simply did not happen — the exact failure parking's own
3973
+ * docs promise to prevent.
3974
+ *
3975
+ * Deliberately NOT rate-limited beyond its call sites: it runs exactly once
3976
+ * per parking event (on the transition, not on every poll while parked), and
3977
+ * a re-park after a release is a new event that deserves a new alert. The
3978
+ * escalate() quiet window is untouched — this is the announcement, that is
3979
+ * the daily reminder.
3980
+ *
3981
+ * Never allowed to throw or linger: the hub gets a short deadline, and an
3982
+ * unreachable hub falls back to the poller log (launchd sends both streams to
3983
+ * /tmp/pai-scheduler.log, which already carries every PARKED line).
3984
+ */
3985
+ async function alertParked(task, reason) {
3986
+ const text = `PAI parked "${task.title}" (${task.id}) — the scheduler will not retry it.\nReason: ${reason}\nTo release it: fix the cause, then move the due date — due_string, natural language only (e.g. "tomorrow 9am").`;
3987
+ try {
3988
+ const { callAiBroker } = await import("./aibroker-client-BYG-GtAg.mjs");
3989
+ await callAiBroker("pailot_send", { text }, 8e3);
3990
+ } catch (e) {
3991
+ console.error(`[pai-poller] parked-task alert not delivered to PAILot (${e instanceof Error ? e.message : String(e)}): ${text}`);
3992
+ }
3993
+ }
3994
+ /**
3966
3995
  * Run state is a rebuildable cache, so a damaged file must not block the
3967
3996
  * scheduler forever — starting fresh is the correct recovery here, which is
3968
3997
  * exactly the case json-store's guard is NOT for.
@@ -4242,6 +4271,7 @@ async function handleDispatch(task, overdue, opts, state, now) {
4242
4271
  at: now,
4243
4272
  due: task.due ?? void 0
4244
4273
  };
4274
+ await alertParked(task, reason);
4245
4275
  await escalate(task, "task is not running", `cannot be dispatched: its project directory ${task.owner.rootPath} does not exist (renamed or moved?). It is now PARKED and will not be retried. Repoint the project with \`pai project\` and move the task's due date to release it.`, state, now, opts);
4246
4276
  return {
4247
4277
  note: `PARKED — ${reason}`,
@@ -4280,6 +4310,7 @@ async function handleDispatch(task, overdue, opts, state, now) {
4280
4310
  at: now,
4281
4311
  due: task.due ?? void 0
4282
4312
  };
4313
+ await alertParked(task, detail);
4283
4314
  await escalate(task, "task is not running", `failed to dispatch ${permanent ? "" : `${fails} times `}(${detail}). It is scheduled and nothing is executing it. It is now PARKED and will not be retried until its due date moves — reschedule it once the cause is fixed.`, state, now, opts);
4284
4315
  return {
4285
4316
  note: `PARKED — ${permanent ? "permanent failure" : `${fails} failed dispatches`}: ${detail}`,
@@ -4695,8 +4726,8 @@ function registerBackupCommands(program) {
4695
4726
  if (existsSync(CONFIG_FILE$1)) {
4696
4727
  const dest = join(backupDir, "config.json");
4697
4728
  try {
4698
- const { copyFileSync } = await import("node:fs");
4699
4729
  copyFileSync(CONFIG_FILE$1, dest);
4730
+ chmodSync(dest, 384);
4700
4731
  results.push({
4701
4732
  label: "Config",
4702
4733
  path: dest,
@@ -6528,6 +6559,40 @@ function registerMcpCommands(mcpCmd) {
6528
6559
  });
6529
6560
  }
6530
6561
 
6562
+ //#endregion
6563
+ //#region src/module-paths.ts
6564
+ /**
6565
+ * module-paths.ts — locate a file relative to the package root from a running module.
6566
+ *
6567
+ * tsdown bundles shared code into chunks emitted directly under dist/ (e.g.
6568
+ * dist/program-*.mjs) rather than always alongside the entry that needs them
6569
+ * (dist/cli/index.mjs, dist/daemon/index.mjs, ...). A fixed "../daemon/index.mjs"
6570
+ * relative to import.meta.url assumes one specific chunk depth and silently
6571
+ * resolves to the wrong file — or a file under the source tree instead of
6572
+ * dist — whenever the bundler's chunking changes. Walking up from the running
6573
+ * module's own directory until `dir/rel` exists is depth-independent, and works
6574
+ * whether `rel` lives under dist/ (built entries) or is a sibling of dist/ at
6575
+ * the package root (files excluded from the bundle, like docker/migrate-sqlite.ts).
6576
+ */
6577
+ /**
6578
+ * Resolve `rel` by walking up from the directory containing `moduleUrl`,
6579
+ * returning the first `dir/rel` that exists on disk.
6580
+ *
6581
+ * Throws rather than returning a guessed path, so a caller writing a service
6582
+ * file or spawning a process fails loudly instead of pointing at nothing.
6583
+ */
6584
+ function resolveFromModule(moduleUrl, rel, maxLevels = 8) {
6585
+ let dir = dirname(fileURLToPath(moduleUrl));
6586
+ for (let i = 0; i < maxLevels; i++) {
6587
+ const candidate = join(dir, rel);
6588
+ if (existsSync(candidate)) return candidate;
6589
+ const parent = dirname(dir);
6590
+ if (parent === dir) break;
6591
+ dir = parent;
6592
+ }
6593
+ throw new Error(`cannot locate "${rel}" above ${dirname(fileURLToPath(moduleUrl))} — is the project built? (bun run build)`);
6594
+ }
6595
+
6531
6596
  //#endregion
6532
6597
  //#region src/cli/commands/daemon-status.ts
6533
6598
  /**
@@ -6594,20 +6659,21 @@ const PLIST_LABEL = "com.pai.pai-daemon";
6594
6659
  const LAUNCH_AGENTS_DIR = join(HOME$1, "Library", "LaunchAgents");
6595
6660
  const PLIST_PATH$1 = join(LAUNCH_AGENTS_DIR, `${PLIST_LABEL}.plist`);
6596
6661
  const DAEMON_LOG = daemonLogPath();
6662
+ const SYSTEMD_UNIT_NAME = "pai-daemon.service";
6663
+ const SYSTEMD_USER_DIR = join(HOME$1, ".config", "systemd", "user");
6664
+ const SYSTEMD_UNIT_PATH = join(SYSTEMD_USER_DIR, SYSTEMD_UNIT_NAME);
6597
6665
  /**
6598
6666
  * Resolve the absolute path to the built daemon entry point.
6599
- * tsdown bundles into dist/daemon/index.mjs (or similar).
6600
- * From dist/cli/index.mjs → dist/ → dist/daemon/index.mjs
6667
+ * tsdown may emit this module's own chunk directly under dist/ rather than
6668
+ * under dist/cli/, so a fixed "../daemon/index.mjs" is not reliable — walk up
6669
+ * from this module until dist/daemon/index.mjs is found instead.
6601
6670
  */
6602
6671
  function getDaemonBinPath() {
6603
- return join(dirname(fileURLToPath(import.meta.url)), "../daemon/index.mjs");
6672
+ return resolveFromModule(import.meta.url, "dist/daemon/index.mjs");
6604
6673
  }
6605
- /**
6606
- * Resolve the absolute path to the built MCP shim entry point.
6607
- * dist/cli/index.mjs → dist/ → dist/daemon-mcp/index.mjs
6608
- */
6674
+ /** Resolve the absolute path to the built MCP shim entry point. Same reasoning as getDaemonBinPath. */
6609
6675
  function getShimBinPath() {
6610
- return join(dirname(fileURLToPath(import.meta.url)), "../daemon-mcp/index.mjs");
6676
+ return resolveFromModule(import.meta.url, "dist/daemon-mcp/index.mjs");
6611
6677
  }
6612
6678
  function generatePlist(daemonBin) {
6613
6679
  return `<?xml version="1.0" encoding="UTF-8"?>
@@ -6648,6 +6714,33 @@ function generatePlist(daemonBin) {
6648
6714
  </plist>
6649
6715
  `;
6650
6716
  }
6717
+ function generateSystemdUnit(daemonBin, nodeBin) {
6718
+ return `[Unit]
6719
+ Description=PAI Daemon
6720
+ After=network.target
6721
+
6722
+ [Service]
6723
+ ExecStart=${nodeBin} ${daemonBin} serve
6724
+ Restart=on-failure
6725
+
6726
+ [Install]
6727
+ WantedBy=default.target
6728
+ `;
6729
+ }
6730
+ /** Warns (does not fail) when the user's systemd instance is not kept alive after logout. */
6731
+ function warnIfLingerDisabled() {
6732
+ const username = userInfo().username;
6733
+ const result = spawnSync("loginctl", [
6734
+ "show-user",
6735
+ username,
6736
+ "-p",
6737
+ "Linger"
6738
+ ], { encoding: "utf8" });
6739
+ if (result.status === 0 && result.stdout.trim() !== "Linger=yes") {
6740
+ console.log(warn$1(` Linger is off for ${username} — the daemon stops when you log out.`));
6741
+ console.log(dim$1(` Enable it: loginctl enable-linger ${username}`));
6742
+ }
6743
+ }
6651
6744
  async function cmdStatus$1() {
6652
6745
  const client = new PaiClient(loadConfig().socketPath);
6653
6746
  try {
@@ -6687,6 +6780,24 @@ async function cmdStatus$1() {
6687
6780
  }
6688
6781
  }
6689
6782
  function cmdRestart() {
6783
+ if (process.platform === "linux") {
6784
+ const result = spawnSync("systemctl", [
6785
+ "--user",
6786
+ "restart",
6787
+ SYSTEMD_UNIT_NAME
6788
+ ], { encoding: "utf8" });
6789
+ if (result.status === 0) console.log(ok$1("Restarted via systemd."));
6790
+ else {
6791
+ console.error(err(`systemctl restart failed: ${(result.stderr || "").trim()}`));
6792
+ process.exitCode = 1;
6793
+ }
6794
+ return;
6795
+ }
6796
+ if (process.platform !== "darwin") {
6797
+ console.error(err(`Unsupported platform: ${process.platform}. Daemon restart supports macOS (launchd) and Linux (systemd user) only.`));
6798
+ process.exitCode = 1;
6799
+ return;
6800
+ }
6690
6801
  try {
6691
6802
  const result = spawnSync("pgrep", ["-f", "pai-daemon.*serve"], { encoding: "utf8" });
6692
6803
  if (result.status !== 0 || !result.stdout.trim()) {
@@ -6713,12 +6824,7 @@ function cmdRestart() {
6713
6824
  process.exitCode = 1;
6714
6825
  }
6715
6826
  }
6716
- function cmdInstall() {
6717
- const daemonBin = getDaemonBinPath();
6718
- const shimBin = getShimBinPath();
6719
- console.log();
6720
- console.log(bold$1(" PAI Daemon Install"));
6721
- console.log();
6827
+ function installServiceDarwin(daemonBin) {
6722
6828
  if (!existsSync(LAUNCH_AGENTS_DIR)) mkdirSync(LAUNCH_AGENTS_DIR, { recursive: true });
6723
6829
  const plistContent = generatePlist(daemonBin);
6724
6830
  try {
@@ -6727,7 +6833,7 @@ function cmdInstall() {
6727
6833
  } catch (e) {
6728
6834
  console.error(err(` Failed to write plist: ${e}`));
6729
6835
  process.exitCode = 1;
6730
- return;
6836
+ return false;
6731
6837
  }
6732
6838
  try {
6733
6839
  spawnSync("launchctl", ["unload", PLIST_PATH$1], { encoding: "utf8" });
@@ -6738,6 +6844,46 @@ function cmdInstall() {
6738
6844
  console.log(warn$1(" Could not run launchctl. Load manually:"));
6739
6845
  console.log(dim$1(` launchctl load ${PLIST_PATH$1}`));
6740
6846
  }
6847
+ return true;
6848
+ }
6849
+ function installServiceLinux(daemonBin) {
6850
+ if (!existsSync(SYSTEMD_USER_DIR)) mkdirSync(SYSTEMD_USER_DIR, { recursive: true });
6851
+ const unitContent = generateSystemdUnit(daemonBin, process.execPath);
6852
+ try {
6853
+ writeFileSync(SYSTEMD_UNIT_PATH, unitContent, "utf8");
6854
+ console.log(ok$1(` Wrote systemd unit: ${SYSTEMD_UNIT_PATH}`));
6855
+ } catch (e) {
6856
+ console.error(err(` Failed to write systemd unit: ${e}`));
6857
+ process.exitCode = 1;
6858
+ return false;
6859
+ }
6860
+ spawnSync("systemctl", ["--user", "daemon-reload"], { encoding: "utf8" });
6861
+ const enableResult = spawnSync("systemctl", [
6862
+ "--user",
6863
+ "enable",
6864
+ "--now",
6865
+ SYSTEMD_UNIT_NAME
6866
+ ], { encoding: "utf8" });
6867
+ if (enableResult.status === 0) console.log(ok$1(" Enabled and started via systemd."));
6868
+ else console.log(warn$1(` systemctl enable --now: ${(enableResult.stderr || "").trim()}`));
6869
+ warnIfLingerDisabled();
6870
+ return true;
6871
+ }
6872
+ function cmdInstall() {
6873
+ const daemonBin = getDaemonBinPath();
6874
+ const shimBin = getShimBinPath();
6875
+ console.log();
6876
+ console.log(bold$1(" PAI Daemon Install"));
6877
+ console.log();
6878
+ if (process.platform === "darwin") {
6879
+ if (!installServiceDarwin(daemonBin)) return;
6880
+ } else if (process.platform === "linux") {
6881
+ if (!installServiceLinux(daemonBin)) return;
6882
+ } else {
6883
+ console.error(err(` Unsupported platform: ${process.platform}. Daemon install supports macOS (launchd) and Linux (systemd user) only.`));
6884
+ process.exitCode = 1;
6885
+ return;
6886
+ }
6741
6887
  const config = readClaudeJson();
6742
6888
  if (typeof config.mcpServers !== "object" || config.mcpServers === null) config.mcpServers = {};
6743
6889
  const servers = config.mcpServers;
@@ -6774,10 +6920,7 @@ function cmdInstall() {
6774
6920
  console.log(dim$1(" Restart Claude Code to activate the daemon-backed PAI tools."));
6775
6921
  console.log();
6776
6922
  }
6777
- function cmdUninstall() {
6778
- console.log();
6779
- console.log(bold$1(" PAI Daemon Uninstall"));
6780
- console.log();
6923
+ function uninstallServiceDarwin() {
6781
6924
  if (existsSync(PLIST_PATH$1)) {
6782
6925
  try {
6783
6926
  spawnSync("launchctl", ["unload", PLIST_PATH$1], { encoding: "utf8" });
@@ -6792,6 +6935,36 @@ function cmdUninstall() {
6792
6935
  console.log(warn$1(` Could not remove plist: ${e}`));
6793
6936
  }
6794
6937
  } else console.log(dim$1(" No launchd plist found."));
6938
+ }
6939
+ function uninstallServiceLinux() {
6940
+ if (existsSync(SYSTEMD_UNIT_PATH)) {
6941
+ spawnSync("systemctl", [
6942
+ "--user",
6943
+ "disable",
6944
+ "--now",
6945
+ SYSTEMD_UNIT_NAME
6946
+ ], { encoding: "utf8" });
6947
+ console.log(ok$1(" Disabled and stopped via systemd."));
6948
+ try {
6949
+ unlinkSync(SYSTEMD_UNIT_PATH);
6950
+ console.log(ok$1(` Removed unit: ${SYSTEMD_UNIT_PATH}`));
6951
+ } catch (e) {
6952
+ console.log(warn$1(` Could not remove unit: ${e}`));
6953
+ }
6954
+ spawnSync("systemctl", ["--user", "daemon-reload"], { encoding: "utf8" });
6955
+ } else console.log(dim$1(" No systemd unit found."));
6956
+ }
6957
+ function cmdUninstall() {
6958
+ console.log();
6959
+ console.log(bold$1(" PAI Daemon Uninstall"));
6960
+ console.log();
6961
+ if (process.platform === "darwin") uninstallServiceDarwin();
6962
+ else if (process.platform === "linux") uninstallServiceLinux();
6963
+ else {
6964
+ console.error(err(` Unsupported platform: ${process.platform}. Daemon uninstall supports macOS (launchd) and Linux (systemd user) only.`));
6965
+ process.exitCode = 1;
6966
+ return;
6967
+ }
6795
6968
  const config = readClaudeJson();
6796
6969
  const servers = typeof config.mcpServers === "object" && config.mcpServers !== null ? config.mcpServers : {};
6797
6970
  const legacy = servers["pai-legacy"];
@@ -6815,9 +6988,7 @@ async function cmdMigrate(connectionString) {
6815
6988
  console.log();
6816
6989
  console.log(dim$1(" Running SQLite → PostgreSQL migration..."));
6817
6990
  console.log();
6818
- const { fileURLToPath } = await import("node:url");
6819
- const { dirname: pathDirname, join: pathJoin } = await import("node:path");
6820
- const migrationScript = pathJoin(pathDirname(fileURLToPath(import.meta.url)), "../..", "docker", "migrate-sqlite.ts");
6991
+ const migrationScript = resolveFromModule(import.meta.url, "docker/migrate-sqlite.ts");
6821
6992
  const { spawnSync: spawn } = await import("node:child_process");
6822
6993
  const spawnArgs = ["tsx", migrationScript];
6823
6994
  if (connectionString) spawnArgs.push("--connection-string", connectionString);
@@ -6879,8 +7050,8 @@ function cmdLogs(opts) {
6879
7050
  }
6880
7051
  function registerDaemonCommands(daemonCmd) {
6881
7052
  daemonCmd.command("serve").description("Start the PAI daemon in the foreground").action(async () => {
6882
- const { serve } = await import("./daemon-B6hx1Ysz.mjs");
6883
- const { loadConfig: lc, ensureConfigDir } = await import("./config-DCYWFaEi.mjs");
7053
+ const { serve } = await import("./daemon-BppwJtrH.mjs");
7054
+ const { loadConfig: lc, ensureConfigDir } = await import("./config-GceCMUxa.mjs");
6884
7055
  ensureConfigDir();
6885
7056
  await serve(lc());
6886
7057
  });
@@ -6890,13 +7061,13 @@ function registerDaemonCommands(daemonCmd) {
6890
7061
  daemonCmd.command("status").description("Query the running daemon status").action(async () => {
6891
7062
  await cmdStatus$1();
6892
7063
  });
6893
- daemonCmd.command("restart").description("Send SIGTERM to the running daemon (launchd will restart it)").action(() => {
7064
+ daemonCmd.command("restart").description("Restart the running daemon (launchd on macOS, systemd on Linux)").action(() => {
6894
7065
  cmdRestart();
6895
7066
  });
6896
- daemonCmd.command("install").description("Install daemon as a launchd service and update ~/.claude.json to use the shim").action(() => {
7067
+ daemonCmd.command("install").description("Install daemon as a service (launchd/systemd) and update ~/.claude.json to use the shim").action(() => {
6897
7068
  cmdInstall();
6898
7069
  });
6899
- daemonCmd.command("uninstall").description("Remove the launchd service and revert to direct MCP").action(() => {
7070
+ daemonCmd.command("uninstall").description("Remove the daemon service (launchd/systemd) and revert to direct MCP").action(() => {
6900
7071
  cmdUninstall();
6901
7072
  });
6902
7073
  daemonCmd.command("keepalive").description("Show interactive-session cache keepalive config, per-session beat counters, and recent ledger lines").action(() => {
@@ -7039,6 +7210,7 @@ function registerRestoreCommands(program) {
7039
7210
  if (hasConfig) try {
7040
7211
  mkdirSync(dirname(CONFIG_FILE), { recursive: true });
7041
7212
  copyFileSync(join(resolvedDir, "config.json"), CONFIG_FILE);
7213
+ chmodSync(CONFIG_FILE, 384);
7042
7214
  results.push({
7043
7215
  label: "Config",
7044
7216
  status: ok$1("restored")
@@ -14347,6 +14519,12 @@ function generateSchedulePlist(intervalSecs, cli) {
14347
14519
  `;
14348
14520
  }
14349
14521
  function installSchedule(intervalSecs = DEFAULT_INTERVAL_SECS) {
14522
+ if (process.platform !== "darwin") return {
14523
+ plistPath: SCHEDULE_PLIST,
14524
+ intervalSecs,
14525
+ loaded: false,
14526
+ message: "scheduler install supports launchd (macOS) only"
14527
+ };
14350
14528
  if (!existsSync(LAUNCH_AGENTS)) mkdirSync(LAUNCH_AGENTS, { recursive: true });
14351
14529
  writeFileSync(SCHEDULE_PLIST, generateSchedulePlist(intervalSecs, cliPath()), "utf8");
14352
14530
  spawnSync("launchctl", ["unload", SCHEDULE_PLIST], { encoding: "utf8" });
@@ -16435,7 +16613,7 @@ async function getSessionTags(registryBackend, sessionId) {
16435
16613
  * Session CRUD commands: list, info, rename, slug, tag, route, active, auto-route.
16436
16614
  */
16437
16615
  async function cmdList(projectSlug, opts) {
16438
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
16616
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
16439
16617
  const registryBackend = await getRegistryBackend();
16440
16618
  const limit = parseInt(opts.limit ?? "20", 10);
16441
16619
  let project;
@@ -16500,7 +16678,7 @@ async function cmdList(projectSlug, opts) {
16500
16678
  console.log(dim$1(` ${rows.length} session(s) shown (limit: ${limit})`));
16501
16679
  }
16502
16680
  async function cmdInfo(projectSlug, sessionNumber) {
16503
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
16681
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
16504
16682
  const registryBackend = await getRegistryBackend();
16505
16683
  const project = await getProject$1(registryBackend, projectSlug);
16506
16684
  if (!project) {
@@ -16524,7 +16702,7 @@ async function cmdInfo(projectSlug, sessionNumber) {
16524
16702
  console.log();
16525
16703
  }
16526
16704
  async function cmdRename(projectSlug, numberOrLatest, newSlug) {
16527
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
16705
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
16528
16706
  await cmdRenameWith(await getRegistryBackend(), projectSlug, numberOrLatest, newSlug);
16529
16707
  }
16530
16708
  async function cmdRenameWith(registryBackend, projectSlug, numberOrLatest, newSlug) {
@@ -16587,7 +16765,7 @@ async function cmdRenameWith(registryBackend, projectSlug, numberOrLatest, newSl
16587
16765
  console.log();
16588
16766
  }
16589
16767
  async function cmdSlug(projectSlug, numberOrLatest, opts) {
16590
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
16768
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
16591
16769
  const registryBackend = await getRegistryBackend();
16592
16770
  const project = await getProject$1(registryBackend, projectSlug);
16593
16771
  if (!project) {
@@ -16617,7 +16795,7 @@ async function cmdSlug(projectSlug, numberOrLatest, opts) {
16617
16795
  }
16618
16796
  }
16619
16797
  async function cmdTag(projectSlug, sessionNumber, rawTags) {
16620
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
16798
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
16621
16799
  const registryBackend = await getRegistryBackend();
16622
16800
  const project = await getProject$1(registryBackend, projectSlug);
16623
16801
  if (!project) {
@@ -16657,7 +16835,7 @@ async function cmdTag(projectSlug, sessionNumber, rawTags) {
16657
16835
  console.log();
16658
16836
  }
16659
16837
  async function cmdRoute(projectSlug, sessionNumber, targetProjectSlug, opts) {
16660
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
16838
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
16661
16839
  const registryBackend = await getRegistryBackend();
16662
16840
  const project = await getProject$1(registryBackend, projectSlug);
16663
16841
  if (!project) {
@@ -16699,7 +16877,7 @@ async function cmdRoute(projectSlug, sessionNumber, targetProjectSlug, opts) {
16699
16877
  console.log();
16700
16878
  }
16701
16879
  async function cmdActive(opts) {
16702
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
16880
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
16703
16881
  const registryBackend = await getRegistryBackend();
16704
16882
  const minutes = parseInt(opts.minutes ?? "60", 10);
16705
16883
  const cutoff = Date.now() - minutes * 60 * 1e3;
@@ -16787,8 +16965,8 @@ async function cmdActive(opts) {
16787
16965
  }
16788
16966
  async function cmdAutoRoute(opts) {
16789
16967
  const { autoRoute, formatAutoRouteJson } = await import("./auto-route-CF8WNaoy.mjs");
16790
- const { getRegistryBackend, createStorageBackend } = await import("./factory-C9wGUe30.mjs");
16791
- const { loadConfig } = await import("./config-DCYWFaEi.mjs");
16968
+ const { getRegistryBackend, createStorageBackend } = await import("./factory-D6I2jmjQ.mjs");
16969
+ const { loadConfig } = await import("./config-GceCMUxa.mjs");
16792
16970
  const config = loadConfig();
16793
16971
  const registryBackend = await getRegistryBackend();
16794
16972
  const federation = await createStorageBackend(config);
@@ -17024,7 +17202,7 @@ async function cmdPause(opts) {
17024
17202
  process.exitCode = 1;
17025
17203
  return;
17026
17204
  }
17027
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
17205
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
17028
17206
  const registryBackend = await getRegistryBackend();
17029
17207
  const cwd = process.cwd();
17030
17208
  const project = await resolveProjectByCwd(registryBackend, cwd);
@@ -17419,7 +17597,7 @@ function buildStopBody(project, sessionId) {
17419
17597
  }
17420
17598
  }
17421
17599
  async function cmdHandover(projectSlug, numberOrLatest, sessionId) {
17422
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
17600
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
17423
17601
  const registryBackend = await getRegistryBackend();
17424
17602
  let project;
17425
17603
  if (projectSlug) {
@@ -17715,7 +17893,7 @@ function touchSentinel(rootPath) {
17715
17893
  async function cmdAutosave(opts) {
17716
17894
  const minGap = parseInt(opts.minGap ?? String(DEFAULT_MIN_GAP), 10);
17717
17895
  const cwd = process.cwd();
17718
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
17896
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
17719
17897
  const registryBackend = await getRegistryBackend();
17720
17898
  const project = await resolveProjectByCwd(registryBackend, cwd);
17721
17899
  if (!project) return;
@@ -17904,23 +18082,24 @@ async function cmdRecent(opts) {
17904
18082
  * (see probeResume in lib/launch.ts — the filesystem answers,
17905
18083
  * and only an unrecognised layout falls back to spawning claude)
17906
18084
  * b. If probe succeeds:
17907
- * exec claude --resume <uuid> --name "<friendlyName>" "/Name <friendlyName>\ngo"
18085
+ * exec claude --resume <uuid> --name "<friendlyName>" "/Name <friendlyName>"
17908
18086
  * — through the provider the transcript ran on, when its model
17909
18087
  * matches one (providerResumePlan in lib/launch.ts)
17910
18088
  * c. Else (probe failed):
17911
18089
  * print clear stderr line: "Resume failed. Starting fresh session in same dir."
17912
- * exec claude --name "<friendlyName>" "/Name <friendlyName>\ngo"
18090
+ * exec claude --name "<friendlyName>" "/Name <friendlyName>"
17913
18091
  * 4. Else (no uuid known):
17914
- * exec claude --name "<friendlyName>" "/Name <friendlyName>\ngo"
18092
+ * exec claude --name "<friendlyName>" "/Name <friendlyName>"
17915
18093
  *
17916
18094
  * Steps 3 and 4 are launchInDir's, shared — this file no longer carries its
17917
18095
  * own copy of the spawn dance.
17918
18096
  *
17919
18097
  * Why both --name AND /Name?
17920
18098
  * --name <friendlyName> → sets Claude Code's internal session label
17921
- * "/Name <friendlyName>\ngo" → runs the /Name slash command via AIBroker, which updates
17922
- * iTerm tab title, statusline, and AIBroker session registry;
17923
- * the \ngo on the next line triggers PAI's ## Continue resume
18099
+ * "/Name <friendlyName>" → runs the /Name slash command via AIBroker, which updates
18100
+ * iTerm tab title, statusline, and AIBroker session registry.
18101
+ * No trailing `go`: the operator types it by hand when the
18102
+ * TODO.md handover is wanted
17924
18103
  *
17925
18104
  * The probe is two `existsSync` calls on the happy path. On the fallback path, total latency
17926
18105
  * is the same as a fresh start.
@@ -17968,7 +18147,7 @@ async function cmdGoto(query, opts) {
17968
18147
  if (resumableUuid) {
17969
18148
  const plan = providerResumePlan(resumableUuid, projectDir);
17970
18149
  const argvResume = resumeArgvText(resumableUuid, name, plan);
17971
- const argvFresh = `claude --name "${name}" "/Name ${name}\\ngo"`;
18150
+ const argvFresh = `claude --name "${name}" "${launchPrompt(name)}"`;
17972
18151
  console.log("\n" + chalk.bold("Dry run — would probe then exec (RESUME path):") + "\n");
17973
18152
  console.log(` cwd: ${chalk.cyan(projectDir)}`);
17974
18153
  console.log(` probe: claude --resume ${resumableUuid} --print --output-format=json "_"`);
@@ -17982,7 +18161,7 @@ async function cmdGoto(query, opts) {
17982
18161
  console.log(` sys: ${resumableSession.topLevelSystemLines} system lines`);
17983
18162
  }
17984
18163
  } else {
17985
- const argvFresh = `claude --name "${name}" "/Name ${name}\\ngo"`;
18164
+ const argvFresh = `claude --name "${name}" "${launchPrompt(name)}"`;
17986
18165
  console.log("\n" + chalk.bold("Dry run — would exec (FRESH path, no resumable UUID):") + "\n");
17987
18166
  console.log(` cwd: ${chalk.cyan(projectDir)}`);
17988
18167
  console.log(` argv: ${chalk.white(argvFresh)}`);
@@ -18250,7 +18429,7 @@ function registerSessionCommands(sessionCmd) {
18250
18429
  process.stderr.write("Deprecated: `pai session recent` — use `pai session list` or `pai sessions` instead.\n");
18251
18430
  await cmdRecent(opts);
18252
18431
  });
18253
- sessionCmd.command("goto <name-or-id>").description("Go to a session: resume if a resumable snapshot exists, start fresh otherwise.\nRecommended short form: pai resume <name>\nResolves by clc/registry name (case-insensitive) or UUID prefix.\nSends '/Name <name>\\ngo' as the initial prompt so the PAI ## Continue hook fires.").option("--skip-name", "Do not prepend /Name to set the session name in chrome").option("--skip-go", "Do not append \\ngo to trigger PAI auto-resume (## Continue)").option("--dry-run", "Print the exact argv and cwd, then exit without launching").action(async (nameOrId, opts) => {
18432
+ sessionCmd.command("goto <name-or-id>").description("Go to a session: resume if a resumable snapshot exists, start fresh otherwise.\nRecommended short form: pai resume <name>\nResolves by clc/registry name (case-insensitive) or UUID prefix.\nSends '/Name <name>' as the initial prompt to label the session.").option("--skip-name", "Do not prepend /Name to set the session name in chrome").option("--skip-go", "Deprecated no-op: 'go' is no longer appended — type it by hand when wanted").option("--dry-run", "Print the exact argv and cwd, then exit without launching").action(async (nameOrId, opts) => {
18254
18433
  process.stderr.write("Note: `pai session goto` works but `pai resume` is the new shorter form.\n");
18255
18434
  await cmdGoto(nameOrId, {
18256
18435
  noName: opts.skipName,
@@ -18535,10 +18714,10 @@ function registerWorkerProviderCommands(providersCmd) {
18535
18714
  fail$4(e);
18536
18715
  }
18537
18716
  });
18538
- providersCmd.command("use <name>").description("Make this provider the active one for runs without --provider/--class").action((name) => {
18717
+ providersCmd.command("use <name>").description("Make this provider the active one, re-pointing every class pinned to a provider").action((name) => {
18539
18718
  try {
18540
- useProvider(name);
18541
- console.log(ok$1(`active provider: ${name}`));
18719
+ const { repointed } = useProvider(name);
18720
+ console.log(ok$1(`active provider: ${name}${repointed ? `; ${repointed} class(es) re-pointed` : ""}`));
18542
18721
  } catch (e) {
18543
18722
  fail$4(e);
18544
18723
  }
@@ -19153,27 +19332,32 @@ function registerWorkerCommands(workerCmd) {
19153
19332
  fail(e);
19154
19333
  }
19155
19334
  });
19156
- workerCmd.command("controls <id> <who>").description("Hand the desktop controls (clickr) to a worker or take them back.\n<who> is `you` (the worker may actuate) or `me` (the operator keeps them);\ninside a worker's pane, typing \"your controls\" does the same.").action((id, who) => {
19335
+ workerCmd.command("controls <id> <who>").description("Hand the desktop controls (clickr) to a worker or take them back.\n<who> is `you` (<id> becomes the exclusive controls holder; needs clickr with\n--agent support) or `me` (the operator takes them back);\ninside a worker's pane, typing \"your controls\" does the same.").action((id, who) => {
19157
19336
  try {
19158
19337
  if (who !== "you" && who !== "me") {
19159
19338
  fail(/* @__PURE__ */ new Error(`<who> must be "you" or "me"`));
19160
19339
  return;
19161
19340
  }
19162
- if (!loadStatus(currentLogDir(), id)) {
19341
+ const logDir = currentLogDir();
19342
+ const status = loadStatus(logDir, id);
19343
+ if (!status) {
19163
19344
  fail(/* @__PURE__ */ new Error(`no worker named "${id}"`));
19164
19345
  return;
19165
19346
  }
19166
- const proc = spawn("clickr", ["controls", who], { stdio: "inherit" });
19347
+ const proc = spawn("clickr", clickrControlsArgv(id, who, status.pid), { stdio: "inherit" });
19167
19348
  proc.on("error", (e) => fail(/* @__PURE__ */ new Error(`cannot run clickr controls: ${e.message} (is clickr installed?)`)));
19168
19349
  proc.on("close", (code) => {
19169
- if (code === 0) console.log(`controls → ${who === "you" ? id : "operator"}`);
19350
+ if (code === 0) {
19351
+ if (who === "you") markControlsHeld(logDir, id);
19352
+ console.log(`controls → ${who === "you" ? `${id} (exclusive; needs clickr with --agent support)` : "operator"}`);
19353
+ }
19170
19354
  process.exitCode = code ?? 1;
19171
19355
  });
19172
19356
  } catch (e) {
19173
19357
  fail(e);
19174
19358
  }
19175
19359
  });
19176
- workerCmd.command("resume <id> <text>").description("Continue a finished worker on the same provider: claude --resume <session>").option("--print-id", "Print the new worker id on its own line (pane follow handoff)").option("--no-pane", "Do not open a follow pane for the resumed worker").action(async (id, text, opts) => {
19360
+ workerCmd.command("resume <id> <text>").description("Continue a finished worker on the same provider: claude --resume <session>").option("--print-id", "Print the new worker id on its own line (pane follow handoff)").option("--no-pane", "Do not open a follow pane for the resumed worker").option("--output-format <fmt>", "text (default), json or stream-json — same as `run`").option("--label <text>", "Short task label (default: \"↩ <old label>\")").action(async (id, text, opts) => {
19177
19361
  try {
19178
19362
  const old = loadStatus(currentLogDir(), id);
19179
19363
  if (!old) {
@@ -19184,18 +19368,20 @@ function registerWorkerCommands(workerCmd) {
19184
19368
  fail(/* @__PURE__ */ new Error(`worker ${id} recorded no Claude session id — it predates resume support or ran through an engine that does not expose one`));
19185
19369
  return;
19186
19370
  }
19371
+ const claudeArgs = [
19372
+ "--resume",
19373
+ old.claudeSession,
19374
+ "-p",
19375
+ text
19376
+ ];
19377
+ if (opts.outputFormat !== void 0) claudeArgs.push("--output-format", opts.outputFormat);
19187
19378
  let newId = "";
19188
19379
  const rc = await runWorker({
19189
19380
  providerFlag: old.provider,
19190
19381
  modelFlag: old.model,
19191
- label: `↩ ${old.label}`,
19382
+ label: opts.label || `↩ ${old.label}`,
19192
19383
  noPane: opts.pane === false,
19193
- claudeArgs: [
19194
- "--resume",
19195
- old.claudeSession,
19196
- "-p",
19197
- text
19198
- ],
19384
+ claudeArgs,
19199
19385
  onWorkerStart: (wid) => {
19200
19386
  newId = wid;
19201
19387
  }
@@ -19983,8 +20169,8 @@ async function analyzeProject(registryBackend, project) {
19983
20169
  async function countVectorDbPaths(oldPaths) {
19984
20170
  if (oldPaths.length === 0) return 0;
19985
20171
  try {
19986
- const { loadConfig } = await import("./config-DCYWFaEi.mjs");
19987
- const { createStorageBackend } = await import("./factory-C9wGUe30.mjs");
20172
+ const { loadConfig } = await import("./config-GceCMUxa.mjs");
20173
+ const { createStorageBackend } = await import("./factory-D6I2jmjQ.mjs");
19988
20174
  const config = loadConfig();
19989
20175
  if (config.storageBackend !== "postgres") return 0;
19990
20176
  const backend = await createStorageBackend(config);
@@ -20001,8 +20187,8 @@ async function countVectorDbPaths(oldPaths) {
20001
20187
  async function updateVectorDbPaths(moves) {
20002
20188
  if (moves.length === 0) return 0;
20003
20189
  try {
20004
- const { loadConfig } = await import("./config-DCYWFaEi.mjs");
20005
- const { createStorageBackend } = await import("./factory-C9wGUe30.mjs");
20190
+ const { loadConfig } = await import("./config-GceCMUxa.mjs");
20191
+ const { createStorageBackend } = await import("./factory-D6I2jmjQ.mjs");
20006
20192
  const config = loadConfig();
20007
20193
  if (config.storageBackend !== "postgres") return 0;
20008
20194
  const backend = await createStorageBackend(config);
@@ -20089,7 +20275,7 @@ async function displayDryRun(plans) {
20089
20275
  console.log();
20090
20276
  }
20091
20277
  async function executeCleanup(plans, skipReindex) {
20092
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
20278
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
20093
20279
  const registryBackend = await getRegistryBackend();
20094
20280
  let deleted = 0;
20095
20281
  let renamed = 0;
@@ -20286,7 +20472,7 @@ async function executeCleanup(plans, skipReindex) {
20286
20472
  //#region src/cli/commands/session-cleanup/index.ts
20287
20473
  function registerSessionCleanupCommand(sessionCmd) {
20288
20474
  sessionCmd.command("cleanup [project-slug]").description("Clean up session notes: delete empties, auto-name unnamed, move into YYYY/MM/ hierarchy, renumber").option("--execute", "Actually perform the cleanup (default is dry-run)").option("--no-renumber", "Skip renumbering sessions after deletions").option("--no-reindex", "Skip triggering memory re-index after moves").action(async (projectSlug, opts) => {
20289
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
20475
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
20290
20476
  const registryBackend = await getRegistryBackend();
20291
20477
  const dryRun = !opts.execute;
20292
20478
  const skipReindex = opts.reindex === false;
@@ -20356,7 +20542,7 @@ function finalizeNote(notePath) {
20356
20542
  }
20357
20543
  async function cmdEnd(opts) {
20358
20544
  await cmdPause(opts);
20359
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
20545
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
20360
20546
  const registryBackend = await getRegistryBackend();
20361
20547
  const cwd = process.cwd();
20362
20548
  const project = await registryBackend.findProjectByCwdPrefix(cwd);
@@ -20804,7 +20990,7 @@ async function cmdFind(query, opts) {
20804
20990
  */
20805
20991
  async function getProjects(includeArchived) {
20806
20992
  try {
20807
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
20993
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
20808
20994
  const rows = await (await getRegistryBackend()).listProjectsWithSessionStats();
20809
20995
  return includeArchived ? rows : rows.filter((p) => p.status === "active");
20810
20996
  } catch {
@@ -21278,7 +21464,7 @@ async function cmdPick(opts = {}) {
21278
21464
  const onRemove = async (rec) => {
21279
21465
  if (!rec.slug) return false;
21280
21466
  try {
21281
- const { getRegistryBackend } = await import("./factory-C9wGUe30.mjs");
21467
+ const { getRegistryBackend } = await import("./factory-D6I2jmjQ.mjs");
21282
21468
  const registryBackend = await getRegistryBackend();
21283
21469
  const project = await registryBackend.getProjectBySlug(rec.slug);
21284
21470
  if (!project || project.status !== "active") return false;
@@ -21563,4 +21749,4 @@ claude() {
21563
21749
 
21564
21750
  //#endregion
21565
21751
  export { drainStdio as n, buildProgram as t };
21566
- //# sourceMappingURL=program-B5miXqQa.mjs.map
21752
+ //# sourceMappingURL=program-QSuci3RH.mjs.map