@tekmidian/pai 0.64.0 → 0.65.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/README.md +60 -1051
  2. package/dist/{aibroker-client-BpfuGPe5.mjs → aibroker-client-B8c42Lh8.mjs} +1 -1
  3. package/dist/{aibroker-client-DRld7g6z.mjs → aibroker-client-C5Fw7DNz.mjs} +10 -4
  4. package/dist/{aibroker-client-DRld7g6z.mjs.map → aibroker-client-C5Fw7DNz.mjs.map} +1 -1
  5. package/dist/{async-CY_cd_8j.mjs → async-CNn36gm4.mjs} +2 -2
  6. package/dist/{async-CY_cd_8j.mjs.map → async-CNn36gm4.mjs.map} +1 -1
  7. package/dist/{auto-route-CPlam4iv.mjs → auto-route-sLMU-NnM.mjs} +2 -2
  8. package/dist/{auto-route-CPlam4iv.mjs.map → auto-route-sLMU-NnM.mjs.map} +1 -1
  9. package/dist/{chain-o-g8AgE3.mjs → chain-CLb6hbFS.mjs} +4 -4
  10. package/dist/{chain-o-g8AgE3.mjs.map → chain-CLb6hbFS.mjs.map} +1 -1
  11. package/dist/cli/index.mjs +19 -19
  12. package/dist/cli/program.d.mts.map +1 -1
  13. package/dist/cli/program.mjs +19 -19
  14. package/dist/{clusters-DsMf20PP.mjs → clusters-PgIUYT_v.mjs} +1 -1
  15. package/dist/{clusters-DsMf20PP.mjs.map → clusters-PgIUYT_v.mjs.map} +1 -1
  16. package/dist/{config-2wkz744W.mjs → config-BbLFD7Uf.mjs} +3 -3
  17. package/dist/config-BbLFD7Uf.mjs.map +1 -0
  18. package/dist/{config-CUTg6VDq.mjs → config-YinjgXEJ.mjs} +1 -1
  19. package/dist/{context-handover-cache-BOpjLsKe.mjs → context-handover-cache-mGxq2-8f.mjs} +11 -29
  20. package/dist/context-handover-cache-mGxq2-8f.mjs.map +1 -0
  21. package/dist/daemon/index.mjs +16 -16
  22. package/dist/daemon-BjaPR39W.mjs +19 -0
  23. package/dist/{daemon-BYieXduQ.mjs → daemon-DqCB3fO-.mjs} +30 -30
  24. package/dist/{daemon-BYieXduQ.mjs.map → daemon-DqCB3fO-.mjs.map} +1 -1
  25. package/dist/daemon-mcp/index.mjs +18 -18
  26. package/dist/daemon-mcp/index.mjs.map +1 -1
  27. package/dist/detector-C3Q7mxQU.mjs +3 -0
  28. package/dist/{detector-BAlmrLQ0.mjs → detector-CIHEXKsV.mjs} +1 -1
  29. package/dist/{detector-BAlmrLQ0.mjs.map → detector-CIHEXKsV.mjs.map} +1 -1
  30. package/dist/{embeddings-DK9XfQic.mjs → embeddings-BbNVXa_0.mjs} +1 -1
  31. package/dist/{embeddings-DK9XfQic.mjs.map → embeddings-BbNVXa_0.mjs.map} +1 -1
  32. package/dist/{embeddings-Xg8XLf2K.mjs → embeddings-CDfM63uC.mjs} +1 -1
  33. package/dist/{factory-BMK0tC1b.mjs → factory-CaswxuJ0.mjs} +1 -1
  34. package/dist/{factory-C4We3xR_.mjs → factory-y36qGegI.mjs} +13 -13
  35. package/dist/{factory-C4We3xR_.mjs.map → factory-y36qGegI.mjs.map} +1 -1
  36. package/dist/{fallback-ECiqCuh6.mjs → fallback-CupzGkuJ.mjs} +1205 -1204
  37. package/dist/fallback-CupzGkuJ.mjs.map +1 -0
  38. package/dist/{federation-db-CUgy0WYs.mjs → federation-db-BTyoufBh.mjs} +2 -2
  39. package/dist/{federation-db-CUgy0WYs.mjs.map → federation-db-BTyoufBh.mjs.map} +1 -1
  40. package/dist/federation-db-HfIFc7FG.mjs +3 -0
  41. package/dist/hooks/block-sleep-poll.mjs +36 -11
  42. package/dist/hooks/block-sleep-poll.mjs.map +2 -2
  43. package/dist/hooks/capture-all-events.mjs +3 -17
  44. package/dist/hooks/capture-all-events.mjs.map +3 -3
  45. package/dist/hooks/capture-session-summary.mjs +3 -17
  46. package/dist/hooks/capture-session-summary.mjs.map +3 -3
  47. package/dist/hooks/capture-tool-output.mjs +3 -17
  48. package/dist/hooks/capture-tool-output.mjs.map +3 -3
  49. package/dist/hooks/cleanup-session-files.mjs +3 -17
  50. package/dist/hooks/cleanup-session-files.mjs.map +2 -2
  51. package/dist/hooks/context-compression-hook.mjs +3 -17
  52. package/dist/hooks/context-compression-hook.mjs.map +3 -3
  53. package/dist/hooks/initialize-session.mjs +3 -17
  54. package/dist/hooks/initialize-session.mjs.map +2 -2
  55. package/dist/hooks/inject-observations.mjs +3 -17
  56. package/dist/hooks/inject-observations.mjs.map +2 -2
  57. package/dist/hooks/load-core-context.mjs +4 -22
  58. package/dist/hooks/load-core-context.mjs.map +2 -2
  59. package/dist/hooks/load-project-context.mjs +3 -17
  60. package/dist/hooks/load-project-context.mjs.map +3 -3
  61. package/dist/hooks/mcp-deferred-gate.mjs +3 -17
  62. package/dist/hooks/mcp-deferred-gate.mjs.map +2 -2
  63. package/dist/hooks/observe.mjs +3 -17
  64. package/dist/hooks/observe.mjs.map +2 -2
  65. package/dist/hooks/post-compact-inject.mjs.map +1 -1
  66. package/dist/hooks/route-agents-to-worker.mjs.map +1 -1
  67. package/dist/hooks/security-validator.mjs +5 -19
  68. package/dist/hooks/security-validator.mjs.map +3 -3
  69. package/dist/hooks/stop-hook.mjs +3 -17
  70. package/dist/hooks/stop-hook.mjs.map +3 -3
  71. package/dist/hooks/sync-todo-to-md.mjs +3 -17
  72. package/dist/hooks/sync-todo-to-md.mjs.map +2 -2
  73. package/dist/hooks/whisper-rules.mjs.map +1 -1
  74. package/dist/hooks/worker-guard.mjs.map +2 -2
  75. package/dist/hooks/worker-proxy.mjs.map +1 -1
  76. package/dist/hooks/worker-status-line.mjs +4 -4
  77. package/dist/hooks/worker-status-line.mjs.map +2 -2
  78. package/dist/hooks/worker-supervision.mjs.map +1 -1
  79. package/dist/{indexer-backend-CJ0RGgMz.mjs → indexer-backend-Cnc7Tf5C.mjs} +2 -2
  80. package/dist/{ipc-client-EhWY8XL0.mjs → ipc-client-D16Xw6Uo.mjs} +2 -2
  81. package/dist/{ipc-client-EhWY8XL0.mjs.map → ipc-client-D16Xw6Uo.mjs.map} +1 -1
  82. package/dist/{kg-entity-1uqCnk4u.mjs → kg-entity-COTUj1ZC.mjs} +1 -1
  83. package/dist/{kg-entity-1uqCnk4u.mjs.map → kg-entity-COTUj1ZC.mjs.map} +1 -1
  84. package/dist/{latent-ideas-CSuKfiq3.mjs → latent-ideas-ByvMjVcb.mjs} +3 -3
  85. package/dist/{latent-ideas-CSuKfiq3.mjs.map → latent-ideas-ByvMjVcb.mjs.map} +1 -1
  86. package/dist/{link-boost-BtjzfE3c.mjs → link-boost-UiE-uooT.mjs} +1 -1
  87. package/dist/{link-boost-BtjzfE3c.mjs.map → link-boost-UiE-uooT.mjs.map} +1 -1
  88. package/dist/{main-resolver-DyOrwNYw.mjs → main-resolver-BeYWNzrt.mjs} +11 -11
  89. package/dist/{main-resolver-DyOrwNYw.mjs.map → main-resolver-BeYWNzrt.mjs.map} +1 -1
  90. package/dist/main-resolver-kHW6FewU.mjs +7 -0
  91. package/dist/merge-2gqRPFu2.mjs +3 -0
  92. package/dist/{merge-D58n_-LZ.mjs → merge-DgU9OgZy.mjs} +1 -1
  93. package/dist/{merge-D58n_-LZ.mjs.map → merge-DgU9OgZy.mjs.map} +1 -1
  94. package/dist/module-paths-DdRzbkUI.mjs +44 -0
  95. package/dist/module-paths-DdRzbkUI.mjs.map +1 -0
  96. package/dist/{neighborhood-DNuRelRB.mjs → neighborhood-CvRHlqdR.mjs} +2 -2
  97. package/dist/{neighborhood-DNuRelRB.mjs.map → neighborhood-CvRHlqdR.mjs.map} +1 -1
  98. package/dist/{note-context-D9JZ4-7o.mjs → note-context-BrbfUIoP.mjs} +1 -1
  99. package/dist/{note-context-D9JZ4-7o.mjs.map → note-context-BrbfUIoP.mjs.map} +1 -1
  100. package/dist/{pai-home-UncxWxlX.mjs → pai-home-Cm9rcJgX.mjs} +1 -1
  101. package/dist/{pai-home-UncxWxlX.mjs.map → pai-home-Cm9rcJgX.mjs.map} +1 -1
  102. package/dist/{planner-DgC3oSvb.mjs → planner-DAq4Yx-H.mjs} +7 -7
  103. package/dist/{planner-DgC3oSvb.mjs.map → planner-DAq4Yx-H.mjs.map} +1 -1
  104. package/dist/{postgres-CcsRKir-.mjs → postgres-DYtZg7J7.mjs} +5 -13
  105. package/dist/{postgres-CcsRKir-.mjs.map → postgres-DYtZg7J7.mjs.map} +1 -1
  106. package/dist/{program-JhF2GgJY.mjs → program-CWf9mT7Z.mjs} +262 -260
  107. package/dist/program-CWf9mT7Z.mjs.map +1 -0
  108. package/dist/{query-feedback-BPa0dISE.mjs → query-feedback-BIaZTTFO.mjs} +2 -2
  109. package/dist/{query-feedback-BPa0dISE.mjs.map → query-feedback-BIaZTTFO.mjs.map} +1 -1
  110. package/dist/query-feedback-B_iigYj-.mjs +3 -0
  111. package/dist/{registry-db-DCzdI4sC.mjs → registry-db-C7voqML9.mjs} +2 -2
  112. package/dist/{registry-db-DCzdI4sC.mjs.map → registry-db-C7voqML9.mjs.map} +1 -1
  113. package/dist/registry-db-JHPhA8vF.mjs +3 -0
  114. package/dist/{registry-postgres-BUnkHKYs.mjs → registry-postgres-DSrkxqfF.mjs} +2 -2
  115. package/dist/{registry-postgres-BUnkHKYs.mjs.map → registry-postgres-DSrkxqfF.mjs.map} +1 -1
  116. package/dist/{registry-sqlite-CKYNwkUv.mjs → registry-sqlite-B2436JgX.mjs} +2 -2
  117. package/dist/{registry-sqlite-CKYNwkUv.mjs.map → registry-sqlite-B2436JgX.mjs.map} +1 -1
  118. package/dist/router-S6C5BxzZ.mjs +3 -0
  119. package/dist/{router-CGZATlvm.mjs → router-aqLMvNMg.mjs} +1 -1
  120. package/dist/{router-CGZATlvm.mjs.map → router-aqLMvNMg.mjs.map} +1 -1
  121. package/dist/{run-env-Br4Bc7Vz.mjs → run-env-BHWnXqle.mjs} +3 -3
  122. package/dist/{run-env-Br4Bc7Vz.mjs.map → run-env-BHWnXqle.mjs.map} +1 -1
  123. package/dist/{run-W2rV_9j0.mjs → run-uAkNItb6.mjs} +140 -44
  124. package/dist/run-uAkNItb6.mjs.map +1 -0
  125. package/dist/{runtime-paths-QGQJAekd.mjs → runtime-paths-CHTg3ywb.mjs} +1 -1
  126. package/dist/{runtime-paths-QGQJAekd.mjs.map → runtime-paths-CHTg3ywb.mjs.map} +1 -1
  127. package/dist/{server-B71rem4q.mjs → server-DlL1QI2k.mjs} +4 -5
  128. package/dist/server-DlL1QI2k.mjs.map +1 -0
  129. package/dist/{session-keepalive-Dgil9hjw.mjs → session-keepalive-BWEjcRrh.mjs} +7 -7
  130. package/dist/{session-keepalive-Dgil9hjw.mjs.map → session-keepalive-BWEjcRrh.mjs.map} +1 -1
  131. package/dist/skills/Art/SKILL.md +1 -1
  132. package/dist/skills/Observability/SKILL.md +4 -4
  133. package/dist/skills/Research/SKILL.md +1 -1
  134. package/dist/skills/Tasks/SKILL.md +1 -1
  135. package/dist/{sources-LcGptLA-.mjs → sources-DX4ElmrE.mjs} +1 -1
  136. package/dist/{sources-LcGptLA-.mjs.map → sources-DX4ElmrE.mjs.map} +1 -1
  137. package/dist/{sqlite-CAJcw2zL.mjs → sqlite-BrEu3avy.mjs} +3 -3
  138. package/dist/{sqlite-CAJcw2zL.mjs.map → sqlite-BrEu3avy.mjs.map} +1 -1
  139. package/dist/{state-S9wlKarB.mjs → state-DW8zdweW.mjs} +1 -1
  140. package/dist/{state-CHltNjXI.mjs → state-HyjqTihC.mjs} +1 -1
  141. package/dist/{state-CHltNjXI.mjs.map → state-HyjqTihC.mjs.map} +1 -1
  142. package/dist/{themes-D1FQFthd.mjs → themes-BgqahYVM.mjs} +2 -2
  143. package/dist/{themes-D1FQFthd.mjs.map → themes-BgqahYVM.mjs.map} +1 -1
  144. package/dist/{tools-BW7OXf-N.mjs → tools-BbqQIHPe.mjs} +4 -4
  145. package/dist/{tools-BFC-113F.mjs → tools-gMGDIpJ9.mjs} +18 -18
  146. package/dist/{tools-BFC-113F.mjs.map → tools-gMGDIpJ9.mjs.map} +1 -1
  147. package/dist/{trace-IDKK1VFs.mjs → trace-CmAB7iJZ.mjs} +1 -1
  148. package/dist/{trace-IDKK1VFs.mjs.map → trace-CmAB7iJZ.mjs.map} +1 -1
  149. package/dist/{vault-indexer-y6YC2-mh.mjs → vault-indexer-Ddq51X20.mjs} +1 -1
  150. package/dist/{vault-indexer-y6YC2-mh.mjs.map → vault-indexer-Ddq51X20.mjs.map} +1 -1
  151. package/dist/{wakeup-CNZQZzsY.mjs → wakeup-CZw88uXf.mjs} +3 -3
  152. package/dist/{wakeup-CNZQZzsY.mjs.map → wakeup-CZw88uXf.mjs.map} +1 -1
  153. package/dist/{work-queue-worker-Dy5FT2ak.mjs → work-queue-worker-DCzfH0d1.mjs} +4 -4
  154. package/dist/{work-queue-worker-Dy5FT2ak.mjs.map → work-queue-worker-DCzfH0d1.mjs.map} +1 -1
  155. package/dist/work-queue-worker-Taba_vcA.mjs +11 -0
  156. package/dist/{zettelkasten-Dx-63BEk.mjs → zettelkasten-Dwj67e4p.mjs} +4 -4
  157. package/dist/{zettelkasten-Dx-63BEk.mjs.map → zettelkasten-Dwj67e4p.mjs.map} +1 -1
  158. package/docker/docker-compose.yml +39 -0
  159. package/docs/auto-compact.md +31 -0
  160. package/docs/budget-advisor.md +48 -0
  161. package/docs/command-reference.md +25 -0
  162. package/docs/commands/README.md +3 -3
  163. package/docs/commands/config.md +3 -3
  164. package/docs/commands/setup.md +9 -2
  165. package/docs/commands/worker.md +2 -2
  166. package/docs/companion-projects.md +9 -0
  167. package/docs/context-preservation.md +43 -0
  168. package/docs/how-it-works.md +25 -0
  169. package/docs/install-linux.md +32 -0
  170. package/docs/install.md +56 -0
  171. package/docs/memory.md +96 -0
  172. package/docs/observations.md +58 -0
  173. package/docs/release-history.md +42 -0
  174. package/docs/rules-and-privacy.md +37 -0
  175. package/docs/search.md +169 -0
  176. package/docs/session-management.md +153 -0
  177. package/docs/session-notes.md +64 -0
  178. package/docs/skills.md +45 -0
  179. package/docs/task-bus.md +1 -2
  180. package/docs/use-cases.md +194 -0
  181. package/docs/what-you-can-ask.md +78 -0
  182. package/docs/worker-providers.md +58 -0
  183. package/docs/zettelkasten.md +37 -0
  184. package/package.json +3 -2
  185. package/plugins/productivity/skills/Tasks/SKILL.md +1 -1
  186. package/scripts/build-hooks.mjs +6 -6
  187. package/src/hooks/ts/lib/pai-paths-case.test.ts +12 -0
  188. package/src/hooks/ts/lib/pai-paths-import.test.ts +25 -0
  189. package/src/hooks/ts/lib/pai-paths.ts +5 -28
  190. package/src/hooks/ts/lib/sleep-poll-gate.test.ts +17 -1
  191. package/src/hooks/ts/lib/sleep-poll-gate.ts +21 -9
  192. package/src/hooks/ts/pre-tool-use/security-validator.test.ts +23 -0
  193. package/src/hooks/ts/pre-tool-use/security-validator.ts +1 -1
  194. package/src/hooks/ts/session-start/load-core-context.ts +2 -6
  195. package/src/hooks/ts/session-start/load-project-context.ts +1 -1
  196. package/src/hooks/ts/session-start/session-start-worker-guard.test.ts +24 -1
  197. package/dist/config-2wkz744W.mjs.map +0 -1
  198. package/dist/context-handover-cache-BOpjLsKe.mjs.map +0 -1
  199. package/dist/daemon-q3xjHwa4.mjs +0 -19
  200. package/dist/detector-C-YmZsOi.mjs +0 -3
  201. package/dist/fallback-ECiqCuh6.mjs.map +0 -1
  202. package/dist/federation-db-BZ8PyxFe.mjs +0 -3
  203. package/dist/main-resolver-vN09bkXv.mjs +0 -7
  204. package/dist/merge-CXGz5wXz.mjs +0 -3
  205. package/dist/program-JhF2GgJY.mjs.map +0 -1
  206. package/dist/query-feedback-BPHFFSu1.mjs +0 -3
  207. package/dist/registry-db-Hphlj3H4.mjs +0 -3
  208. package/dist/router-CoA8Uy1m.mjs +0 -3
  209. package/dist/run-W2rV_9j0.mjs.map +0 -1
  210. package/dist/server-B71rem4q.mjs.map +0 -1
  211. package/dist/work-queue-worker-CkShUa0z.mjs +0 -11
  212. /package/dist/{indexer-backend-DotpxHJd.mjs → indexer-backend-isSLg6yE.mjs} +0 -0
