@code-yeongyu/senpi 2026.9.24-3 → 2026.9.25

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 (256) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/bundle/chunks/{app-server-command-OTRHYRIB.js → app-server-command-RB6QUPRI.js} +1 -1
  3. package/dist/bundle/chunks/{chunk-UM43CBL4.js → chunk-JJ3NJZBD.js} +118 -111
  4. package/dist/bundle/chunks/chunk-SGYEOIOJ.js +8 -0
  5. package/dist/bundle/chunks/chunk-YS5YEX4S.js +2 -0
  6. package/dist/bundle/chunks/{cli-main-TX6I4X4F.js → cli-main-KO53KYDN.js} +1 -1
  7. package/dist/bundle/chunks/{interactive-mode-UI5Q6FGY.js → interactive-mode-ABDNLJSI.js} +1 -1
  8. package/dist/bundle/chunks/{multi-session-host-D6HMI3RQ.js → multi-session-host-JV3MNWXZ.js} +1 -1
  9. package/dist/bundle/chunks/{package-manager-cli-3UWH3ZZS.js → package-manager-cli-ZYYAVCXU.js} +1 -1
  10. package/dist/bundle/chunks/{rpc-mode-LXIZHJSJ.js → rpc-mode-Q2OUJURB.js} +1 -1
  11. package/dist/bundle/chunks/runtime-session-XBT646O5.js +2 -0
  12. package/dist/bundle/chunks/session-worker.js +184 -177
  13. package/dist/bundle/cli.js +1 -1
  14. package/dist/bundle/index.js +1 -1
  15. package/dist/bundle/rpc-entry.js +1 -1
  16. package/dist/core/agent-session.d.ts.map +1 -1
  17. package/dist/core/agent-session.js +1 -1
  18. package/dist/core/agent-session.js.map +1 -1
  19. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts +7 -0
  20. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -1
  21. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +23 -4
  22. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -1
  23. package/dist/core/extensions/builtin/terminal/durable-command.d.ts +11 -16
  24. package/dist/core/extensions/builtin/terminal/durable-command.d.ts.map +1 -1
  25. package/dist/core/extensions/builtin/terminal/durable-command.js +97 -43
  26. package/dist/core/extensions/builtin/terminal/durable-command.js.map +1 -1
  27. package/dist/core/extensions/builtin/terminal/durable-file.d.ts.map +1 -1
  28. package/dist/core/extensions/builtin/terminal/durable-file.js +11 -4
  29. package/dist/core/extensions/builtin/terminal/durable-file.js.map +1 -1
  30. package/dist/core/extensions/builtin/terminal/extension-state.d.ts +26 -0
  31. package/dist/core/extensions/builtin/terminal/extension-state.d.ts.map +1 -0
  32. package/dist/core/extensions/builtin/terminal/extension-state.js +151 -0
  33. package/dist/core/extensions/builtin/terminal/extension-state.js.map +1 -0
  34. package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
  35. package/dist/core/extensions/builtin/terminal/extension.js +32 -308
  36. package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
  37. package/dist/core/extensions/builtin/terminal/lease-file.d.ts +52 -0
  38. package/dist/core/extensions/builtin/terminal/lease-file.d.ts.map +1 -0
  39. package/dist/core/extensions/builtin/terminal/lease-file.js +205 -0
  40. package/dist/core/extensions/builtin/terminal/lease-file.js.map +1 -0
  41. package/dist/core/extensions/builtin/terminal/lease-keeper.d.ts +33 -0
  42. package/dist/core/extensions/builtin/terminal/lease-keeper.d.ts.map +1 -0
  43. package/dist/core/extensions/builtin/terminal/lease-keeper.js +110 -0
  44. package/dist/core/extensions/builtin/terminal/lease-keeper.js.map +1 -0
  45. package/dist/core/extensions/builtin/terminal/manager.d.ts +1 -0
  46. package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
  47. package/dist/core/extensions/builtin/terminal/manager.js +6 -0
  48. package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
  49. package/dist/core/extensions/builtin/terminal/manifest-lease.d.ts +48 -4
  50. package/dist/core/extensions/builtin/terminal/manifest-lease.d.ts.map +1 -1
  51. package/dist/core/extensions/builtin/terminal/manifest-lease.js +141 -59
  52. package/dist/core/extensions/builtin/terminal/manifest-lease.js.map +1 -1
  53. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +2 -0
  54. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  55. package/dist/core/extensions/builtin/terminal/monitor-registry.js +15 -10
  56. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  57. package/dist/core/extensions/builtin/terminal/monitor-state-dir.d.ts +11 -0
  58. package/dist/core/extensions/builtin/terminal/monitor-state-dir.d.ts.map +1 -0
  59. package/dist/core/extensions/builtin/terminal/monitor-state-dir.js +28 -0
  60. package/dist/core/extensions/builtin/terminal/monitor-state-dir.js.map +1 -0
  61. package/dist/core/extensions/builtin/terminal/orphan-reaper.d.ts +41 -0
  62. package/dist/core/extensions/builtin/terminal/orphan-reaper.d.ts.map +1 -0
  63. package/dist/core/extensions/builtin/terminal/orphan-reaper.js +131 -0
  64. package/dist/core/extensions/builtin/terminal/orphan-reaper.js.map +1 -0
  65. package/dist/core/extensions/builtin/terminal/process-identity.d.ts +21 -0
  66. package/dist/core/extensions/builtin/terminal/process-identity.d.ts.map +1 -0
  67. package/dist/core/extensions/builtin/terminal/process-identity.js +34 -0
  68. package/dist/core/extensions/builtin/terminal/process-identity.js.map +1 -0
  69. package/dist/core/extensions/builtin/terminal/process-start-probe.d.ts +6 -0
  70. package/dist/core/extensions/builtin/terminal/process-start-probe.d.ts.map +1 -0
  71. package/dist/core/extensions/builtin/terminal/process-start-probe.js +60 -0
  72. package/dist/core/extensions/builtin/terminal/process-start-probe.js.map +1 -0
  73. package/dist/core/extensions/builtin/terminal/prompt.d.ts.map +1 -1
  74. package/dist/core/extensions/builtin/terminal/prompt.js +8 -4
  75. package/dist/core/extensions/builtin/terminal/prompt.js.map +1 -1
  76. package/dist/core/extensions/builtin/terminal/restore-digest.d.ts +54 -0
  77. package/dist/core/extensions/builtin/terminal/restore-digest.d.ts.map +1 -0
  78. package/dist/core/extensions/builtin/terminal/restore-digest.js +133 -0
  79. package/dist/core/extensions/builtin/terminal/restore-digest.js.map +1 -0
  80. package/dist/core/extensions/builtin/terminal/restore-session.d.ts +56 -0
  81. package/dist/core/extensions/builtin/terminal/restore-session.d.ts.map +1 -0
  82. package/dist/core/extensions/builtin/terminal/restore-session.js +238 -0
  83. package/dist/core/extensions/builtin/terminal/restore-session.js.map +1 -0
  84. package/dist/core/extensions/builtin/terminal/restore.d.ts +52 -19
  85. package/dist/core/extensions/builtin/terminal/restore.d.ts.map +1 -1
  86. package/dist/core/extensions/builtin/terminal/restore.js +63 -138
  87. package/dist/core/extensions/builtin/terminal/restore.js.map +1 -1
  88. package/dist/core/extensions/builtin/terminal/runtime-session.d.ts +5 -0
  89. package/dist/core/extensions/builtin/terminal/runtime-session.d.ts.map +1 -1
  90. package/dist/core/extensions/builtin/terminal/runtime-session.js +17 -0
  91. package/dist/core/extensions/builtin/terminal/runtime-session.js.map +1 -1
  92. package/dist/core/extensions/builtin/terminal/session-activity.d.ts +8 -0
  93. package/dist/core/extensions/builtin/terminal/session-activity.d.ts.map +1 -0
  94. package/dist/core/extensions/builtin/terminal/session-activity.js +41 -0
  95. package/dist/core/extensions/builtin/terminal/session-activity.js.map +1 -0
  96. package/dist/core/extensions/builtin/terminal/shared.d.ts +14 -0
  97. package/dist/core/extensions/builtin/terminal/shared.d.ts.map +1 -1
  98. package/dist/core/extensions/builtin/terminal/shared.js +14 -0
  99. package/dist/core/extensions/builtin/terminal/shared.js.map +1 -1
  100. package/dist/core/extensions/builtin/terminal/terminal-manifest-model.d.ts +8 -0
  101. package/dist/core/extensions/builtin/terminal/terminal-manifest-model.d.ts.map +1 -1
  102. package/dist/core/extensions/builtin/terminal/terminal-manifest-model.js.map +1 -1
  103. package/dist/core/extensions/builtin/terminal/terminal-manifest-parse.d.ts +15 -0
  104. package/dist/core/extensions/builtin/terminal/terminal-manifest-parse.d.ts.map +1 -0
  105. package/dist/core/extensions/builtin/terminal/terminal-manifest-parse.js +148 -0
  106. package/dist/core/extensions/builtin/terminal/terminal-manifest-parse.js.map +1 -0
  107. package/dist/core/extensions/builtin/terminal/terminal-manifest.d.ts +15 -2
  108. package/dist/core/extensions/builtin/terminal/terminal-manifest.d.ts.map +1 -1
  109. package/dist/core/extensions/builtin/terminal/terminal-manifest.js +62 -11
  110. package/dist/core/extensions/builtin/terminal/terminal-manifest.js.map +1 -1
  111. package/dist/core/extensions/builtin/terminal/terminal-state-gc.d.ts +17 -0
  112. package/dist/core/extensions/builtin/terminal/terminal-state-gc.d.ts.map +1 -0
  113. package/dist/core/extensions/builtin/terminal/terminal-state-gc.js +81 -0
  114. package/dist/core/extensions/builtin/terminal/terminal-state-gc.js.map +1 -0
  115. package/dist/core/extensions/builtin/terminal/tools/context.d.ts +5 -0
  116. package/dist/core/extensions/builtin/terminal/tools/context.d.ts.map +1 -1
  117. package/dist/core/extensions/builtin/terminal/tools/context.js.map +1 -1
  118. package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
  119. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +16 -1
  120. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
  121. package/dist/core/extensions/builtin/terminal/tools/monitor-manifest-binding.d.ts +27 -0
  122. package/dist/core/extensions/builtin/terminal/tools/monitor-manifest-binding.d.ts.map +1 -0
  123. package/dist/core/extensions/builtin/terminal/tools/monitor-manifest-binding.js +75 -0
  124. package/dist/core/extensions/builtin/terminal/tools/monitor-manifest-binding.js.map +1 -0
  125. package/dist/core/extensions/builtin/terminal/tools/monitor-schema.d.ts +35 -0
  126. package/dist/core/extensions/builtin/terminal/tools/monitor-schema.d.ts.map +1 -0
  127. package/dist/core/extensions/builtin/terminal/tools/monitor-schema.js +67 -0
  128. package/dist/core/extensions/builtin/terminal/tools/monitor-schema.js.map +1 -0
  129. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +14 -35
  130. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
  131. package/dist/core/extensions/builtin/terminal/tools/monitor.js +30 -110
  132. package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
  133. package/dist/core/extensions/builtin/todotools/first-turn.d.ts +7 -6
  134. package/dist/core/extensions/builtin/todotools/first-turn.d.ts.map +1 -1
  135. package/dist/core/extensions/builtin/todotools/first-turn.js +8 -8
  136. package/dist/core/extensions/builtin/todotools/first-turn.js.map +1 -1
  137. package/dist/core/extensions/builtin/todotools/index.js +1 -1
  138. package/dist/core/extensions/builtin/todotools/index.js.map +1 -1
  139. package/dist/core/extensions/builtin/todotools/todo-format.js +1 -1
  140. package/dist/core/extensions/builtin/todotools/todo-format.js.map +1 -1
  141. package/dist/core/extensions/builtin/ttsr/detectors/repetitive-turns.d.ts.map +1 -1
  142. package/dist/core/extensions/builtin/ttsr/detectors/repetitive-turns.js +4 -0
  143. package/dist/core/extensions/builtin/ttsr/detectors/repetitive-turns.js.map +1 -1
  144. package/dist/core/extensions/builtin/ttsr/prompts.js +1 -1
  145. package/dist/core/extensions/builtin/ttsr/prompts.js.map +1 -1
  146. package/dist/core/extensions/runner.d.ts +1 -0
  147. package/dist/core/extensions/runner.d.ts.map +1 -1
  148. package/dist/core/extensions/runner.js +1 -0
  149. package/dist/core/extensions/runner.js.map +1 -1
  150. package/dist/core/extensions/types.d.ts +6 -0
  151. package/dist/core/extensions/types.d.ts.map +1 -1
  152. package/dist/core/extensions/types.js.map +1 -1
  153. package/docs/extensions.md +2 -0
  154. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +12 -0
  155. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  156. package/node_modules/@code-yeongyu/senpi-desktop-engine/README.md +31 -0
  157. package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/handshake.d.ts +38 -0
  158. package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/handshake.d.ts.map +1 -0
  159. package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/handshake.js +117 -0
  160. package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/handshake.js.map +1 -0
  161. package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/index.d.ts +3 -0
  162. package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/index.d.ts.map +1 -0
  163. package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/index.js +3 -0
  164. package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/index.js.map +1 -0
  165. package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/locator.d.ts +42 -0
  166. package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/locator.d.ts.map +1 -0
  167. package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/locator.js +79 -0
  168. package/node_modules/@code-yeongyu/senpi-desktop-engine/dist/locator.js.map +1 -0
  169. package/node_modules/@code-yeongyu/senpi-desktop-engine/native/check-prebuild-fresh.mjs +160 -0
  170. package/node_modules/@code-yeongyu/senpi-desktop-engine/native/index.d.ts +20 -0
  171. package/node_modules/@code-yeongyu/senpi-desktop-engine/native/index.js +23 -0
  172. package/node_modules/@code-yeongyu/senpi-desktop-engine/native/prebuilds/darwin-arm64/senpi-desktop-engine +0 -0
  173. package/node_modules/@code-yeongyu/senpi-desktop-engine/package.json +53 -0
  174. package/node_modules/@code-yeongyu/senpi-desktop-prelude/README.md +20 -0
  175. package/node_modules/@code-yeongyu/senpi-desktop-prelude/dist/index.d.ts +2 -0
  176. package/node_modules/@code-yeongyu/senpi-desktop-prelude/dist/index.d.ts.map +1 -0
  177. package/node_modules/@code-yeongyu/senpi-desktop-prelude/dist/index.js +2 -0
  178. package/node_modules/@code-yeongyu/senpi-desktop-prelude/dist/index.js.map +1 -0
  179. package/node_modules/@code-yeongyu/senpi-desktop-prelude/package.json +44 -0
  180. package/node_modules/@code-yeongyu/senpi-desktop-protocol/README.md +20 -0
  181. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/audit.d.ts +38 -0
  182. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/audit.d.ts.map +1 -0
  183. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/audit.js +2 -0
  184. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/audit.js.map +1 -0
  185. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/call.d.ts +28 -0
  186. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/call.d.ts.map +1 -0
  187. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/call.js +102 -0
  188. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/call.js.map +1 -0
  189. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/engine-schema.generated.d.ts +14 -0
  190. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/engine-schema.generated.d.ts.map +1 -0
  191. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/engine-schema.generated.js +71 -0
  192. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/engine-schema.generated.js.map +1 -0
  193. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/index.d.ts +7 -0
  194. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/index.d.ts.map +1 -0
  195. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/index.js +4 -0
  196. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/index.js.map +1 -0
  197. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/json-rpc.d.ts +66 -0
  198. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/json-rpc.d.ts.map +1 -0
  199. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/json-rpc.js +8 -0
  200. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/json-rpc.js.map +1 -0
  201. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/session.d.ts +44 -0
  202. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/session.d.ts.map +1 -0
  203. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/session.js +2 -0
  204. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/session.js.map +1 -0
  205. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/wire.d.ts +59 -0
  206. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/wire.d.ts.map +1 -0
  207. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/wire.js +2 -0
  208. package/node_modules/@code-yeongyu/senpi-desktop-protocol/dist/wire.js.map +1 -0
  209. package/node_modules/@code-yeongyu/senpi-desktop-protocol/package.json +45 -0
  210. package/node_modules/@code-yeongyu/senpi-desktop-service/README.md +27 -0
  211. package/node_modules/@code-yeongyu/senpi-desktop-service/dist/index.d.ts +2 -0
  212. package/node_modules/@code-yeongyu/senpi-desktop-service/dist/index.d.ts.map +1 -0
  213. package/node_modules/@code-yeongyu/senpi-desktop-service/dist/index.js +2 -0
  214. package/node_modules/@code-yeongyu/senpi-desktop-service/dist/index.js.map +1 -0
  215. package/node_modules/@code-yeongyu/senpi-desktop-service/package.json +44 -0
  216. package/node_modules/@code-yeongyu/senpi-desktop-tool/README.md +20 -0
  217. package/node_modules/@code-yeongyu/senpi-desktop-tool/dist/index.d.ts +2 -0
  218. package/node_modules/@code-yeongyu/senpi-desktop-tool/dist/index.d.ts.map +1 -0
  219. package/node_modules/@code-yeongyu/senpi-desktop-tool/dist/index.js +2 -0
  220. package/node_modules/@code-yeongyu/senpi-desktop-tool/dist/index.js.map +1 -0
  221. package/node_modules/@code-yeongyu/senpi-desktop-tool/package.json +44 -0
  222. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  223. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  224. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cerebras.json +1 -1
  225. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  226. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  227. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  228. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.d.ts +2 -0
  229. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.d.ts.map +1 -1
  230. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.js +6 -0
  231. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.js.map +1 -1
  232. package/node_modules/@earendil-works/pi-pty/dist/session-bun.d.ts +1 -0
  233. package/node_modules/@earendil-works/pi-pty/dist/session-bun.d.ts.map +1 -1
  234. package/node_modules/@earendil-works/pi-pty/dist/session-bun.js +1 -0
  235. package/node_modules/@earendil-works/pi-pty/dist/session-bun.js.map +1 -1
  236. package/node_modules/@earendil-works/pi-pty/dist/session-native.js +5 -0
  237. package/node_modules/@earendil-works/pi-pty/dist/session-native.js.map +1 -1
  238. package/node_modules/@earendil-works/pi-pty/dist/session-raw-tail.d.ts +11 -0
  239. package/node_modules/@earendil-works/pi-pty/dist/session-raw-tail.d.ts.map +1 -0
  240. package/node_modules/@earendil-works/pi-pty/dist/session-raw-tail.js +24 -0
  241. package/node_modules/@earendil-works/pi-pty/dist/session-raw-tail.js.map +1 -0
  242. package/node_modules/@earendil-works/pi-pty/dist/session-types.d.ts +2 -0
  243. package/node_modules/@earendil-works/pi-pty/dist/session-types.d.ts.map +1 -1
  244. package/node_modules/@earendil-works/pi-pty/dist/session-types.js.map +1 -1
  245. package/node_modules/@earendil-works/pi-pty/dist/session.d.ts +4 -5
  246. package/node_modules/@earendil-works/pi-pty/dist/session.d.ts.map +1 -1
  247. package/node_modules/@earendil-works/pi-pty/dist/session.js +11 -16
  248. package/node_modules/@earendil-works/pi-pty/dist/session.js.map +1 -1
  249. package/node_modules/@earendil-works/pi-pty/native/prebuilds/darwin-arm64/senpi_pty.darwin-arm64.node +0 -0
  250. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  251. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  252. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  253. package/package.json +22 -7
  254. package/dist/bundle/chunks/chunk-GFUZPFHN.js +0 -2
  255. package/dist/bundle/chunks/chunk-JBOV6VNK.js +0 -8
  256. package/dist/bundle/chunks/runtime-session-F3EXC44D.js +0 -2
@@ -0,0 +1,75 @@
1
+ import { MAX_DURABLE_MONITORS } from "../shared.js";
2
+ import { errorResult } from "./context.js";
3
+ const manifestWriters = new Map();
4
+ /**
5
+ * Specs captured before a writer is bound (lazy persistence, or a generation waiting on a
6
+ * foreign lease holder). Drained in order the moment the writer binds, so nothing the agent
7
+ * registered is lost to timing. Bounded: past the cap the oldest spec is dropped.
8
+ */
9
+ const pendingSpecs = new Map();
10
+ export const MAX_PENDING_MONITOR_SPECS = 32;
11
+ /** Bind the session's manifest writer so monitor tool calls can hand it specs captured at the call site. */
12
+ export function bindTerminalManifestWriter(sessionId, writer) {
13
+ manifestWriters.set(sessionId, writer);
14
+ const queued = pendingSpecs.get(sessionId);
15
+ pendingSpecs.delete(sessionId);
16
+ for (const registration of queued ?? [])
17
+ void writer.recordRegister(registration);
18
+ }
19
+ export function unbindTerminalManifestWriter(sessionId) {
20
+ manifestWriters.delete(sessionId);
21
+ }
22
+ export function pendingDurableSpecCount(sessionId) {
23
+ let count = 0;
24
+ for (const registration of pendingSpecs.get(sessionId) ?? [])
25
+ if (registration.spec.persistent)
26
+ count += 1;
27
+ return count;
28
+ }
29
+ /** The durability session key for a tool context: the agent session id, when the context carries one. */
30
+ export function manifestSessionKey(ctx) {
31
+ return ctx.getSessionContext?.()?.sessionManager?.getSessionId?.();
32
+ }
33
+ /**
34
+ * Hand a spec captured at the monitor tool call site to the session's writer, or queue it until
35
+ * one binds. The write is awaited: the tool result must not claim a watch before its manifest
36
+ * entry exists on disk.
37
+ */
38
+ export async function handMonitorSpec(sessionKey, registration) {
39
+ if (sessionKey === undefined)
40
+ return;
41
+ const writer = manifestWriters.get(sessionKey);
42
+ if (writer) {
43
+ await writer.recordRegister(registration);
44
+ return;
45
+ }
46
+ const queue = pendingSpecs.get(sessionKey) ?? [];
47
+ queue.push(registration);
48
+ if (queue.length > MAX_PENDING_MONITOR_SPECS)
49
+ queue.shift();
50
+ pendingSpecs.set(sessionKey, queue);
51
+ }
52
+ /** Persist a durable file watch's baseline checkpoint through the writer's debounced path. */
53
+ export function handFileCheckpoint(sessionKey, monitorId, registry, runtimeId) {
54
+ const writer = sessionKey === undefined ? undefined : manifestWriters.get(sessionKey);
55
+ const checkpoint = registry.fileCheckpoint(runtimeId);
56
+ if (writer && checkpoint)
57
+ writer.scheduleCheckpoint(monitorId, checkpoint);
58
+ }
59
+ /**
60
+ * Admission control for a durable create: refuse once the session already holds
61
+ * MAX_DURABLE_MONITORS restart-surviving monitors, counting both the bound writer's entries
62
+ * and the specs still queued for a writer. Checked BEFORE any spawn or registry registration
63
+ * so a refused call leaves no PTY and no manifest entry behind. A context with no session
64
+ * key persists nothing, so it has no durable population to cap.
65
+ */
66
+ export function durableAdmissionError(ctx) {
67
+ const sessionKey = manifestSessionKey(ctx);
68
+ if (sessionKey === undefined)
69
+ return undefined;
70
+ const held = (manifestWriters.get(sessionKey)?.durableCount() ?? 0) + pendingDurableSpecCount(sessionKey);
71
+ if (held < MAX_DURABLE_MONITORS)
72
+ return undefined;
73
+ return errorResult(`Cannot start another persistent monitor: this session already holds ${MAX_DURABLE_MONITORS} durable monitors (the maximum). Stop one with kill_bash first.`);
74
+ }
75
+ //# sourceMappingURL=monitor-manifest-binding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitor-manifest-binding.js","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/monitor-manifest-binding.ts"],"names":[],"mappings":"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","sourcesContent":["import type { MonitorRegistry } from \"../monitor-registry.ts\";\nimport { MAX_DURABLE_MONITORS } from \"../shared.ts\";\nimport type { MonitorRegistration, TerminalManifestWriter } from \"../terminal-manifest.ts\";\nimport { errorResult, type TerminalToolContext, type TerminalToolResult } from \"./context.ts\";\n\nconst manifestWriters = new Map<string, TerminalManifestWriter>();\n/**\n * Specs captured before a writer is bound (lazy persistence, or a generation waiting on a\n * foreign lease holder). Drained in order the moment the writer binds, so nothing the agent\n * registered is lost to timing. Bounded: past the cap the oldest spec is dropped.\n */\nconst pendingSpecs = new Map<string, MonitorRegistration[]>();\nexport const MAX_PENDING_MONITOR_SPECS = 32;\n\n/** Bind the session's manifest writer so monitor tool calls can hand it specs captured at the call site. */\nexport function bindTerminalManifestWriter(sessionId: string, writer: TerminalManifestWriter): void {\n\tmanifestWriters.set(sessionId, writer);\n\tconst queued = pendingSpecs.get(sessionId);\n\tpendingSpecs.delete(sessionId);\n\tfor (const registration of queued ?? []) void writer.recordRegister(registration);\n}\n\nexport function unbindTerminalManifestWriter(sessionId: string): void {\n\tmanifestWriters.delete(sessionId);\n}\n\nexport function pendingDurableSpecCount(sessionId: string): number {\n\tlet count = 0;\n\tfor (const registration of pendingSpecs.get(sessionId) ?? []) if (registration.spec.persistent) count += 1;\n\treturn count;\n}\n\n/** The durability session key for a tool context: the agent session id, when the context carries one. */\nexport function manifestSessionKey(ctx: TerminalToolContext): string | undefined {\n\treturn ctx.getSessionContext?.()?.sessionManager?.getSessionId?.();\n}\n\n/**\n * Hand a spec captured at the monitor tool call site to the session's writer, or queue it until\n * one binds. The write is awaited: the tool result must not claim a watch before its manifest\n * entry exists on disk.\n */\nexport async function handMonitorSpec(\n\tsessionKey: string | undefined,\n\tregistration: MonitorRegistration,\n): Promise<void> {\n\tif (sessionKey === undefined) return;\n\tconst writer = manifestWriters.get(sessionKey);\n\tif (writer) {\n\t\tawait writer.recordRegister(registration);\n\t\treturn;\n\t}\n\tconst queue = pendingSpecs.get(sessionKey) ?? [];\n\tqueue.push(registration);\n\tif (queue.length > MAX_PENDING_MONITOR_SPECS) queue.shift();\n\tpendingSpecs.set(sessionKey, queue);\n}\n\n/** Persist a durable file watch's baseline checkpoint through the writer's debounced path. */\nexport function handFileCheckpoint(\n\tsessionKey: string | undefined,\n\tmonitorId: string,\n\tregistry: MonitorRegistry,\n\truntimeId: string,\n): void {\n\tconst writer = sessionKey === undefined ? undefined : manifestWriters.get(sessionKey);\n\tconst checkpoint = registry.fileCheckpoint(runtimeId);\n\tif (writer && checkpoint) writer.scheduleCheckpoint(monitorId, checkpoint);\n}\n\n/**\n * Admission control for a durable create: refuse once the session already holds\n * MAX_DURABLE_MONITORS restart-surviving monitors, counting both the bound writer's entries\n * and the specs still queued for a writer. Checked BEFORE any spawn or registry registration\n * so a refused call leaves no PTY and no manifest entry behind. A context with no session\n * key persists nothing, so it has no durable population to cap.\n */\nexport function durableAdmissionError(ctx: TerminalToolContext): TerminalToolResult | undefined {\n\tconst sessionKey = manifestSessionKey(ctx);\n\tif (sessionKey === undefined) return undefined;\n\tconst held = (manifestWriters.get(sessionKey)?.durableCount() ?? 0) + pendingDurableSpecCount(sessionKey);\n\tif (held < MAX_DURABLE_MONITORS) return undefined;\n\treturn errorResult(\n\t\t`Cannot start another persistent monitor: this session already holds ${MAX_DURABLE_MONITORS} durable monitors (the maximum). Stop one with kill_bash first.`,\n\t);\n}\n"]}
@@ -0,0 +1,35 @@
1
+ import { type Static, Type } from "typebox";
2
+ export declare const DEFAULT_MONITOR_TIMEOUT_MS = 300000;
3
+ export declare const MAX_MONITOR_TIMEOUT_MS = 3600000;
4
+ /**
5
+ * One flat object schema, no top-level union: several provider payload paths
6
+ * (e.g. Anthropic's legacy input_schema conversion) rebuild tool schemas from
7
+ * top-level `properties` only, so a root anyOf would reach the model as an
8
+ * empty schema. Branch requirements are enforced at runtime in `execute`.
9
+ */
10
+ export declare const monitorSchema: Type.TObject<{
11
+ action: Type.TOptional<Type.TUnsafe<"create" | "rearm">>;
12
+ description: Type.TOptional<Type.TString>;
13
+ command: Type.TOptional<Type.TString>;
14
+ path: Type.TOptional<Type.TString>;
15
+ event: Type.TOptional<Type.TUnsafe<"create" | "modify">>;
16
+ filter: Type.TOptional<Type.TString>;
17
+ timeout_ms: Type.TOptional<Type.TNumber>;
18
+ persistent: Type.TOptional<Type.TBoolean>;
19
+ bash_id: Type.TOptional<Type.TString>;
20
+ }>;
21
+ export type MonitorInput = Static<typeof monitorSchema>;
22
+ export type MonitorCreateInput = MonitorInput & {
23
+ description: string;
24
+ command: string;
25
+ };
26
+ export type FileMonitorCreateInput = MonitorInput & {
27
+ description: string;
28
+ path: string;
29
+ };
30
+ export declare function isFileCreateInput(input: MonitorInput): input is FileMonitorCreateInput;
31
+ export declare function isCreateInput(input: MonitorInput): input is MonitorCreateInput;
32
+ export declare function resolveDimension(value: number | undefined, fallback: number): number;
33
+ export declare function resolveTimeoutMs(value: number | undefined): number;
34
+ export declare function compileFilter(filter: string | undefined): RegExp | undefined;
35
+ //# sourceMappingURL=monitor-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitor-schema.d.ts","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/monitor-schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,eAAO,MAAM,0BAA0B,SAAU,CAAC;AAClD,eAAO,MAAM,sBAAsB,UAAY,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;EAkDxB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC;AAExD,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AACzF,MAAM,MAAM,sBAAsB,GAAG,YAAY,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,KAAK,IAAI,sBAAsB,CAOtF;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,KAAK,IAAI,kBAAkB,CAO9E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGpF;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAGlE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAG5E"}
@@ -0,0 +1,67 @@
1
+ import { StringEnum } from "@earendil-works/pi-ai";
2
+ import { Type } from "typebox";
3
+ export const DEFAULT_MONITOR_TIMEOUT_MS = 300_000;
4
+ export const MAX_MONITOR_TIMEOUT_MS = 3_600_000;
5
+ /**
6
+ * One flat object schema, no top-level union: several provider payload paths
7
+ * (e.g. Anthropic's legacy input_schema conversion) rebuild tool schemas from
8
+ * top-level `properties` only, so a root anyOf would reach the model as an
9
+ * empty schema. Branch requirements are enforced at runtime in `execute`.
10
+ */
11
+ export const monitorSchema = Type.Object({
12
+ action: Type.Optional(StringEnum(["create", "rearm"], {
13
+ description: "Defaults to create. rearm resumes a monitor paused by the wake budget.",
14
+ })),
15
+ description: Type.Optional(Type.String({
16
+ minLength: 1,
17
+ maxLength: 200,
18
+ description: "Create (required): specific label shown with every event, e.g. 'errors in deploy.log'.",
19
+ })),
20
+ command: Type.Optional(Type.String({
21
+ description: "Create, command branch (XOR path): shell command to run and watch in a PTY-backed monitor session.",
22
+ })),
23
+ path: Type.Optional(Type.String({
24
+ minLength: 1,
25
+ description: "Create, file branch (XOR command): one regular file to watch natively, whose parent directory must already exist; takes no filter.",
26
+ })),
27
+ event: Type.Optional(StringEnum(["create", "modify"], {
28
+ description: "File branch only: which file event fires the watch (defaults to create).",
29
+ })),
30
+ filter: Type.Optional(Type.String({ description: "Only PTY output lines matching this regex become monitor events." })),
31
+ timeout_ms: Type.Optional(Type.Number({
32
+ minimum: 1,
33
+ maximum: MAX_MONITOR_TIMEOUT_MS,
34
+ description: "Watcher deadline in milliseconds (default 300000; ignored when persistent).",
35
+ })),
36
+ persistent: Type.Optional(Type.Boolean({
37
+ description: "Standing watch: no deadline, and it survives a session restart (command re-run once, file rescanned and any detached change reported). Expires 7 days after creation; max 5 per session; stop one with kill_bash.",
38
+ })),
39
+ bash_id: Type.Optional(Type.String({ description: "Rearm: paused monitor id (mon_ or bash_id) to resume; omit for all paused." })),
40
+ });
41
+ export function isFileCreateInput(input) {
42
+ return (typeof input.description === "string" &&
43
+ input.description.length > 0 &&
44
+ typeof input.path === "string" &&
45
+ input.path.length > 0);
46
+ }
47
+ export function isCreateInput(input) {
48
+ return (typeof input.description === "string" &&
49
+ input.description.length > 0 &&
50
+ typeof input.command === "string" &&
51
+ input.command.length > 0);
52
+ }
53
+ export function resolveDimension(value, fallback) {
54
+ if (value === undefined || !Number.isFinite(value) || value < 1)
55
+ return fallback;
56
+ return Math.trunc(value);
57
+ }
58
+ export function resolveTimeoutMs(value) {
59
+ const timeout = value ?? DEFAULT_MONITOR_TIMEOUT_MS;
60
+ return Math.min(Math.max(Math.trunc(timeout), 1), MAX_MONITOR_TIMEOUT_MS);
61
+ }
62
+ export function compileFilter(filter) {
63
+ if (filter === undefined)
64
+ return undefined;
65
+ return new RegExp(filter);
66
+ }
67
+ //# sourceMappingURL=monitor-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitor-schema.js","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/monitor-schema.ts"],"names":[],"mappings":"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","sourcesContent":["import { StringEnum } from \"@earendil-works/pi-ai\";\nimport { type Static, Type } from \"typebox\";\n\nexport const DEFAULT_MONITOR_TIMEOUT_MS = 300_000;\nexport const MAX_MONITOR_TIMEOUT_MS = 3_600_000;\n\n/**\n * One flat object schema, no top-level union: several provider payload paths\n * (e.g. Anthropic's legacy input_schema conversion) rebuild tool schemas from\n * top-level `properties` only, so a root anyOf would reach the model as an\n * empty schema. Branch requirements are enforced at runtime in `execute`.\n */\nexport const monitorSchema = Type.Object({\n\taction: Type.Optional(\n\t\tStringEnum([\"create\", \"rearm\"] as const, {\n\t\t\tdescription: \"Defaults to create. rearm resumes a monitor paused by the wake budget.\",\n\t\t}),\n\t),\n\tdescription: Type.Optional(\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tmaxLength: 200,\n\t\t\tdescription: \"Create (required): specific label shown with every event, e.g. 'errors in deploy.log'.\",\n\t\t}),\n\t),\n\tcommand: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Create, command branch (XOR path): shell command to run and watch in a PTY-backed monitor session.\",\n\t\t}),\n\t),\n\tpath: Type.Optional(\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tdescription:\n\t\t\t\t\"Create, file branch (XOR command): one regular file to watch natively, whose parent directory must already exist; takes no filter.\",\n\t\t}),\n\t),\n\tevent: Type.Optional(\n\t\tStringEnum([\"create\", \"modify\"] as const, {\n\t\t\tdescription: \"File branch only: which file event fires the watch (defaults to create).\",\n\t\t}),\n\t),\n\tfilter: Type.Optional(\n\t\tType.String({ description: \"Only PTY output lines matching this regex become monitor events.\" }),\n\t),\n\ttimeout_ms: Type.Optional(\n\t\tType.Number({\n\t\t\tminimum: 1,\n\t\t\tmaximum: MAX_MONITOR_TIMEOUT_MS,\n\t\t\tdescription: \"Watcher deadline in milliseconds (default 300000; ignored when persistent).\",\n\t\t}),\n\t),\n\tpersistent: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Standing watch: no deadline, and it survives a session restart (command re-run once, file rescanned and any detached change reported). Expires 7 days after creation; max 5 per session; stop one with kill_bash.\",\n\t\t}),\n\t),\n\tbash_id: Type.Optional(\n\t\tType.String({ description: \"Rearm: paused monitor id (mon_ or bash_id) to resume; omit for all paused.\" }),\n\t),\n});\nexport type MonitorInput = Static<typeof monitorSchema>;\n\nexport type MonitorCreateInput = MonitorInput & { description: string; command: string };\nexport type FileMonitorCreateInput = MonitorInput & { description: string; path: string };\n\nexport function isFileCreateInput(input: MonitorInput): input is FileMonitorCreateInput {\n\treturn (\n\t\ttypeof input.description === \"string\" &&\n\t\tinput.description.length > 0 &&\n\t\ttypeof input.path === \"string\" &&\n\t\tinput.path.length > 0\n\t);\n}\n\nexport function isCreateInput(input: MonitorInput): input is MonitorCreateInput {\n\treturn (\n\t\ttypeof input.description === \"string\" &&\n\t\tinput.description.length > 0 &&\n\t\ttypeof input.command === \"string\" &&\n\t\tinput.command.length > 0\n\t);\n}\n\nexport function resolveDimension(value: number | undefined, fallback: number): number {\n\tif (value === undefined || !Number.isFinite(value) || value < 1) return fallback;\n\treturn Math.trunc(value);\n}\n\nexport function resolveTimeoutMs(value: number | undefined): number {\n\tconst timeout = value ?? DEFAULT_MONITOR_TIMEOUT_MS;\n\treturn Math.min(Math.max(Math.trunc(timeout), 1), MAX_MONITOR_TIMEOUT_MS);\n}\n\nexport function compileFilter(filter: string | undefined): RegExp | undefined {\n\tif (filter === undefined) return undefined;\n\treturn new RegExp(filter);\n}\n"]}
@@ -1,46 +1,25 @@
1
- import { type Static, Type } from "typebox";
2
- import type { TerminalManifestWriter } from "../terminal-manifest.ts";
3
1
  import { type TerminalToolContext, type TerminalToolResult } from "./context.ts";