@@ -1,6 +1,6 @@
1
- import { i as getRegistryBackend } from "./factory-C4We3xR_.mjs";
2
- import { E as readWorkersSection, O as resolveModelCapability, T as providerKeyPath, b as isModelCapability, d as NATIVE_ANTHROPIC_MODELS, g as classModelCapability, h as assertProviderRunnable, k as resolveProviderKey, m as WorkersConfigError, n as claudeCommand, t as buildRunEnv } from "./run-env-Br4Bc7Vz.mjs";
3
- import { _ as statusPath, d as resolveTarget, f as setCooldown, g as panesDir, h as ledgerPath, i as capabilityForRun, m as eventsPath, n as ensureProxyRunning, o as isQuotaFailure, p as ensureNoMcpConfig, s as nextAutoProvider, t as DEFAULT_PROXY_PORT, u as resolveCapabilityRun, v as workersLogDir } from "./server-B71rem4q.mjs";
1
+ import { i as getRegistryBackend } from "./factory-y36qGegI.mjs";
2
+ import { E as readWorkersSection, O as resolveModelCapability, T as providerKeyPath, b as isModelCapability, d as NATIVE_ANTHROPIC_MODELS, g as classModelCapability, h as assertProviderRunnable, k as resolveProviderKey, m as WorkersConfigError, n as claudeCommand, t as buildRunEnv } from "./run-env-BHWnXqle.mjs";
3
+ import { _ as statusPath, d as resolveTarget, f as setCooldown, g as panesDir, h as ledgerPath, i as capabilityForRun, m as eventsPath, n as ensureProxyRunning, o as isQuotaFailure, p as ensureNoMcpConfig, s as nextAutoProvider, t as DEFAULT_PROXY_PORT, u as resolveCapabilityRun, v as workersLogDir } from "./server-DlL1QI2k.mjs";
4
4
  import { appendFileSync, closeSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
5
5
  import { homedir, platform, tmpdir } from "node:os";
6
6
  import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
@@ -233,6 +233,7 @@ function parseRunnerArgs(argv) {
233
233
  let callerModel = false;
234
234
  let callerMcpConfig = false;
235
235
  let callerSystemPrompt = false;
236
+ const callerSystemPrompts = [];
236
237
  let callerTools = false;
237
238
  const mcp = [];
238
239
  const allowedTools = [];
@@ -254,7 +255,13 @@ function parseRunnerArgs(argv) {
254
255
  } else if (a.startsWith("--output-format=")) {
255
256
  const v = a.slice(16);
256
257
  if (v === "json" || v === "stream-json") outputFormat = v;
257
- } else if (a === "--verbose") {} else if (a === "--mcp") {
258
+ } else if (a === "--verbose") {} else if (a === "--append-system-prompt") {
259
+ callerSystemPrompt = true;
260
+ if (i + 1 < argv.length) callerSystemPrompts.push(argv[++i]);
261
+ } else if (a.startsWith("--append-system-prompt=")) {
262
+ callerSystemPrompt = true;
263
+ callerSystemPrompts.push(a.slice(23));
264
+ } else if (a === "--mcp") {
258
265
  const v = argv[i + 1];
259
266
  if (v !== void 0 && !v.startsWith("-")) {
260
267
  mcp.push(v);
@@ -277,8 +284,6 @@ function parseRunnerArgs(argv) {
277
284
  if (a.startsWith("--model=")) callerModel = true;
278
285
  if (a === "--mcp-config") callerMcpConfig = true;
279
286
  if (a.startsWith("--mcp-config=")) callerMcpConfig = true;
280
- if (a === "--append-system-prompt") callerSystemPrompt = true;
281
- if (a.startsWith("--append-system-prompt=")) callerSystemPrompt = true;
282
287
  if (a === "--tools") callerTools = true;
283
288
  if (a.startsWith("--tools=")) callerTools = true;
284
289
  if (prompt === null && !a.startsWith("-") && rest.length > 0 && (rest[rest.length - 1] === "-p" || rest[rest.length - 1] === "--print")) prompt = a;
@@ -294,12 +299,22 @@ function parseRunnerArgs(argv) {
294
299
  callerModel,
295
300
  callerMcpConfig,
296
301
  callerSystemPrompt,
302
+ callerSystemPrompts,
297
303
  callerTools,
298
304
  mcp,
299
305
  allowedTools
300
306
  };
301
307
  }
302
308
  /**
309
+ * The one --append-system-prompt flag for a spawn: claude keeps only the LAST
310
+ * occurrence, so every piece is joined (blank line between) into a single flag.
311
+ * Empty parts are dropped; no parts, no flag.
312
+ */
313
+ function appendSystemPromptArgs(parts) {
314
+ const text = parts.filter((p) => p.trim()).join("\n\n");
315
+ return text ? ["--append-system-prompt", text] : [];
316
+ }
317
+ /**
303
318
  * Turn `-p "<prompt>"` into bare `-p` (same for --print): for stream-json
304
319
  * stdin runs the prompt moves to the first user message on stdin, so the
305
320
  * value must not stay on the command line. Only prompt values directly
@@ -903,6 +918,10 @@ function salvageOnExit(ledger, id, label, wtDir, timeoutMs) {
903
918
  paths: committed.join(","),
904
919
  skipped: skipped.length
905
920
  });
921
+ if (skipped.length) appendLedger(ledger, "WORKER-NOTE", {
922
+ id,
923
+ note: `salvage skipped: ${skipped.join("; ")}`
924
+ });
906
925
  return committed;
907
926
  } catch (e) {
908
927
  try {
@@ -992,8 +1011,10 @@ function salvageUncommitted(wtDir, label, timeoutMs) {
992
1011
  skipped: []
993
1012
  };
994
1013
  const skipped = [];
1014
+ const missing = new Set(git(wtDir, ["ls-files", "--deleted"], timeoutMs).split("\n").filter(Boolean));
995
1015
  const toStage = [];
996
- for (const p of paths) if (symlinkEscapesWorktree(wtDir, p)) skipped.push(`symlink ${p} -> ${readlinkSync(join(wtDir, p))} (points outside the worktree)`);
1016
+ for (const p of paths) if (missing.has(p)) skipped.push(`${p} (missing, not committed)`);
1017
+ else if (symlinkEscapesWorktree(wtDir, p)) skipped.push(`symlink ${p} -> ${readlinkSync(join(wtDir, p))} (points outside the worktree)`);
997
1018
  else toStage.push(p);
998
1019
  if (!toStage.length) return {
999
1020
  committed: [],
@@ -1002,11 +1023,21 @@ function salvageUncommitted(wtDir, label, timeoutMs) {
1002
1023
  try {
1003
1024
  git(wtDir, [
1004
1025
  "add",
1005
- "-A",
1026
+ "--ignore-removal",
1006
1027
  "--",
1007
1028
  ".",
1008
1029
  ...paths.filter((p) => !toStage.includes(p)).map((p) => `:(exclude,literal)${p}`)
1009
1030
  ], timeoutMs);
1031
+ const deleted = git(wtDir, [
1032
+ "diff",
1033
+ "--cached",
1034
+ "--diff-filter=D",
1035
+ "--name-only"
1036
+ ], timeoutMs).split("\n").filter(Boolean);
1037
+ if (deleted.length > MAX_SALVAGE_DELETES) {
1038
+ git(wtDir, ["reset", "-q"], timeoutMs);
1039
+ throw new Error(`salvage would delete ${deleted.length} tracked files (${deleted.slice(0, 5).join(", ")}, ...), refused`);
1040
+ }
1010
1041
  git(wtDir, [
1011
1042
  "commit",
1012
1043
  "-m",
@@ -1020,6 +1051,8 @@ function salvageUncommitted(wtDir, label, timeoutMs) {
1020
1051
  skipped
1021
1052
  };
1022
1053
  }
1054
+ /** A salvage commit that deletes more tracked files than this is refused. */
1055
+ const MAX_SALVAGE_DELETES = 5;
1023
1056
  /**
1024
1057
  * The dirty paths of a checkout, parsed from `git status --porcelain -z`:
1025
1058
  * NUL-separated (a path with a newline in it cannot corrupt the parse),
@@ -1338,14 +1371,20 @@ function gcWorktrees(logDir, olderThanHours = 24, dryRun = false, now = Date.now
1338
1371
  const out = [];
1339
1372
  const root = worktreesDir(logDir);
1340
1373
  const known = /* @__PURE__ */ new Set();
1341
- const archive = (id, dir, repo, st) => {
1374
+ const archive = (id, dir, st) => {
1342
1375
  const target = `refs/pai-archive/${id}`;
1343
1376
  if (dryRun) return void out.push({
1344
1377
  id,
1345
1378
  archive: target
1346
1379
  });
1347
1380
  try {
1348
- if (!repo) throw new Error("not a git worktree");
1381
+ let repo;
1382
+ try {
1383
+ const raw = git(dir, ["rev-parse", "--git-common-dir"]);
1384
+ repo = isAbsolute(raw) ? raw : resolve(dir, raw);
1385
+ } catch {
1386
+ throw new Error("not a git worktree");
1387
+ }
1349
1388
  const label = `archive: leftover state of worker ${id}`;
1350
1389
  if (uncommittedPaths(dir).length) {
1351
1390
  const res = salvageUncommitted(dir, label);
@@ -1424,7 +1463,7 @@ function gcWorktrees(logDir, olderThanHours = 24, dryRun = false, now = Date.now
1424
1463
  continue;
1425
1464
  }
1426
1465
  if (end > cutoff) continue;
1427
- archive(st.id, st.worktreeDir, st.cwd, st);
1466
+ archive(st.id, st.worktreeDir, st);
1428
1467
  }
1429
1468
  if (existsSync(root)) for (const ent of readdirSync(root, { withFileTypes: true })) {
1430
1469
  if (!ent.isDirectory() || known.has(ent.name) || existsSync(statusPath(logDir, ent.name))) continue;
@@ -1434,14 +1473,7 @@ function gcWorktrees(logDir, olderThanHours = 24, dryRun = false, now = Date.now
1434
1473
  } catch {
1435
1474
  continue;
1436
1475
  }
1437
- let repo = null;
1438
- try {
1439
- const raw = git(dir, ["rev-parse", "--git-common-dir"]);
1440
- repo = isAbsolute(raw) ? raw : resolve(dir, raw);
1441
- } catch {
1442
- repo = null;
1443
- }
1444
- archive(ent.name, dir, repo, null);
1476
+ archive(ent.name, dir, null);
1445
1477
  }
1446
1478
  if (!dryRun) for (const repo of new Set(loadStatuses(logDir).map((s) => s.cwd))) try {
1447
1479
  git(repo, ["worktree", "prune"]);
@@ -2200,12 +2232,51 @@ function absoluteCliPath() {
2200
2232
  if (argv1.endsWith("pai")) return argv1;
2201
2233
  return "pai";
2202
2234
  }
2235
+ /** The one place that decides where a follow pane opens: tmux (any OS) > iTerm2 (macOS) > nowhere. */
2236
+ function paneBackend(env = process.env, platform = process.platform) {
2237
+ if (env.TMUX) return "tmux";
2238
+ if (platform === "darwin" && env.ITERM_SESSION_ID) return "iterm";
2239
+ return "none";
2240
+ }
2241
+ function noPaneMessage(wid) {
2242
+ return `no pane support here, use: pai worker follow${wid ? ` ${wid}` : ""}`;
2243
+ }
2244
+ /** Split the current tmux window horizontally (right-hand column) and run cmd in it, focus stays put. */
2245
+ function tmuxSplit(cmd) {
2246
+ const target = process.env.TMUX_PANE ? ["-t", process.env.TMUX_PANE] : [];
2247
+ try {
2248
+ execFileSync("tmux", [
2249
+ "split-window",
2250
+ "-h",
2251
+ "-d",
2252
+ "-l",
2253
+ "40%",
2254
+ ...target,
2255
+ cmd
2256
+ ], { stdio: "pipe" });
2257
+ } catch (e) {
2258
+ const err = e;
2259
+ throw new Error(`pai worker pane: tmux split-window failed: ${(err.stderr?.toString() || err.message || "").trim().slice(0, 200)}`);
2260
+ }
2261
+ }
2203
2262
  /** Open (or only report on) the stacked follow pane of one worker. */
2204
2263
  async function openPaneForWorker(logDir, config, wid, term) {
2205
2264
  if (workerPaneOpen(wid)) return {
2206
2265
  message: `pane for ${wid} already open`,
2207
2266
  session: null
2208
2267
  };
2268
+ const backend = paneBackend();
2269
+ if (backend === "none") return {
2270
+ message: noPaneMessage(wid),
2271
+ session: null
2272
+ };
2273
+ if (backend === "tmux") {
2274
+ tmuxSplit(followCommand(wid, config.pane.autoExitSecs));
2275
+ return {
2276
+ message: `pane opened for ${wid}`,
2277
+ session: null
2278
+ };
2279
+ }
2209
2280
  const uid = itermUuid(term);
2210
2281
  const regPath = join(panesDir(logDir), `${scopeKey(term)}.json`);
2211
2282
  const reg = loadRegistry(regPath);
@@ -2259,6 +2330,7 @@ async function windowBoundsLine(term) {
2259
2330
  */
2260
2331
  async function checkPaneForWorker(wid, fontSize, term) {
2261
2332
  const lines = [workerPaneOpen(wid) ? `pane for ${wid} open` : `no pane for ${wid}`];
2333
+ if (paneBackend() !== "iterm") return lines.join("\n");
2262
2334
  lines.push(await windowBoundsLine(term));
2263
2335
  const path = dynamicProfilePath();
2264
2336
  if (existsSync(path)) {
@@ -2281,6 +2353,13 @@ async function checkPaneForWorker(wid, fontSize, term) {
2281
2353
  * with the running command, so names cannot serve as idempotence).
2282
2354
  */
2283
2355
  async function openFollowPane(logDir, _config, term, checkOnly) {
2356
+ const backend = paneBackend();
2357
+ if (backend === "none") return noPaneMessage();
2358
+ if (backend === "tmux") {
2359
+ if (checkOnly) return "no follow pane";
2360
+ tmuxSplit(`${process.execPath} ${absoluteCliPath()} worker follow`);
2361
+ return "follow pane opened";
2362
+ }
2284
2363
  const uid = itermUuid(term);
2285
2364
  const out = (await osascript(TAB_TTYS_SCRIPT, [uid])).stdout.trim();
2286
2365
  if (out === "notfound") throw new Error("pai worker pane: no open iTerm2 session matches ITERM_SESSION_ID");
@@ -2399,7 +2478,9 @@ function operatorSocketPath(logDir, id) {
2399
2478
  }
2400
2479
  /**
2401
2480
  * The runner's side: listen on the worker socket, hand every received line to
2402
- * `onLine`. Returns the server (close it when the run ends; the socket file is
2481
+ * `onLine`. It acknowledges with "ok" only when `onLine` returns without a
2482
+ * string; a returned string (or a throw) is the failure reason, sent back as
2483
+ * `err <reason>` so `say` never reports ok for a message nobody received. Returns the server (close it when the run ends; the socket file is
2403
2484
  * unlinked on close, best effort).
2404
2485
  */
2405
2486
  function createOperatorServer(logDir, id, onLine) {
@@ -2409,14 +2490,20 @@ function createOperatorServer(logDir, id, onLine) {
2409
2490
  } catch {}
2410
2491
  const server = createServer((socket) => {
2411
2492
  let buf = "";
2412
- socket.on("data", (chunk) => {
2493
+ socket.on("error", () => {});
2494
+ socket.on("data", async (chunk) => {
2413
2495
  buf += chunk.toString("utf8");
2414
2496
  let nl;
2415
2497
  while ((nl = buf.indexOf("\n")) >= 0) {
2416
2498
  const line = buf.slice(0, nl).replace(/\r$/, "");
2417
2499
  buf = buf.slice(nl + 1);
2418
- if (line.trim()) onLine(line);
2419
- socket.write("ok\n");
2500
+ let failure;
2501
+ if (line.trim()) try {
2502
+ failure = await onLine(line);
2503
+ } catch (e) {
2504
+ failure = e.message;
2505
+ }
2506
+ if (!socket.destroyed) socket.write(typeof failure === "string" && failure ? `err ${failure.replace(/\n/g, " ")}\n` : "ok\n");
2420
2507
  }
2421
2508
  });
2422
2509
  });
@@ -2430,7 +2517,7 @@ function createOperatorServer(logDir, id, onLine) {
2430
2517
  }
2431
2518
  /**
2432
2519
  * `pai worker say <id> "<text>"`: forward one line to a running worker.
2433
- * Resolves "ok", rejects with a clear message when the worker is not running.
2520
+ * Resolves "ok" only once the worker acknowledged delivery; rejects with the reason otherwise.
2434
2521
  */
2435
2522
  function sayToWorker(logDir, id, text, timeoutMs = 4e3) {
2436
2523
  const status = loadStatus(logDir, id);
@@ -2449,10 +2536,13 @@ function sayToWorker(logDir, id, text, timeoutMs = 4e3) {
2449
2536
  sock.once("connect", () => {
2450
2537
  sock.write(text.replace(/\n/g, " ") + "\n");
2451
2538
  });
2452
- sock.once("data", () => {
2539
+ sock.once("data", (d) => {
2453
2540
  sock.end();
2454
- resolve("ok");
2541
+ const reply = d.toString("utf8").trim();
2542
+ if (reply === "ok") resolve("ok");
2543
+ else reject(/* @__PURE__ */ new Error(`worker ${id} did not take the message: ${reply.replace(/^err /, "") || "no acknowledgement"}`));
2455
2544
  });
2545
+ sock.once("close", () => fail(/* @__PURE__ */ new Error("connection closed before the worker acknowledged")));
2456
2546
  });
2457
2547
  }
2458
2548
 
@@ -2857,10 +2947,10 @@ function fullContractPrompt(format) {
2857
2947
  "report.",
2858
2948
  "No narration, no timestamps, no greetings, no summaries of what you read; act, verify, then stop.",
2859
2949
  "Never sleep-poll: no `sleep N` over 60 seconds, anywhere in a command — standalone, after `&&`/`||`/",
2860
- "`;`/`|`, or inside a loop or subshell. Ending your turn ends this process, so a background job gets",
2861
- "no later wake-up here: run the long command in the FOREGROUND with a Bash timeout up to 600000 ms;",
2862
- "if it can run longer than that, start it in the background and wait with a foreground loop that ends",
2863
- "when the job ends: `while kill -0 <pid> 2>/dev/null; do sleep 15; done`.",
2950
+ "`;`/`|`, or inside a loop or subshell — and the sleeps of one command add up (`sleep 58; sleep 58` is",
2951
+ "116 s). Ending your turn ends this process, so a background job gets no later wake-up here:",
2952
+ "run_in_background is denied. Run the long command in the FOREGROUND with a Bash timeout up to 600000 ms",
2953
+ "and split longer jobs into steps that each fit it.",
2864
2954
  ...fileNotInlineBlock(),
2865
2955
  "",
2866
2956
  `Operator messages: a user turn starting with ${OPERATOR_MARK} was typed by the operator while you`,
@@ -2903,7 +2993,7 @@ function promptTrailer(format) {
2903
2993
  return format === "ag2" ? "\n\nFinal message: one AG2 R message only, no prose (format in system prompt)." : "\n\nFinal message: a JSON object only, no prose before or after, no code fence (format in system prompt).";
2904
2994
  }
2905
2995
  /** The exact text sent to resend a final message that failed AG2 validation. */
2906
- const AG2_REASK_TEXT = "Your final message was not a valid AG2 R message. Resend it now as a single AG2 R message only: first line R, then i=id r=result G=+ (gate, if r=+) t=tests(Name+) c=path summary p=proof z=the answer/note. No prose.";
2996
+ const AG2_REASK_TEXT = "Your final message was not a valid AG2 R message. Resend it now as a single AG2 R message only, no prose, no code fence. Minimal example (one key=value per line, first line R):\nR\ni=task-id\nr=+\nG=+\nc=path summary\nt=Name+\np=proof command\nz=the answer, one line\n(r is + - ~ ? !; r=+ needs G=+ and p; when r is not +, add y=why.)";
2907
2997
  /** The full-contract AG2 prompt — the keepalive path's identical-construction baseline. */
2908
2998
  const WORKER_CONTRACT_PROMPT = fullContractPrompt("ag2");
2909
2999
  /**
@@ -3433,7 +3523,7 @@ async function runWorker(opts) {
3433
3523
  if (gc?.length) process.stderr.write(`pai worker: archived ${gc.length} leftover worktree(s) (pai worker gc)\n`);
3434
3524
  } catch {}
3435
3525
  if (opts.className === "plan" && !opts._planner) {
3436
- const { runPlanner } = await import("./planner-DgC3oSvb.mjs");
3526
+ const { runPlanner } = await import("./planner-DAq4Yx-H.mjs");
3437
3527
  return runPlanner(opts);
3438
3528
  }
3439
3529
  const parent = launchParent(opts.parent);
@@ -3809,7 +3899,7 @@ async function executeImageRun(a) {
3809
3899
  * ledger line). Fire-and-forget, never blocks the worker.
3810
3900
  */
3811
3901
  async function announcePane(logDir, config, wid, term, noPane, ledger) {
3812
- const skip = !noPane ? !config.pane.enabled ? "workers.pane.enabled is false" : process.env.PAI_WORKER_AUTOPANE === "0" ? "PAI_WORKER_AUTOPANE=0" : !term ? "not in iTerm2 (no ITERM_SESSION_ID)" : null : "--no-pane";
3902
+ const skip = !noPane ? !config.pane.enabled ? "workers.pane.enabled is false" : process.env.PAI_WORKER_AUTOPANE === "0" ? "PAI_WORKER_AUTOPANE=0" : paneBackend() === "none" ? "no tmux ($TMUX) or iTerm2 (ITERM_SESSION_ID) here" : null : "--no-pane";
3813
3903
  if (skip) {
3814
3904
  process.stderr.write(`[pai worker] no pane for ${wid}: ${skip}\n`);
3815
3905
  return;
@@ -3902,12 +3992,12 @@ async function executeRun(a) {
3902
3992
  let cmd = [...claudeCmd];
3903
3993
  cmd.push(...modelFlagArgs(headless, model, Boolean(parsed.callerModel)));
3904
3994
  cmd.push(...chromeArgs, ...mcpArgs, ...toolArgs, ...toolsFlag, ...restArgs);
3905
- if (headless) {
3906
- cmd.push("--output-format", "stream-json", "--verbose", "--input-format", "stream-json");
3907
- if (!parsed.callerSystemPrompt) cmd.push("--append-system-prompt", workerContractPrompt(a.className, a.reportFormat));
3908
- if (worktree) cmd.push("--append-system-prompt", worktreeSystemPrompt(wid, worktree.branch, worktree.dir, worktree.snapshot));
3909
- else cmd.push("--append-system-prompt", inPlaceSystemPrompt());
3910
- }
3995
+ if (headless) cmd.push("--output-format", "stream-json", "--verbose", "--input-format", "stream-json");
3996
+ cmd.push(...appendSystemPromptArgs([
3997
+ ...headless && !parsed.callerSystemPrompt ? [workerContractPrompt(a.className, a.reportFormat)] : [],
3998
+ ...headless ? [worktree ? worktreeSystemPrompt(wid, worktree.branch, worktree.dir, worktree.snapshot) : inPlaceSystemPrompt()] : [],
3999
+ ...parsed.callerSystemPrompts
4000
+ ]));
3911
4001
  cmd = ensureToolSearch(cmd);
3912
4002
  if (a.printCmd) {
3913
4003
  console.log(JSON.stringify({
@@ -3994,7 +4084,9 @@ async function executeRun(a) {
3994
4084
  }) + "\n");
3995
4085
  } catch {}
3996
4086
  };
3997
- const operatorServer = headless ? createOperatorServer(logDir, wid, (text) => {
4087
+ const operatorServer = headless ? createOperatorServer(logDir, wid, async (text) => {
4088
+ const stdin = proc.stdin;
4089
+ if (!stdin || !stdin.writable || stdin.writableEnded) return "the worker's stdin is already closed (its turn ended) — use: pai worker resume";
3998
4090
  operatorInFlight += 1;
3999
4091
  if (closeTimer) {
4000
4092
  clearTimeout(closeTimer);
@@ -4005,9 +4097,13 @@ async function executeRun(a) {
4005
4097
  text,
4006
4098
  handoff: isHandoffMessage(text)
4007
4099
  });
4008
- try {
4009
- proc.stdin?.write(stdinUserMessage(operatorUserText(text)) + "\n");
4010
- } catch {}
4100
+ return new Promise((resolve) => {
4101
+ try {
4102
+ stdin.write(stdinUserMessage(operatorUserText(text)) + "\n", (e) => resolve(e ? `write to the worker failed: ${e.message}` : void 0));
4103
+ } catch (e) {
4104
+ resolve(`write to the worker failed: ${e.message}`);
4105
+ }
4106
+ });
4011
4107
  }) : null;
4012
4108
  let killed = false;
4013
4109
  const cleanup = () => {
@@ -4595,4 +4691,4 @@ function resultFromOutput(out) {
4595
4691
 
4596
4692
  //#endregion
4597
4693
  export { expandMcpNames as $, ledgerSummary as A, loadStatuses as B, discardWorker as C, uncommittedPaths as D, mergeWorker as E, contextPercent as F, setWorkerLabel as G, nowStamp as H, elapsedOf as I, parseRunnerArgs as J, waitForTerminalStatus as K, isChatPane as L, ageOf as M, alive as N, worktreesDir as O, contextLabel as P, describeMcp as Q, isLive as R, workerInScope as S, git as T, ownsPid as U, newWorkerId as V, saveStatus as W, pinNotices as X, shortText as Y, projectLaunchConfig as Z, itermForClaudeSession as _, parseWorkerReport as a, sessionMapPath as b, readInbox as c, checkPaneForWorker as d, openFollowPane as f, currentTabKey as g, markControlsHeld as h, usageContextTokens as i, UNLABELED as j, appendLedger as k, sayToWorker as l, clickrControlsArgv as m, runWorker as n, renderReport as o, openPaneForWorker as p, longInlinePromptHint as q, testProvider as r, handoffFromInside as s, printResult as t, workerDepth as u, itermUuid as v, gcWorktrees as w, sessionTag as x, resolveSession as y, loadStatus as z };
4598
- //# sourceMappingURL=run-W2rV_9j0.mjs.map
4694
+ //# sourceMappingURL=run-uAkNItb6.mjs.map