2
+ import { type MonitorInput } from "./monitor-schema.ts";
4
3
  import { renderMonitorCall } from "./render.ts";
5
- export declare const DEFAULT_MONITOR_TIMEOUT_MS = 300000;
6
- export declare const MAX_MONITOR_TIMEOUT_MS = 3600000;
7
- /**
8
- * One flat object schema, no top-level union: several provider payload paths
9
- * (e.g. Anthropic's legacy input_schema conversion) rebuild tool schemas from
10
- * top-level `properties` only, so a root anyOf would reach the model as an
11
- * empty schema. Branch requirements are enforced at runtime in `execute`.
12
- */
13
- export declare const monitorSchema: Type.TObject<{
14
- action: Type.TOptional<Type.TUnsafe<"create" | "rearm">>;
15
- description: Type.TOptional<Type.TString>;
16
- command: Type.TOptional<Type.TString>;
17
- path: Type.TOptional<Type.TString>;
18
- event: Type.TOptional<Type.TUnsafe<"create" | "modify">>;
19
- filter: Type.TOptional<Type.TString>;
20
- timeout_ms: Type.TOptional<Type.TNumber>;
21
- persistent: Type.TOptional<Type.TBoolean>;
22
- bash_id: Type.TOptional<Type.TString>;
23
- }>;
24
- export type MonitorInput = Static<typeof monitorSchema>;
25
- /** Bind the session's manifest writer so monitor tool calls can hand it specs captured at the call site. */
26
- export declare function bindTerminalManifestWriter(sessionId: string, writer: TerminalManifestWriter): void;
27
- export declare function unbindTerminalManifestWriter(sessionId: string): void;
4
+ export { bindTerminalManifestWriter, MAX_PENDING_MONITOR_SPECS, pendingDurableSpecCount, unbindTerminalManifestWriter, } from "./monitor-manifest-binding.ts";
5
+ export { DEFAULT_MONITOR_TIMEOUT_MS, MAX_MONITOR_TIMEOUT_MS, type MonitorInput, monitorSchema, } from "./monitor-schema.ts";
6
+ /** Build the PTY-backed monitor tool. Monitor handles share TerminalManager's bash_N namespace. */
28
7
  export declare function createMonitorTool(ctx: TerminalToolContext): {
29
8
  name: string;
30
9
  label: string;
31
10
  description: string;
32
11
  promptSnippet: string;
33
12
  promptGuidelines: string[];
34
- parameters: Type.TObject<{
35
- action: Type.TOptional<Type.TUnsafe<"create" | "rearm">>;
36
- description: Type.TOptional<Type.TString>;
37
- command: Type.TOptional<Type.TString>;
38
- path: Type.TOptional<Type.TString>;
39
- event: Type.TOptional<Type.TUnsafe<"create" | "modify">>;
40
- filter: Type.TOptional<Type.TString>;
41
- timeout_ms: Type.TOptional<Type.TNumber>;
42
- persistent: Type.TOptional<Type.TBoolean>;
43
- bash_id: Type.TOptional<Type.TString>;
13
+ parameters: import("typebox").TObject<{
14
+ action: import("typebox").TOptional<import("typebox").TUnsafe<"create" | "rearm">>;
15
+ description: import("typebox").TOptional<import("typebox").TString>;
16
+ command: import("typebox").TOptional<import("typebox").TString>;
17
+ path: import("typebox").TOptional<import("typebox").TString>;
18
+ event: import("typebox").TOptional<import("typebox").TUnsafe<"create" | "modify">>;
19
+ filter: import("typebox").TOptional<import("typebox").TString>;
20
+ timeout_ms: import("typebox").TOptional<import("typebox").TNumber>;
21
+ persistent: import("typebox").TOptional<import("typebox").TBoolean>;
22
+ bash_id: import("typebox").TOptional<import("typebox").TString>;
44
23
  }>;
45
24
  renderCall: typeof renderMonitorCall;
46
25
  execute(_toolCallId: string, input: MonitorInput, _signal?: AbortSignal, _onUpdate?: undefined, execCtx?: {
@@ -1 +1 @@
1
- {"version":3,"file":"monitor.d.ts","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/monitor.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAU5C,OAAO,KAAK,EAAuB,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAC3F,OAAO,EAGN,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAEvB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGhD,eAAO,MAAM,0BAA0B,SAAU,CAAC;AAClD,eAAO,MAAM,sBAAsB,UAAY,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;EAkDxB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC;AAmGxD,4GAA4G;AAC5G,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAElG;AAED,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAEpE;AAwCD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,mBAAmB;;;;;;;;;;;;;;;;;;yBAwB1C,MAAM,SACZ,YAAY,YACT,WAAW,cACT,SAAS,YACX;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GACxB,OAAO,CAAC,kBAAkB,CAAC;EAmF/B"}
1
+ {"version":3,"file":"monitor.d.ts","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/monitor.ts"],"names":[],"mappings":"AAYA,OAAO,EAGN,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAEvB,MAAM,cAAc,CAAC;AAOtB,OAAO,EAKN,KAAK,YAAY,EAIjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,EACN,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,4BAA4B,GAC5B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACN,0BAA0B,EAC1B,sBAAsB,EACtB,KAAK,YAAY,EACjB,aAAa,GACb,MAAM,qBAAqB,CAAC;AAwE7B,mGAAmG;AACnG,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,mBAAmB;;;;;;;;;;;;;;;;;;yBAyB1C,MAAM,SACZ,YAAY,YACT,WAAW,cACT,SAAS,YACX;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GACxB,OAAO,CAAC,kBAAkB,CAAC;EAuF/B"}
@@ -1,76 +1,15 @@
1
1
  import { resolve } from "node:path";
2
- import { StringEnum } from "@earendil-works/pi-ai";
3
- import { Type } from "typebox";
4
2
  import { APPROVED_MONITOR_PARENT } from "../monitor-permission.js";
5
- import { MonitorRegistry } from "../monitor-registry.js";
6
- import { DEFAULT_COLS, DEFAULT_ROWS, DURABLE_MONITOR_EXPIRY_MS, MAX_DURABLE_MONITORS, TERMINAL_MONITOR_TOOL, } from "../shared.js";
3
+ import { allocateMonitorId, MonitorRegistry } from "../monitor-registry.js";
4
+ import { ensureMonitorStateDir, terminalStateDir } from "../monitor-state-dir.js";
5
+ import { DEFAULT_COLS, DEFAULT_ROWS, DURABLE_MONITOR_EXPIRY_MS, MONITOR_ENV_ID, MONITOR_ENV_STATE_DIR, TERMINAL_MONITOR_TOOL, } from "../shared.js";
7
6
  import { errorResult, resolveTerminalId, textResult, } from "./context.js";
7
+ import { durableAdmissionError, handFileCheckpoint, handMonitorSpec, manifestSessionKey, } from "./monitor-manifest-binding.js";
8
+ import { compileFilter, isCreateInput, isFileCreateInput, monitorSchema, resolveDimension, resolveTimeoutMs, } from "./monitor-schema.js";
8
9
  import { renderMonitorCall } from "./render.js";
9
10
  import { spawnCommandSession } from "./spawn.js";
10
- export const DEFAULT_MONITOR_TIMEOUT_MS = 300_000;
11
- export const MAX_MONITOR_TIMEOUT_MS = 3_600_000;
12
- /**
13
- * One flat object schema, no top-level union: several provider payload paths
14
- * (e.g. Anthropic's legacy input_schema conversion) rebuild tool schemas from
15
- * top-level `properties` only, so a root anyOf would reach the model as an
16
- * empty schema. Branch requirements are enforced at runtime in `execute`.
17
- */
18
- export const monitorSchema = Type.Object({
19
- action: Type.Optional(StringEnum(["create", "rearm"], {
20
- description: "Defaults to create. rearm resumes a monitor paused by the wake budget.",
21
- })),
22
- description: Type.Optional(Type.String({
23
- minLength: 1,
24
- maxLength: 200,
25
- description: "Create (required): specific label shown with every event, e.g. 'errors in deploy.log'.",
26
- })),
27
- command: Type.Optional(Type.String({
28
- description: "Create, command branch (XOR path): shell command to run and watch in a PTY-backed monitor session.",
29
- })),
30
- path: Type.Optional(Type.String({
31
- minLength: 1,
32
- description: "Create, file branch (XOR command): one regular file to watch natively, whose parent directory must already exist; takes no filter.",
33
- })),
34
- event: Type.Optional(StringEnum(["create", "modify"], {
35
- description: "File branch only: which file event fires the watch (defaults to create).",
36
- })),
37
- filter: Type.Optional(Type.String({ description: "Only PTY output lines matching this regex become monitor events." })),
38
- timeout_ms: Type.Optional(Type.Number({
39
- minimum: 1,
40
- maximum: MAX_MONITOR_TIMEOUT_MS,
41
- description: "Watcher deadline in milliseconds (default 300000; ignored when persistent).",
42
- })),
43
- persistent: Type.Optional(Type.Boolean({
44
- description: "Standing watch: no deadline, and it survives a session restart (command re-run once, file rescanned and any detached change reported). Expires 7 days after creation; max 5 per session; stop one with kill_bash.",
45
- })),
46
- bash_id: Type.Optional(Type.String({ description: "Rearm: paused monitor id (mon_ or bash_id) to resume; omit for all paused." })),
47
- });
48
- function isFileCreateInput(input) {
49
- return (typeof input.description === "string" &&
50
- input.description.length > 0 &&
51
- typeof input.path === "string" &&
52
- input.path.length > 0);
53
- }
54
- function isCreateInput(input) {
55
- return (typeof input.description === "string" &&
56
- input.description.length > 0 &&
57
- typeof input.command === "string" &&
58
- input.command.length > 0);
59
- }
60
- function resolveDimension(value, fallback) {
61
- if (value === undefined || !Number.isFinite(value) || value < 1)
62
- return fallback;
63
- return Math.trunc(value);
64
- }
65
- function resolveTimeoutMs(value) {
66
- const timeout = value ?? DEFAULT_MONITOR_TIMEOUT_MS;
67
- return Math.min(Math.max(Math.trunc(timeout), 1), MAX_MONITOR_TIMEOUT_MS);
68
- }
69
- function compileFilter(filter) {
70
- if (filter === undefined)
71
- return undefined;
72
- return new RegExp(filter);
73
- }
11
+ export { bindTerminalManifestWriter, MAX_PENDING_MONITOR_SPECS, pendingDurableSpecCount, unbindTerminalManifestWriter, } from "./monitor-manifest-binding.js";
12
+ export { DEFAULT_MONITOR_TIMEOUT_MS, MAX_MONITOR_TIMEOUT_MS, monitorSchema, } from "./monitor-schema.js";
74
13
  async function createMonitor(ctx, registry, input, execCtx) {
75
14
  let filter;
76
15
  try {
@@ -84,15 +23,24 @@ async function createMonitor(ctx, registry, input, execCtx) {
84
23
  const cwd = resolve(execCtx?.cwd ?? ctx.cwd);
85
24
  const timeoutMs = input.persistent ? undefined : resolveTimeoutMs(input.timeout_ms);
86
25
  const deadlineMs = timeoutMs === undefined ? null : Date.now() + timeoutMs;
26
+ // The stable id is allocated BEFORE the spawn so the command can see it in its environment.
27
+ const monitorId = allocateMonitorId();
28
+ const terminalDir = terminalStateDir(ctx.getSessionContext?.());
29
+ const envOverrides = { [MONITOR_ENV_ID]: monitorId };
30
+ if (input.persistent === true && terminalDir !== undefined) {
31
+ envOverrides[MONITOR_ENV_STATE_DIR] = await ensureMonitorStateDir(terminalDir, monitorId);
32
+ }
87
33
  const { id, runtime } = await spawnCommandSession(ctx, {
88
34
  command: input.command,
89
35
  cols: resolveDimension(undefined, ctx.defaultCols || DEFAULT_COLS),
90
36
  rows: resolveDimension(undefined, ctx.defaultRows || DEFAULT_ROWS),
91
37
  cwd,
38
+ envOverrides,
92
39
  ...(timeoutMs === undefined ? {} : { timeoutMs }),
93
40
  });
94
41
  ctx.onMonitorRearmed?.(id);
95
- const monitorId = registry.register({
42
+ registry.register({
43
+ monitorId,
96
44
  id,
97
45
  description: input.description,
98
46
  runtime,
@@ -108,7 +56,8 @@ async function createMonitor(ctx, registry, input, execCtx) {
108
56
  ctx.manager.bindMonitorId(monitorId, id);
109
57
  // The tool call site is the only place the branch inputs (command, persistent, filter)
110
58
  // live; hand the captured spec to the session's manifest writer for durable recording.
111
- handMonitorSpec(manifestSessionKey(ctx), {
59
+ const identity = runtime.identity();
60
+ await handMonitorSpec(manifestSessionKey(ctx), {
112
61
  monitorId,
113
62
  spec: {
114
63
  kind: "command",
@@ -118,49 +67,14 @@ async function createMonitor(ctx, registry, input, execCtx) {
118
67
  cwd,
119
68
  persistent: input.persistent === true,
120
69
  },
70
+ ...(identity === undefined ? {} : { runtime: identity }),
71
+ ...(deadlineMs === null ? {} : { deadlineMs }),
121
72
  });
122
73
  return textResult(`Monitor started with ID: ${monitorId}`, {
123
74
  details: { monitor_id: monitorId, bash_id: id, monitor: true },
124
75
  });
125
76
  }
126
77
  /** Build the PTY-backed monitor tool. Monitor handles share TerminalManager's bash_N namespace. */
127
- const manifestWriters = new Map();
128
- /** Bind the session's manifest writer so monitor tool calls can hand it specs captured at the call site. */
129
- export function bindTerminalManifestWriter(sessionId, writer) {
130
- manifestWriters.set(sessionId, writer);
131
- }
132
- export function unbindTerminalManifestWriter(sessionId) {
133
- manifestWriters.delete(sessionId);
134
- }
135
- /** The durability session key for a tool context: the agent session id, when the context carries one. */
136
- function manifestSessionKey(ctx) {
137
- return ctx.getSessionContext?.()?.sessionManager?.getSessionId?.();
138
- }
139
- /** Hand a spec captured at the monitor tool call site to the session's bound writer, if any. */
140
- function handMonitorSpec(sessionKey, registration) {
141
- const writer = sessionKey === undefined ? undefined : manifestWriters.get(sessionKey);
142
- void writer?.recordRegister(registration);
143
- }
144
- /** Persist a durable file watch's baseline checkpoint through the writer's debounced path. */
145
- function handFileCheckpoint(sessionKey, monitorId, registry, runtimeId) {
146
- const writer = sessionKey === undefined ? undefined : manifestWriters.get(sessionKey);
147
- const checkpoint = registry.fileCheckpoint(runtimeId);
148
- if (writer && checkpoint)
149
- writer.scheduleCheckpoint(monitorId, checkpoint);
150
- }
151
- /**
152
- * Admission control for a durable create: refuse once the session already holds
153
- * MAX_DURABLE_MONITORS restart-surviving monitors. Checked BEFORE any spawn or registry
154
- * registration so a refused call leaves no PTY and no manifest entry behind. A context
155
- * with no bound writer persists nothing, so it has no durable population to cap.
156
- */
157
- function durableAdmissionError(ctx) {
158
- const sessionKey = manifestSessionKey(ctx);
159
- const writer = sessionKey === undefined ? undefined : manifestWriters.get(sessionKey);
160
- if (writer === undefined || writer.durableCount() < MAX_DURABLE_MONITORS)
161
- return undefined;
162
- return errorResult(`Cannot start another persistent monitor: this session already holds ${MAX_DURABLE_MONITORS} durable monitors (the maximum). Stop one with kill_bash first.`);
163
- }
164
78
  export function createMonitorTool(ctx) {
165
79
  let fallbackRegistry;
166
80
  const getRegistry = () => {
@@ -179,6 +93,7 @@ export function createMonitorTool(ctx) {
179
93
  "Waiting on observable state (CI checks, builds, log patterns, deploys, a file landing) means a monitor, never a foreground sleep/poll loop.",
180
94
  'Waiting for one file to appear or change is the path branch: `monitor({ description, path, event? })` beats wrapping `test -f` in a shell poll loop; a file that already exists needs `event: "modify"`, since `create` only fires on appearance, and registration needs the parent directory to exist already — when the run creates that directory too, use the `command` branch instead.',
181
95
  "Shape the command for the events you need: one-shot gate = `until <cond>; do sleep 1; done; printf 'READY\\n'` with filter ^READY$; stream = `tail -n 0 -F <log> | grep --line-buffered <pat>` with persistent: true, then kill_bash.",
96
+ 'Persistent command watches are re-run after a restart with `SENPI_MONITOR_RESTORED=1`; keep any baseline in `$SENPI_MONITOR_STATE_DIR` so a move during downtime (at most `$SENPI_MONITOR_DOWNTIME_MS` ms) is still reported: `b="$SENPI_MONITOR_STATE_DIR/base"; p=$(cat "$b" 2>/dev/null || git rev-parse origin/main | tee "$b"); while sleep 30; do git fetch -q || true; n=$(git rev-parse origin/main); [ "$n" != "$p" ] && { echo "MOVED $p..$n"; p=$n; echo "$p" > "$b"; }; done`. A watch whose command exits is not restarted, so keep failures inside the loop non-fatal.',
182
97
  "Sleep loops belong INSIDE the monitor command, never in your turn: about to sleep, re-poll bash_output, or foreground-block on a long command means register a monitor and keep working.",
183
98
  ],
184
99
  parameters: monitorSchema,
@@ -212,7 +127,11 @@ export function createMonitorTool(ctx) {
212
127
  const commandInput = isCreateInput(input);
213
128
  if (fileInput && commandInput)
214
129
  return errorResult("monitor accepts either command or path, not both.");
215
- // Admission runs before either create branch touches a PTY or the registry.
130
+ // Every create binds persistence first (lazy lease + recorder: an ephemeral watch with time
131
+ // left is restorable too), then durable admission runs before either create branch touches
132
+ // a PTY or the registry.
133
+ if (fileInput || commandInput)
134
+ await ctx.ensurePersistence?.();
216
135
  if (input.persistent === true && (fileInput || commandInput)) {
217
136
  const refused = durableAdmissionError(ctx);
218
137
  if (refused)
@@ -229,7 +148,8 @@ export function createMonitorTool(ctx) {
229
148
  description: input.description,
230
149
  path: input.path,
231
150
  event: input.event ?? "create",
232
- timeoutMs: resolveTimeoutMs(input.timeout_ms),
151
+ // A persistent watch has no deadline (`deadlineMs: null`); its timeout only bounds registration.
152
+ timeoutMs: resolveTimeoutMs(input.persistent === true ? undefined : input.timeout_ms),
233
153
  persistent: input.persistent === true,
234
154
  deadlineMs: input.persistent === true ? null : Date.now() + resolveTimeoutMs(input.timeout_ms),
235
155
  cwd: execCtx?.cwd ?? ctx.cwd,
@@ -239,7 +159,7 @@ export function createMonitorTool(ctx) {
239
159
  ctx.manager.bindMonitorId(monitorId, id);
240
160
  // Same spec capture as the command branch: durability inputs live only here.
241
161
  const sessionKey = manifestSessionKey(ctx);
242
- handMonitorSpec(sessionKey, {
162
+ await handMonitorSpec(sessionKey, {
243
163
  monitorId,
244
164
  spec: {
245
165
  kind: "file",
@@ -1 +1 @@
1
- {"version":3,"file":"monitor.js","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/monitor.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from \"node:path\";\nimport { StringEnum } from \"@earendil-works/pi-ai\";\nimport { type Static, Type } from \"typebox\";\nimport { APPROVED_MONITOR_PARENT } from \"../monitor-permission.ts\";\nimport { MonitorRegistry } from \"../monitor-registry.ts\";\nimport {\n\tDEFAULT_COLS,\n\tDEFAULT_ROWS,\n\tDURABLE_MONITOR_EXPIRY_MS,\n\tMAX_DURABLE_MONITORS,\n\tTERMINAL_MONITOR_TOOL,\n} from \"../shared.ts\";\nimport type { MonitorRegistration, TerminalManifestWriter } from \"../terminal-manifest.ts\";\nimport {\n\terrorResult,\n\tresolveTerminalId,\n\ttype TerminalToolContext,\n\ttype TerminalToolResult,\n\ttextResult,\n} from \"./context.ts\";\nimport { renderMonitorCall } from \"./render.ts\";\nimport { spawnCommandSession } from \"./spawn.ts\";\n\nexport const DEFAULT_MONITOR_TIMEOUT_MS = 300_000;\nexport const MAX_MONITOR_TIMEOUT_MS = 3_600_000;\n\n/**\n * One flat object schema, no top-level union: several provider payload paths\n * (e.g. Anthropic's legacy input_schema conversion) rebuild tool schemas from\n * top-level `properties` only, so a root anyOf would reach the model as an\n * empty schema. Branch requirements are enforced at runtime in `execute`.\n */\nexport const monitorSchema = Type.Object({\n\taction: Type.Optional(\n\t\tStringEnum([\"create\", \"rearm\"] as const, {\n\t\t\tdescription: \"Defaults to create. rearm resumes a monitor paused by the wake budget.\",\n\t\t}),\n\t),\n\tdescription: Type.Optional(\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tmaxLength: 200,\n\t\t\tdescription: \"Create (required): specific label shown with every event, e.g. 'errors in deploy.log'.\",\n\t\t}),\n\t),\n\tcommand: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Create, command branch (XOR path): shell command to run and watch in a PTY-backed monitor session.\",\n\t\t}),\n\t),\n\tpath: Type.Optional(\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tdescription:\n\t\t\t\t\"Create, file branch (XOR command): one regular file to watch natively, whose parent directory must already exist; takes no filter.\",\n\t\t}),\n\t),\n\tevent: Type.Optional(\n\t\tStringEnum([\"create\", \"modify\"] as const, {\n\t\t\tdescription: \"File branch only: which file event fires the watch (defaults to create).\",\n\t\t}),\n\t),\n\tfilter: Type.Optional(\n\t\tType.String({ description: \"Only PTY output lines matching this regex become monitor events.\" }),\n\t),\n\ttimeout_ms: Type.Optional(\n\t\tType.Number({\n\t\t\tminimum: 1,\n\t\t\tmaximum: MAX_MONITOR_TIMEOUT_MS,\n\t\t\tdescription: \"Watcher deadline in milliseconds (default 300000; ignored when persistent).\",\n\t\t}),\n\t),\n\tpersistent: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Standing watch: no deadline, and it survives a session restart (command re-run once, file rescanned and any detached change reported). Expires 7 days after creation; max 5 per session; stop one with kill_bash.\",\n\t\t}),\n\t),\n\tbash_id: Type.Optional(\n\t\tType.String({ description: \"Rearm: paused monitor id (mon_ or bash_id) to resume; omit for all paused.\" }),\n\t),\n});\nexport type MonitorInput = Static<typeof monitorSchema>;\n\ntype MonitorCreateInput = MonitorInput & { description: string; command: string };\ntype FileMonitorCreateInput = MonitorInput & { description: string; path: string };\n\nfunction isFileCreateInput(input: MonitorInput): input is FileMonitorCreateInput {\n\treturn (\n\t\ttypeof input.description === \"string\" &&\n\t\tinput.description.length > 0 &&\n\t\ttypeof input.path === \"string\" &&\n\t\tinput.path.length > 0\n\t);\n}\n\nfunction isCreateInput(input: MonitorInput): input is MonitorCreateInput {\n\treturn (\n\t\ttypeof input.description === \"string\" &&\n\t\tinput.description.length > 0 &&\n\t\ttypeof input.command === \"string\" &&\n\t\tinput.command.length > 0\n\t);\n}\n\nfunction resolveDimension(value: number | undefined, fallback: number): number {\n\tif (value === undefined || !Number.isFinite(value) || value < 1) return fallback;\n\treturn Math.trunc(value);\n}\n\nfunction resolveTimeoutMs(value: number | undefined): number {\n\tconst timeout = value ?? DEFAULT_MONITOR_TIMEOUT_MS;\n\treturn Math.min(Math.max(Math.trunc(timeout), 1), MAX_MONITOR_TIMEOUT_MS);\n}\n\nfunction compileFilter(filter: string | undefined): RegExp | undefined {\n\tif (filter === undefined) return undefined;\n\treturn new RegExp(filter);\n}\n\nasync function createMonitor(\n\tctx: TerminalToolContext,\n\tregistry: MonitorRegistry,\n\tinput: MonitorCreateInput,\n\texecCtx: { cwd?: string } | undefined,\n): Promise<TerminalToolResult> {\n\tlet filter: RegExp | undefined;\n\ttry {\n\t\tfilter = compileFilter(input.filter);\n\t} catch {\n\t\treturn errorResult(`Invalid monitor filter regex: ${input.filter}`);\n\t}\n\n\t// Durability needs an absolute directory: a restore runs in a different process whose\n\t// process cwd is unrelated, so the spec must carry the resolved path the spawn used.\n\tconst cwd = resolve(execCtx?.cwd ?? ctx.cwd);\n\tconst timeoutMs = input.persistent ? undefined : resolveTimeoutMs(input.timeout_ms);\n\tconst deadlineMs = timeoutMs === undefined ? null : Date.now() + timeoutMs;\n\tconst { id, runtime } = await spawnCommandSession(ctx, {\n\t\tcommand: input.command,\n\t\tcols: resolveDimension(undefined, ctx.defaultCols || DEFAULT_COLS),\n\t\trows: resolveDimension(undefined, ctx.defaultRows || DEFAULT_ROWS),\n\t\tcwd,\n\t\t...(timeoutMs === undefined ? {} : { timeoutMs }),\n\t});\n\tctx.onMonitorRearmed?.(id);\n\tconst monitorId = registry.register({\n\t\tid,\n\t\tdescription: input.description,\n\t\truntime,\n\t\tfilter,\n\t\tcommand: input.command,\n\t\tpersistent: input.persistent === true,\n\t\tdeadlineMs,\n\t\t// Only persistent command watches are restartable-command durable: those carry the fire budget.\n\t\tdurabilityClass: input.persistent === true ? \"restartable-command\" : \"ephemeral\",\n\t\t// Same deadline the manifest writer persists, so the footer warns off the live record too.\n\t\t...(input.persistent === true ? { expiresAt: Date.now() + DURABLE_MONITOR_EXPIRY_MS } : {}),\n\t});\n\tctx.manager.bindMonitorId(monitorId, id);\n\t// The tool call site is the only place the branch inputs (command, persistent, filter)\n\t// live; hand the captured spec to the session's manifest writer for durable recording.\n\thandMonitorSpec(manifestSessionKey(ctx), {\n\t\tmonitorId,\n\t\tspec: {\n\t\t\tkind: \"command\",\n\t\t\tdescription: input.description,\n\t\t\tcommand: input.command,\n\t\t\tfilter: input.filter,\n\t\t\tcwd,\n\t\t\tpersistent: input.persistent === true,\n\t\t},\n\t});\n\treturn textResult(`Monitor started with ID: ${monitorId}`, {\n\t\tdetails: { monitor_id: monitorId, bash_id: id, monitor: true },\n\t});\n}\n\n/** Build the PTY-backed monitor tool. Monitor handles share TerminalManager's bash_N namespace. */\nconst manifestWriters = new Map<string, TerminalManifestWriter>();\n\n/** Bind the session's manifest writer so monitor tool calls can hand it specs captured at the call site. */\nexport function bindTerminalManifestWriter(sessionId: string, writer: TerminalManifestWriter): void {\n\tmanifestWriters.set(sessionId, writer);\n}\n\nexport function unbindTerminalManifestWriter(sessionId: string): void {\n\tmanifestWriters.delete(sessionId);\n}\n\n/** The durability session key for a tool context: the agent session id, when the context carries one. */\nfunction manifestSessionKey(ctx: TerminalToolContext): string | undefined {\n\treturn ctx.getSessionContext?.()?.sessionManager?.getSessionId?.();\n}\n\n/** Hand a spec captured at the monitor tool call site to the session's bound writer, if any. */\nfunction handMonitorSpec(sessionKey: string | undefined, registration: MonitorRegistration): void {\n\tconst writer = sessionKey === undefined ? undefined : manifestWriters.get(sessionKey);\n\tvoid writer?.recordRegister(registration);\n}\n\n/** Persist a durable file watch's baseline checkpoint through the writer's debounced path. */\nfunction handFileCheckpoint(\n\tsessionKey: string | undefined,\n\tmonitorId: string,\n\tregistry: MonitorRegistry,\n\truntimeId: string,\n): void {\n\tconst writer = sessionKey === undefined ? undefined : manifestWriters.get(sessionKey);\n\tconst checkpoint = registry.fileCheckpoint(runtimeId);\n\tif (writer && checkpoint) writer.scheduleCheckpoint(monitorId, checkpoint);\n}\n\n/**\n * Admission control for a durable create: refuse once the session already holds\n * MAX_DURABLE_MONITORS restart-surviving monitors. Checked BEFORE any spawn or registry\n * registration so a refused call leaves no PTY and no manifest entry behind. A context\n * with no bound writer persists nothing, so it has no durable population to cap.\n */\nfunction durableAdmissionError(ctx: TerminalToolContext): TerminalToolResult | undefined {\n\tconst sessionKey = manifestSessionKey(ctx);\n\tconst writer = sessionKey === undefined ? undefined : manifestWriters.get(sessionKey);\n\tif (writer === undefined || writer.durableCount() < MAX_DURABLE_MONITORS) return undefined;\n\treturn errorResult(\n\t\t`Cannot start another persistent monitor: this session already holds ${MAX_DURABLE_MONITORS} durable monitors (the maximum). Stop one with kill_bash first.`,\n\t);\n}\n\nexport function createMonitorTool(ctx: TerminalToolContext) {\n\tlet fallbackRegistry: MonitorRegistry | undefined;\n\tconst getRegistry = (): MonitorRegistry => {\n\t\tconst sessionRegistry = ctx.monitorRegistry;\n\t\tif (sessionRegistry) return sessionRegistry;\n\t\tfallbackRegistry ??= new MonitorRegistry((event) => ctx.onMonitorEvent?.(event));\n\t\treturn fallbackRegistry;\n\t};\n\treturn {\n\t\tname: TERMINAL_MONITOR_TOOL,\n\t\tlabel: \"monitor\",\n\t\tdescription:\n\t\t\t\"Subscribe to a change instead of polling. Pass command XOR path, never both: command watches a PTY session, injecting matching newline-terminated output lines (stderr merged) plus an exit summary; path natively watches one file and fires once, where create (the default) fires only when the file appears after registration, so use event modify for an existing file, and filter is rejected. Returns a bash_id immediately; peek with bash_output, stop with kill_bash.\",\n\t\tpromptSnippet:\n\t\t\t\"Subscribe to a command's output or a file's create/modify event as injected events instead of polling\",\n\t\tpromptGuidelines: [\n\t\t\t\"Waiting on observable state (CI checks, builds, log patterns, deploys, a file landing) means a monitor, never a foreground sleep/poll loop.\",\n\t\t\t'Waiting for one file to appear or change is the path branch: `monitor({ description, path, event? })` beats wrapping `test -f` in a shell poll loop; a file that already exists needs `event: \"modify\"`, since `create` only fires on appearance, and registration needs the parent directory to exist already — when the run creates that directory too, use the `command` branch instead.',\n\t\t\t\"Shape the command for the events you need: one-shot gate = `until <cond>; do sleep 1; done; printf 'READY\\\\n'` with filter ^READY$; stream = `tail -n 0 -F <log> | grep --line-buffered <pat>` with persistent: true, then kill_bash.\",\n\t\t\t\"Sleep loops belong INSIDE the monitor command, never in your turn: about to sleep, re-poll bash_output, or foreground-block on a long command means register a monitor and keep working.\",\n\t\t],\n\t\tparameters: monitorSchema,\n\t\trenderCall: renderMonitorCall,\n\t\tasync execute(\n\t\t\t_toolCallId: string,\n\t\t\tinput: MonitorInput,\n\t\t\t_signal?: AbortSignal,\n\t\t\t_onUpdate?: undefined,\n\t\t\texecCtx?: { cwd?: string },\n\t\t): Promise<TerminalToolResult> {\n\t\t\tconst registry = getRegistry();\n\t\t\tif (input.action === \"rearm\") {\n\t\t\t\tif (input.bash_id === undefined || input.bash_id.length === 0) {\n\t\t\t\t\tconst resumed = registry.resume();\n\t\t\t\t\tif (resumed.length === 0) return textResult(\"No paused monitors to re-arm.\");\n\t\t\t\t\tctx.onMonitorsResumed?.(resumed.map((monitor) => monitor.id));\n\t\t\t\t\tconst total = resumed.reduce((sum, monitor) => sum + monitor.mutedDropped, 0);\n\t\t\t\t\treturn textResult(\n\t\t\t\t\t\ttotal > 0\n\t\t\t\t\t\t\t? `Re-armed ${resumed.length} paused monitor(s) (${total} line(s) dropped while muted).`\n\t\t\t\t\t\t\t: `Re-armed ${resumed.length} paused monitor(s).`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst bashId = resolveTerminalId(ctx.manager, input.bash_id);\n\t\t\t\tconst dropped = registry.mutedDropped(bashId);\n\t\t\t\tconst outcome = registry.rearm(bashId);\n\t\t\t\tif (outcome === \"not_found\") return errorResult(`No active monitor found with id: ${bashId}`);\n\t\t\t\tif (outcome === \"not_paused\") return textResult(`Monitor ${bashId} is not paused; no action taken.`);\n\t\t\t\tctx.onMonitorRearmed?.(bashId);\n\t\t\t\treturn textResult(\n\t\t\t\t\tdropped > 0\n\t\t\t\t\t\t? `Monitor ${bashId} re-armed (${dropped} line(s) dropped while muted).`\n\t\t\t\t\t\t: `Monitor ${bashId} re-armed.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst fileInput = isFileCreateInput(input);\n\t\t\tconst commandInput = isCreateInput(input);\n\t\t\tif (fileInput && commandInput) return errorResult(\"monitor accepts either command or path, not both.\");\n\t\t\t// Admission runs before either create branch touches a PTY or the registry.\n\t\t\tif (input.persistent === true && (fileInput || commandInput)) {\n\t\t\t\tconst refused = durableAdmissionError(ctx);\n\t\t\t\tif (refused) return refused;\n\t\t\t}\n\t\t\tif (fileInput) {\n\t\t\t\tif (input.filter !== undefined) return errorResult(\"Native file monitors do not support filter.\");\n\t\t\t\tif (!ctx.monitorRegistry)\n\t\t\t\t\treturn errorResult(\"Native file monitors require a lifecycle-owned monitor registry.\");\n\t\t\t\ttry {\n\t\t\t\t\tconst approvedParent = (input as Record<string | symbol, unknown>)[APPROVED_MONITOR_PARENT] as\n\t\t\t\t\t\t| string\n\t\t\t\t\t\t| undefined;\n\t\t\t\t\tconst { id, monitorId } = await ctx.monitorRegistry.registerFile({\n\t\t\t\t\t\tdescription: input.description,\n\t\t\t\t\t\tpath: input.path,\n\t\t\t\t\t\tevent: input.event ?? \"create\",\n\t\t\t\t\t\ttimeoutMs: resolveTimeoutMs(input.timeout_ms),\n\t\t\t\t\t\tpersistent: input.persistent === true,\n\t\t\t\t\t\tdeadlineMs: input.persistent === true ? null : Date.now() + resolveTimeoutMs(input.timeout_ms),\n\t\t\t\t\t\tcwd: execCtx?.cwd ?? ctx.cwd,\n\t\t\t\t\t\t...(approvedParent !== undefined ? { approvedParent } : {}),\n\t\t\t\t\t\t...(input.persistent === true ? { expiresAt: Date.now() + DURABLE_MONITOR_EXPIRY_MS } : {}),\n\t\t\t\t\t});\n\t\t\t\t\tctx.manager.bindMonitorId(monitorId, id);\n\t\t\t\t\t// Same spec capture as the command branch: durability inputs live only here.\n\t\t\t\t\tconst sessionKey = manifestSessionKey(ctx);\n\t\t\t\t\thandMonitorSpec(sessionKey, {\n\t\t\t\t\t\tmonitorId,\n\t\t\t\t\t\tspec: {\n\t\t\t\t\t\t\tkind: \"file\",\n\t\t\t\t\t\t\tdescription: input.description,\n\t\t\t\t\t\t\tpath: input.path,\n\t\t\t\t\t\t\tevent: input.event ?? \"create\",\n\t\t\t\t\t\t\ttimeoutMs: resolveTimeoutMs(input.timeout_ms),\n\t\t\t\t\t\t\tcwd: execCtx?.cwd ?? ctx.cwd,\n\t\t\t\t\t\t\tpersistent: input.persistent === true,\n\t\t\t\t\t\t\t...(approvedParent !== undefined ? { approvedParent } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\t// A durable watch checkpoints the registry's own identity tuple straight away, so a\n\t\t\t\t\t// restart before the first change still has a baseline (digest included) to compare to.\n\t\t\t\t\tif (input.persistent === true) handFileCheckpoint(sessionKey, monitorId, ctx.monitorRegistry, id);\n\t\t\t\t\treturn textResult(`Monitor started with ID: ${monitorId}`, {\n\t\t\t\t\t\tdetails: { monitor_id: monitorId, bash_id: id, monitor: true },\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn errorResult(error instanceof Error ? error.message : String(error));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!commandInput) return errorResult(\"monitor requires description and command or path to start a watcher.\");\n\t\t\treturn createMonitor(ctx, registry, input, execCtx);\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"monitor.js","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/monitor.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from \"node:path\";\nimport { APPROVED_MONITOR_PARENT } from \"../monitor-permission.ts\";\nimport { allocateMonitorId, MonitorRegistry } from \"../monitor-registry.ts\";\nimport { ensureMonitorStateDir, terminalStateDir } from \"../monitor-state-dir.ts\";\nimport {\n\tDEFAULT_COLS,\n\tDEFAULT_ROWS,\n\tDURABLE_MONITOR_EXPIRY_MS,\n\tMONITOR_ENV_ID,\n\tMONITOR_ENV_STATE_DIR,\n\tTERMINAL_MONITOR_TOOL,\n} from \"../shared.ts\";\nimport {\n\terrorResult,\n\tresolveTerminalId,\n\ttype TerminalToolContext,\n\ttype TerminalToolResult,\n\ttextResult,\n} from \"./context.ts\";\nimport {\n\tdurableAdmissionError,\n\thandFileCheckpoint,\n\thandMonitorSpec,\n\tmanifestSessionKey,\n} from \"./monitor-manifest-binding.ts\";\nimport {\n\tcompileFilter,\n\tisCreateInput,\n\tisFileCreateInput,\n\ttype MonitorCreateInput,\n\ttype MonitorInput,\n\tmonitorSchema,\n\tresolveDimension,\n\tresolveTimeoutMs,\n} from \"./monitor-schema.ts\";\nimport { renderMonitorCall } from \"./render.ts\";\nimport { spawnCommandSession } from \"./spawn.ts\";\n\nexport {\n\tbindTerminalManifestWriter,\n\tMAX_PENDING_MONITOR_SPECS,\n\tpendingDurableSpecCount,\n\tunbindTerminalManifestWriter,\n} from \"./monitor-manifest-binding.ts\";\nexport {\n\tDEFAULT_MONITOR_TIMEOUT_MS,\n\tMAX_MONITOR_TIMEOUT_MS,\n\ttype MonitorInput,\n\tmonitorSchema,\n} from \"./monitor-schema.ts\";\n\nasync function createMonitor(\n\tctx: TerminalToolContext,\n\tregistry: MonitorRegistry,\n\tinput: MonitorCreateInput,\n\texecCtx: { cwd?: string } | undefined,\n): Promise<TerminalToolResult> {\n\tlet filter: RegExp | undefined;\n\ttry {\n\t\tfilter = compileFilter(input.filter);\n\t} catch {\n\t\treturn errorResult(`Invalid monitor filter regex: ${input.filter}`);\n\t}\n\n\t// Durability needs an absolute directory: a restore runs in a different process whose\n\t// process cwd is unrelated, so the spec must carry the resolved path the spawn used.\n\tconst cwd = resolve(execCtx?.cwd ?? ctx.cwd);\n\tconst timeoutMs = input.persistent ? undefined : resolveTimeoutMs(input.timeout_ms);\n\tconst deadlineMs = timeoutMs === undefined ? null : Date.now() + timeoutMs;\n\t// The stable id is allocated BEFORE the spawn so the command can see it in its environment.\n\tconst monitorId = allocateMonitorId();\n\tconst terminalDir = terminalStateDir(ctx.getSessionContext?.());\n\tconst envOverrides: Record<string, string> = { [MONITOR_ENV_ID]: monitorId };\n\tif (input.persistent === true && terminalDir !== undefined) {\n\t\tenvOverrides[MONITOR_ENV_STATE_DIR] = await ensureMonitorStateDir(terminalDir, monitorId);\n\t}\n\tconst { id, runtime } = await spawnCommandSession(ctx, {\n\t\tcommand: input.command,\n\t\tcols: resolveDimension(undefined, ctx.defaultCols || DEFAULT_COLS),\n\t\trows: resolveDimension(undefined, ctx.defaultRows || DEFAULT_ROWS),\n\t\tcwd,\n\t\tenvOverrides,\n\t\t...(timeoutMs === undefined ? {} : { timeoutMs }),\n\t});\n\tctx.onMonitorRearmed?.(id);\n\tregistry.register({\n\t\tmonitorId,\n\t\tid,\n\t\tdescription: input.description,\n\t\truntime,\n\t\tfilter,\n\t\tcommand: input.command,\n\t\tpersistent: input.persistent === true,\n\t\tdeadlineMs,\n\t\t// Only persistent command watches are restartable-command durable: those carry the fire budget.\n\t\tdurabilityClass: input.persistent === true ? \"restartable-command\" : \"ephemeral\",\n\t\t// Same deadline the manifest writer persists, so the footer warns off the live record too.\n\t\t...(input.persistent === true ? { expiresAt: Date.now() + DURABLE_MONITOR_EXPIRY_MS } : {}),\n\t});\n\tctx.manager.bindMonitorId(monitorId, id);\n\t// The tool call site is the only place the branch inputs (command, persistent, filter)\n\t// live; hand the captured spec to the session's manifest writer for durable recording.\n\tconst identity = runtime.identity();\n\tawait handMonitorSpec(manifestSessionKey(ctx), {\n\t\tmonitorId,\n\t\tspec: {\n\t\t\tkind: \"command\",\n\t\t\tdescription: input.description,\n\t\t\tcommand: input.command,\n\t\t\tfilter: input.filter,\n\t\t\tcwd,\n\t\t\tpersistent: input.persistent === true,\n\t\t},\n\t\t...(identity === undefined ? {} : { runtime: identity }),\n\t\t...(deadlineMs === null ? {} : { deadlineMs }),\n\t});\n\treturn textResult(`Monitor started with ID: ${monitorId}`, {\n\t\tdetails: { monitor_id: monitorId, bash_id: id, monitor: true },\n\t});\n}\n\n/** Build the PTY-backed monitor tool. Monitor handles share TerminalManager's bash_N namespace. */\nexport function createMonitorTool(ctx: TerminalToolContext) {\n\tlet fallbackRegistry: MonitorRegistry | undefined;\n\tconst getRegistry = (): MonitorRegistry => {\n\t\tconst sessionRegistry = ctx.monitorRegistry;\n\t\tif (sessionRegistry) return sessionRegistry;\n\t\tfallbackRegistry ??= new MonitorRegistry((event) => ctx.onMonitorEvent?.(event));\n\t\treturn fallbackRegistry;\n\t};\n\treturn {\n\t\tname: TERMINAL_MONITOR_TOOL,\n\t\tlabel: \"monitor\",\n\t\tdescription:\n\t\t\t\"Subscribe to a change instead of polling. Pass command XOR path, never both: command watches a PTY session, injecting matching newline-terminated output lines (stderr merged) plus an exit summary; path natively watches one file and fires once, where create (the default) fires only when the file appears after registration, so use event modify for an existing file, and filter is rejected. Returns a bash_id immediately; peek with bash_output, stop with kill_bash.\",\n\t\tpromptSnippet:\n\t\t\t\"Subscribe to a command's output or a file's create/modify event as injected events instead of polling\",\n\t\tpromptGuidelines: [\n\t\t\t\"Waiting on observable state (CI checks, builds, log patterns, deploys, a file landing) means a monitor, never a foreground sleep/poll loop.\",\n\t\t\t'Waiting for one file to appear or change is the path branch: `monitor({ description, path, event? })` beats wrapping `test -f` in a shell poll loop; a file that already exists needs `event: \"modify\"`, since `create` only fires on appearance, and registration needs the parent directory to exist already — when the run creates that directory too, use the `command` branch instead.',\n\t\t\t\"Shape the command for the events you need: one-shot gate = `until <cond>; do sleep 1; done; printf 'READY\\\\n'` with filter ^READY$; stream = `tail -n 0 -F <log> | grep --line-buffered <pat>` with persistent: true, then kill_bash.\",\n\t\t\t'Persistent command watches are re-run after a restart with `SENPI_MONITOR_RESTORED=1`; keep any baseline in `$SENPI_MONITOR_STATE_DIR` so a move during downtime (at most `$SENPI_MONITOR_DOWNTIME_MS` ms) is still reported: `b=\"$SENPI_MONITOR_STATE_DIR/base\"; p=$(cat \"$b\" 2>/dev/null || git rev-parse origin/main | tee \"$b\"); while sleep 30; do git fetch -q || true; n=$(git rev-parse origin/main); [ \"$n\" != \"$p\" ] && { echo \"MOVED $p..$n\"; p=$n; echo \"$p\" > \"$b\"; }; done`. A watch whose command exits is not restarted, so keep failures inside the loop non-fatal.',\n\t\t\t\"Sleep loops belong INSIDE the monitor command, never in your turn: about to sleep, re-poll bash_output, or foreground-block on a long command means register a monitor and keep working.\",\n\t\t],\n\t\tparameters: monitorSchema,\n\t\trenderCall: renderMonitorCall,\n\t\tasync execute(\n\t\t\t_toolCallId: string,\n\t\t\tinput: MonitorInput,\n\t\t\t_signal?: AbortSignal,\n\t\t\t_onUpdate?: undefined,\n\t\t\texecCtx?: { cwd?: string },\n\t\t): Promise<TerminalToolResult> {\n\t\t\tconst registry = getRegistry();\n\t\t\tif (input.action === \"rearm\") {\n\t\t\t\tif (input.bash_id === undefined || input.bash_id.length === 0) {\n\t\t\t\t\tconst resumed = registry.resume();\n\t\t\t\t\tif (resumed.length === 0) return textResult(\"No paused monitors to re-arm.\");\n\t\t\t\t\tctx.onMonitorsResumed?.(resumed.map((monitor) => monitor.id));\n\t\t\t\t\tconst total = resumed.reduce((sum, monitor) => sum + monitor.mutedDropped, 0);\n\t\t\t\t\treturn textResult(\n\t\t\t\t\t\ttotal > 0\n\t\t\t\t\t\t\t? `Re-armed ${resumed.length} paused monitor(s) (${total} line(s) dropped while muted).`\n\t\t\t\t\t\t\t: `Re-armed ${resumed.length} paused monitor(s).`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst bashId = resolveTerminalId(ctx.manager, input.bash_id);\n\t\t\t\tconst dropped = registry.mutedDropped(bashId);\n\t\t\t\tconst outcome = registry.rearm(bashId);\n\t\t\t\tif (outcome === \"not_found\") return errorResult(`No active monitor found with id: ${bashId}`);\n\t\t\t\tif (outcome === \"not_paused\") return textResult(`Monitor ${bashId} is not paused; no action taken.`);\n\t\t\t\tctx.onMonitorRearmed?.(bashId);\n\t\t\t\treturn textResult(\n\t\t\t\t\tdropped > 0\n\t\t\t\t\t\t? `Monitor ${bashId} re-armed (${dropped} line(s) dropped while muted).`\n\t\t\t\t\t\t: `Monitor ${bashId} re-armed.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst fileInput = isFileCreateInput(input);\n\t\t\tconst commandInput = isCreateInput(input);\n\t\t\tif (fileInput && commandInput) return errorResult(\"monitor accepts either command or path, not both.\");\n\t\t\t// Every create binds persistence first (lazy lease + recorder: an ephemeral watch with time\n\t\t\t// left is restorable too), then durable admission runs before either create branch touches\n\t\t\t// a PTY or the registry.\n\t\t\tif (fileInput || commandInput) await ctx.ensurePersistence?.();\n\t\t\tif (input.persistent === true && (fileInput || commandInput)) {\n\t\t\t\tconst refused = durableAdmissionError(ctx);\n\t\t\t\tif (refused) return refused;\n\t\t\t}\n\t\t\tif (fileInput) {\n\t\t\t\tif (input.filter !== undefined) return errorResult(\"Native file monitors do not support filter.\");\n\t\t\t\tif (!ctx.monitorRegistry)\n\t\t\t\t\treturn errorResult(\"Native file monitors require a lifecycle-owned monitor registry.\");\n\t\t\t\ttry {\n\t\t\t\t\tconst approvedParent = (input as Record<string | symbol, unknown>)[APPROVED_MONITOR_PARENT] as\n\t\t\t\t\t\t| string\n\t\t\t\t\t\t| undefined;\n\t\t\t\t\tconst { id, monitorId } = await ctx.monitorRegistry.registerFile({\n\t\t\t\t\t\tdescription: input.description,\n\t\t\t\t\t\tpath: input.path,\n\t\t\t\t\t\tevent: input.event ?? \"create\",\n\t\t\t\t\t\t// A persistent watch has no deadline (`deadlineMs: null`); its timeout only bounds registration.\n\t\t\t\t\t\ttimeoutMs: resolveTimeoutMs(input.persistent === true ? undefined : input.timeout_ms),\n\t\t\t\t\t\tpersistent: input.persistent === true,\n\t\t\t\t\t\tdeadlineMs: input.persistent === true ? null : Date.now() + resolveTimeoutMs(input.timeout_ms),\n\t\t\t\t\t\tcwd: execCtx?.cwd ?? ctx.cwd,\n\t\t\t\t\t\t...(approvedParent !== undefined ? { approvedParent } : {}),\n\t\t\t\t\t\t...(input.persistent === true ? { expiresAt: Date.now() + DURABLE_MONITOR_EXPIRY_MS } : {}),\n\t\t\t\t\t});\n\t\t\t\t\tctx.manager.bindMonitorId(monitorId, id);\n\t\t\t\t\t// Same spec capture as the command branch: durability inputs live only here.\n\t\t\t\t\tconst sessionKey = manifestSessionKey(ctx);\n\t\t\t\t\tawait handMonitorSpec(sessionKey, {\n\t\t\t\t\t\tmonitorId,\n\t\t\t\t\t\tspec: {\n\t\t\t\t\t\t\tkind: \"file\",\n\t\t\t\t\t\t\tdescription: input.description,\n\t\t\t\t\t\t\tpath: input.path,\n\t\t\t\t\t\t\tevent: input.event ?? \"create\",\n\t\t\t\t\t\t\ttimeoutMs: resolveTimeoutMs(input.timeout_ms),\n\t\t\t\t\t\t\tcwd: execCtx?.cwd ?? ctx.cwd,\n\t\t\t\t\t\t\tpersistent: input.persistent === true,\n\t\t\t\t\t\t\t...(approvedParent !== undefined ? { approvedParent } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\t// A durable watch checkpoints the registry's own identity tuple straight away, so a\n\t\t\t\t\t// restart before the first change still has a baseline (digest included) to compare to.\n\t\t\t\t\tif (input.persistent === true) handFileCheckpoint(sessionKey, monitorId, ctx.monitorRegistry, id);\n\t\t\t\t\treturn textResult(`Monitor started with ID: ${monitorId}`, {\n\t\t\t\t\t\tdetails: { monitor_id: monitorId, bash_id: id, monitor: true },\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn errorResult(error instanceof Error ? error.message : String(error));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!commandInput) return errorResult(\"monitor requires description and command or path to start a watcher.\");\n\t\t\treturn createMonitor(ctx, registry, input, execCtx);\n\t\t},\n\t};\n}\n"]}